详解tensorflow训练⾃⼰的数据集实现CNN图像分类
1 概述
2 数据准备
教程的图⽚从Cifar数据集中获取,download_cifar.py从⾃带的Cifar数据集中获取了部分Cifar数据集,并将其转换为jpg图⽚。
默认从Cifar数据集中选取了3类图⽚,每类50张图,分别是
图⽚都放在data⽂件夹中,按照label_id.jpg进⾏命名,例如2_111.jpg代表图⽚类别为2(鸟),id为111。
3 导⼊相关库
除了Tensorflow,本教程还需要使⽤pillow(PIL),在Windows下PIL可能需要使⽤conda安装。
如果使⽤download_cifar.py⾃⼰构建数据集,还需要安装keras。
import os
#图像读取库
from PIL import Image
#矩阵运算库
import numpy as np
import tensorflow as tf
4 配置信息
设置了⼀些变量增加程序的灵活性。图⽚⽂件存放在data_dir⽂件夹中,train表⽰当前执⾏是训练还是测试,model-path约定了模型存放的路径。
# 数据⽂件夹
data_dir = "data"
# 训练还是测试
train = True
# 模型⽂件路径
model_path = "model/image_model"
5 数据读取
从图⽚⽂件夹中将图⽚读⼊numpy的array中。这⾥有⼏个细节:
# 从⽂件夹读取图⽚和标签到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)
tensorflow入门教程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))
6 定义placeholder(容器)
除了图像数据和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)
7 定义卷基⽹络(卷积和Pooling部分)
# 定义卷积层, 20个卷积核, 卷积核⼤⼩为5,⽤Relu激活
conv0 = v2d(datas_placeholder, 20, 5, lu)
# 定义max-pooling层,pooling窗⼝为2x2,步长为2x2
pool0 = tf.layers.max_pooling2d(conv0, [2, 2], [2, 2])
# 定义卷积层, 40个卷积核, 卷积核⼤⼩为4,⽤Relu激活
conv1 = v2d(pool0, 40, 4, lu)
# 定义max-pooling层,pooling窗⼝为2x2,步长为2x2
pool1 = tf.layers.max_pooling2d(conv1, [2, 2], [2, 2])
8 定义全连接部分
# 将3维特征转换为1维向量
flatten = tf.layers.flatten(pool1)
# 全连接层,转换为长度为100的特征向量
fc = tf.layers.dense(flatten, 400, lu)
# 加上DropOut,防⽌过拟合
dropout_fc = tf.layers.dropout(fc, dropout_placeholdr)
# 未激活的输出层
logits = tf.layers.dense(dropout_fc, num_classes)
predicted_labels = tf.arg_max(logits, 1)
9 定义损失函数和优化器
这⾥有⼀个技巧,没有必要给Optimizer传递平均的损失,直接将未平均的损失函数传给Optimizer即可。
# 利⽤交叉熵定义损失
losses = tf.nn.softmax_cross_entropy_with_logits(
_hot(labels_placeholder, num_classes),
logits=logits
)
# 平均损失
mean_loss = tf.reduce_mean(losses)
# 定义优化器,指定要优化的损失函数
optimizer = tf.train.AdamOptimizer(learning_rate=1e-2).minimize(losses)
10 定义模型保存器/载⼊器
如果在⽐较⼤的数据集上进⾏长时间训练,定期保存模型。
# ⽤于保存和载⼊模型
saver = tf.train.Saver()
11 进⼊训练/测试执⾏阶段
with tf.Session() as sess:
在执⾏阶段有两条分⽀:
12 训练阶段执⾏
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))
13 测试阶段执⾏
else:
print("测试模式")
# 如果是测试,载⼊参数
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))
完整代码和相关教程可以查看我的Github代码链接
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论