首先需要下载pillow
进入prompt ,activate tensorflow-gpu,pip install Pillow即可
注意 要是有h5py包 那么一定要卸载 否则会报错
附上完整程序
#coding=utf-8
import os
#图像读取库
from PIL import Image
#矩阵运算库
import numpy as np
import tensorflow as tf
# 数据文件夹
data_dir = "data"
# 训练还是测试
train = True
# 模型文件路径
model_path = "model/image_model"
# 从文件夹读取图片和标签到numpy数组中
# 标签信息在文件名中,例如1_40.jpg表示该图片的标签为1
def read_data(data_dir):
datas = []
labels = []
fpaths = []
for fname in os.listdir(data_dir):
fpath = os.path.join(data_dir, fname)
fpaths.append(fpath)
image = Image.open(fpath)
data = np.array(image) / 255.0
label = int(fname.split("_")[0])
datas.append(data)
labels.append(label)
datas = np.array(datas)
labels = np.array(labels)
print("shape of datas: {}\tshape of labels: {}".format(datas.shape, labels.shape))
return fpaths, datas, labels
fpaths, datas, labels = read_data(data_dir)
# 计算有多少类图片
num_classes = len(set(labels))
# 定义Placeholder,存放输入和标签
datas_placeholder = tf.placeholder(tf.float32, [None, 32, 32, 3])
labels_placeholder = tf.placeholder(tf.int32, [None])
# 存放DropOut参数的容器,训练时为0.25,测试时为0
dropout_placeholdr = tf.placeholder(tf.float32)
# 定义卷积层, 20个卷积核, 卷积核大小为5,用Relu激活
conv0 = tf.layers.conv2d(datas_placeholder, 20, 5, activation=tf.nn.relu)
# 定义max-pooling层,pooling窗口为2x2,步长为2x2
pool0 = tf.layers.max_pooling2d(conv0, [2, 2], [2, 2])
# 定义卷积层, 40个卷积核, 卷积核大小为4,用Relu激活
conv1 = tf.layers.conv2d(pool0, 40, 4, activation=tf.nn.relu)
# 定义max-pooling层,pooling窗口为2x2,步长为2x2
pool1 = tf.layers.max_pooling2d(conv1, [2, 2], [2, 2])
# 将3维特征转换为1维向量
flatten = tf.layers.flatten(pool1)
# 全连接层,转换为长度为100的特征向量
fc = tf.layers.dense(flatten, 400, activation=tf.nn.relu)
# 加上DropOut,防止过拟合
dropout_fc = tf.layers.dropout(fc, dropout_placeholdr)
# 未激活的输出层
logits = tf.layers.dense(dropout_fc, num_classes)
predicted_labels = tf.arg_max(logits, 1)
# 利用交叉熵定义损失
losses = tf.nn.softmax_cross_entropy_with_logits(
labels=tf.one_hot(labels_placeholder, num_classes),
logits=logits
)
# 平均损失
mean_loss = tf.reduce_mean(losses)
# 定义优化器,指定要优化的损失函数
optimizer = tf.train.AdamOptimizer(learning_rate=1e-2).minimize(losses)
# 用于保存和载入模型
saver = tf.train.Saver()
with tf.Session() as sess:
if train:
print("训练模式")
# 如果是训练,初始化参数
sess.run(tf.global_variables_initializer())
# 定义输入和Label以填充容器,训练时dropout为0.25
train_feed_dict = {
datas_placeholder: datas,
labels_placeholder: labels,
dropout_placeholdr: 0.25
}
for step in range(150):
_, mean_loss_val = sess.run([optimizer, mean_loss], feed_dict=train_feed_dict)
if step % 10 == 0:
print("step = {}\tmean loss = {}".format(step, mean_loss_val))
saver.save(sess, model_path)
print("训练结束,保存模型到{}".format(model_path))
else:
print("测试模式")
# 如果是测试,载入参数
saver.restore(sess, model_path)
print("从{}载入模型".format(model_path))
# label和名称的对照关系
label_name_dict = {
0: "飞机",
1: "汽车",
2: "鸟"
}
# 定义输入和Label以填充容器,测试时dropout为0
test_feed_dict = {
datas_placeholder: datas,
labels_placeholder: labels,
dropout_placeholdr: 0
}
predicted_labels_val = sess.run(predicted_labels, feed_dict=test_feed_dict)
# 真实label与模型预测label
for fpath, real_label, predicted_label in zip(fpaths, labels, predicted_labels_val):
# 将label id转换为label名
real_label_name = label_name_dict[real_label]
predicted_label_name = label_name_dict[predicted_label]
print("{}\t{} => {}".format(fpath, real_label_name, predicted_label_name))
开始详细分析
除了Tensorflow,本教程还需要使用pillow(PIL),在Windows下PIL可能需要使用conda安装。
如果使用download_cifar.py自己构建数据集,还需要安装keras。
import os
#图像读取库
from PIL import Image
#矩阵运算库
import numpy as np
import tensorflow as tf
设置了一些变量增加程序的灵活性。图片文件存放在data_dir文件夹中,train表示当前执行是训练还是测试,model-path约定了模型存放的路径。
data是数据文件夹的名字
# 数据文件夹
data_dir = "data"
# 训练还是测试
train = True
# 模型文件路径
model_path = "model/image_model"
从图片文件夹中将图片读入numpy的array中。这里有几个细节:
pillow读取的图像像素值在0-255之间,需要归一化。
在读取图像数据、Label信息的同时,记录图像的路径,方便后期调试。
os.path.join 将路径接起来
2.
os.listdir() 方法用于返回指定的文件夹包含的文件或文件夹的名字的列表。这个列表以字母顺序。 它不包括 ‘.’ 和’…’ 即使它在文件夹中。
3.
fname看来是默认表示的文件名
data_dir是文件夹名称 fname是文件名(图片) 因此连接起来才是图片最终路径
4.
.append用法https://blog.csdn.net/m0_37870649/article/details/80552102
列表名称.append()就是在列表里面的最后面加上这个元素
5.
# 从文件夹读取图片和标签到numpy数组中
# 标签信息在文件名中,例如1_40.jpg表示该图片的标签为1
def read_data(data_dir):
datas = []
labels = []
fpaths = []
for fname in os.listdir(data_dir):
fpath = os.path.join(data_dir, fname)
fpaths.append(fpath)
image = Image.open(fpath)
data = np.array(image) / 255.0
label = int(fname.split("_")[0])
datas.append(data)
labels.append(label)
datas = np.array(datas)
labels = np.array(labels)
print("shape of datas: {}\tshape of labels: {}".format(datas.shape, labels.shape))
return fpaths, datas, labels
fpaths, datas, labels = read_data(data_dir)
# 计算有多少类图片
num_classes = len(set(labels))
datas = []
labels = []
fpaths = []
这种操作是定义数组吧?
for fname in os.listdir(data_dir):可以看出来os库是用来进行图像读取的
fpaths.append作用:append单词是添加的意思,这里的意思是吧fpath = os.path.join(data_dir, fname)
获取的一系列fpath添加到fpaths这个定义的数组里面
image = Image.open(fpath):把获取的路径open开就是图片
data = np.array(image) / 255.0:
结果= np.array(图像)= = >结果的形状=(宽度、高度、通道)
但是这个/255还没有搞清楚目的
经过验证 datas.append(data) 是把一次次循环得到的新的data加入datas这个列表里面
labels也是这样
num_classes = len(set(labels)) set()的功能是计算类别数
可用# print(num_classes)验证
np.array是生成数组
注意 图片的三类分别为0.1.2
除了图像数据和Label,Dropout率也要放在placeholder中,因为在训练阶段和测试阶段需要设置不同的Dropout率
# 定义Placeholder,存放输入和标签
datas_placeholder = tf.placeholder(tf.float32, [None, 32, 32, 3])
labels_placeholder = tf.placeholder(tf.int32, [None])
# 存放DropOut参数的容器,训练时为0.25,测试时为0
dropout_placeholdr = tf.placeholder(tf.float32)
存放DropOut参数的容器,这种方式值得学习
开始定义网络
定义卷积网络
# 定义卷积层, 20个卷积核, 卷积核大小为5,用Relu激活
conv0 = tf.layers.conv2d(datas_placeholder, 20, 5, activation=tf.nn.relu)
# 定义max-pooling层,pooling窗口为2x2,步长为2x2
pool0 = tf.layers.max_pooling2d(conv0, [2, 2], [2, 2])
# 定义卷积层, 40个卷积核, 卷积核大小为4,用Relu激活
conv1 = tf.layers.conv2d(pool0, 40, 4, activation=tf.nn.relu)
# 定义max-pooling层,pooling窗口为2x2,步长为2x2
pool1 = tf.layers.max_pooling2d(conv1, [2, 2], [2, 2])
tf.layers.conv2d第一个部分是输入
定义全连接部分
# 将3维特征转换为1维向量
flatten = tf.layers.flatten(pool1)
# 全连接层,转换为长度为100的特征向量
fc = tf.layers.dense(flatten, 400, activation=tf.nn.relu)
# 加上DropOut,防止过拟合
dropout_fc = tf.layers.dropout(fc, dropout_placeholdr)
# 未激活的输出层
logits = tf.layers.dense(dropout_fc, num_classes)
predicted_labels = tf.arg_max(logits, 1)
flatten = tf.layers.flatten(pool1)命令:
https://blog.csdn.net/qq_28224015/article/details/80875665
即过他 输出为[batch_size,height * width * channel]
fc = tf.layers.dense(flatten, 400, activation=tf.nn.relu)即是定义全连接层
常用的几个参数:
inputs: 该层的输入
units: 输出的大小(维数),整数或long
activation: 使用什么激活函数(神经网络的非线性层),默认为None,不使用激活函数
use_bias: 使用bias为True(默认使用),不用bias改成False即可
tf.layers.dense ( inputs, units, activation)
在这里num_classes=3
定义损失函数和优化器
这里有一个技巧,没有必要给Optimizer传递平均的损失,直接将未平均的损失函数传给Optimizer即可
# 利用交叉熵定义损失
losses = tf.nn.softmax_cross_entropy_with_logits(
labels=tf.one_hot(labels_placeholder, num_classes),
logits=logits
)
# 平均损失
mean_loss = tf.reduce_mean(losses)
# 定义优化器,指定要优化的损失函数
optimizer = tf.train.AdamOptimizer(learning_rate=1e-2).minimize(losses)
https://blog.csdn.net/yhily2008/article/details/80262321揭示了如何衡量两个分布间的概率差
https://blog.csdn.net/wenqiwenqi123/article/details/78055740解释独热码的作用
labels_placeholder是标准答案 需要传入
定义模型保存器/载入器
# 用于保存和载入模型
saver = tf.train.Saver()
进入训练/测试执行阶段
with tf.Session() as sess:
如果trian为True,进行训练。训练需要使用sess.run(tf.global_variables_initializer())初始化参数,训练完成后,需要使用saver.save(sess, model_path)保存模型参数。
如果train为False,进行测试,测试需要使用saver.restore(sess, model_path)读取参数。
训练阶段执行
if train:
print("训练模式")
# 如果是训练,初始化参数
sess.run(tf.global_variables_initializer())
# 定义输入和Label以填充容器,训练时dropout为0.25
train_feed_dict = {
datas_placeholder: datas,
labels_placeholder: labels,
dropout_placeholdr: 0.25
}
for step in range(150):
_, mean_loss_val = sess.run([optimizer, mean_loss], feed_dict=train_feed_dict)
if step % 10 == 0:
print("step = {}\tmean loss = {}".format(step, mean_loss_val))
saver.save(sess, model_path)
print("训练结束,保存模型到{}".format(model_path))