附:课程链接
第六讲.全连接网络实践
6.2制作数据集
由于个人使用Win7系统,并未完全按照课程所讲,以下记录的也基本是我的结合课程做的Windows系统+PyCharm操作。且本人有python基础,故一些操作可能简略。并未完全按照网课。
记住编写代码时,除注释内容外,字符均使用英文格式。
本节目标:
1、实现断点续训
2、输入真实图片,输出预测结果
3、制作数据集,实现特定应用
三、制作数据集,实现特定应用
制作数据集可以使用二进制文件tfrecords来保存对图片的序列化。
用tf.train.Example协议来存储训练数据。
训练数据的特征用键值对表示,如:
即:“原始图片:图片的值”、“标签:标签的值”。值可以是字符串、实数列表、整数列表。
用SerializeToString()把数据序列化成字符串存储。
生成tfrecords文件,具体语法:
writer = tf.python_io.TFRecordWriter(tfRecordName) #新建一个writer,tfRecordName的位置要告知生成的文件在哪里、叫什么名
for循环遍历每张图和标签:
"""
把每张图片和标签用tf.train.Example中的feature封装到example中,在tf.feature.Feature中特征会以字典的形式给出,
img_raw要放入二进制图片,labels放入这张图片所对应的标签
"""
example = tf.train.Example(features = tf.train.Feature(feature={
'img_raw':tf.train.Feature(bytes_list=tf.train.BytesList(value=[img_raw])),
'label':tf.train.Feature(int64_list=tf.train.Int64List(value=labels))
}))
writer.write(example.SerialToString()) #把example进行序列化
writer.close()
解析tfrecords文件,具体语法:
filename_queue = tf.train.string_input_producer([tfRecord_path]) #建立文件对列名,
#tfRecord_path需告知tfrecords文件放到何处,包括路径和文件名
reader = tf.TFRecordReader() #新建reader
_,serialized_example = reader.read(filename_queue) #读出来的每一个样本保存在serialized_example中
"""
进行解序列化
"""
features = tf.parse_single_example(serialized_example,
features={
'label':tf.FixedLenFeature([10],tf.int64),
'img_raw':tf.FixedLenFeature([],tf.string)
})
img = tf.decode_raw(features['img_raw'],tf.uint8) #从tfrecords文件中恢复img_raw到img
img.set_shape([784]) #把img的形状变成1行784列,也就是包含784个元素的一维数组
img = tf.cast(img,tf.float32) * (1./255) #把img的每个元素变为浮点数,并且在0-1之间
label = tf.cast(features['label'],tf.float32) #把label的每个数也变成浮点数,这样img和label就满足上节课模型的输入要求了
与上节课的程序相比,本节课新增了一个mnist_generateds.py,里面包含了tfrecords文件的生成和标签、图片的批获取。
在反向传播和测试程序中修改了图片和标签获取的接口、前向传播和应用程序与上节课完全相同。
首先让我们来看mnist_generateds.py文件:
import tensorflow as tf
import numpy as np
from PIL import Image
import os
image_train_path = './mnist_work/t10k-images-idx3-ubyte.gz/'
label_train_path = './mnist_work/t10k-labels-idx1-ubyte.gz/'
tfRecord_train = './mnist_data/mnist_train.tfrecords/'
image_test_path = './mnist_work/train-images-idx3-ubyte.gz/'
label_test_path = './mnist_work/train-labels-idx1-ubyte.gz/'
tfRecord_test = './mnist_data/mnist_test.tfrecords/'
data_path = './mnist_data'
resize_height = 28
resize_width = 28
"""
生成一个tfRecord文件
"""
def write_tfRecord(tfRecordName,image_path,label_path): #write_tfRecord接收到存放tfrecords文件的路径和文件名(tfrecordname)
#还接收到图像路径(image_path)和标签文件(label_path)
writer = tf.python_io.TFRecordWriter(tfRecordName) #新建一个writer,tfRecordName的位置要告知生成的文件在哪里、叫什么名
num_pic = 0 #为了显示进度,新建一个计数器num_pic
f = open(label_path,'r') #以“读”的形式打开标签文件,标签文件是一个txt文件。txt文件每行由图片名和标签组成,中间由空格隔开
contents = f.readlines() #用readlines()读取整个文件的内容赋给contents
f.close() #关闭文件
for content in contents: #for循环遍历每张图和标签
value = content.split() #用空格分隔每行的内容,分隔后组成列表value
image_path = image_path + value[0] #图片文件的路径就变成了image_path接图片名value[0]
img = Image.open(image_path) #打开文件
img_raw = img.tobytes() #将img转换成二进制数据
labels = [0] * 10 #把labels的每个元素赋值为0
labels[int(value[1])] = 1 #把labels所对应的标签位赋值为1
"""
创建一个example
把每张图片和标签用tf.train.Example中的feature封装到example中,在tf.feature.Feature中特征会以字典的形式给出,
img_raw要放入二进制图片,labels放入这张图片所对应的标签
"""
example = tf.train.Example(features = tf.train.Feature(feature={
'img_raw':tf.train.Feature(bytes_list=tf.train.BytesList(value=[img_raw])),
'label':tf.train.Feature(int64_list=tf.train.Int64List(value=labels))
}))
writer.write(example.SerialToString()) #用writer.write把example进行序列化
num_pic += 1 #每保存一张图片计数器+1
print("the number of picture:",num_pic) #打印出进度提示
writer.close() #当for循环把所有图片和标签都完成序列化后,关闭writer
print("write tfrecord successful") #打印提示
def generate_tfRecord():
isExists = os.path.exists(data_path) #判断保存路径是否存在
if not isExists: #如果不存在
os.makedirs(data_path) #用os.makedirs新建这个路径
print("The directory was created successfully") #打印出提示
else:
print("directory already exists") #否则告知已经存在目录
#用自定义函数write_tfRecord()把训练集中的图片(image_train_path)和标签(label_train_path)生成名叫tfRecord_train的tfrecords文件
write_tfRecord(tfRecord_train,image_train_path,label_train_path)
#用自定义函数write_tfRecord()把测试集中的图片(image_train_path)和标签(label_train_path)生成名叫tfRecord_train的tfrecords文件
write_tfRecord(tfRecord_test,image_test_path,label_test_path)
"""
从tfrecords文件解析出图片和标签
"""
def read_tfRecord(tfRecord_path): #read_tdrecord接收到待读取的tfrecords文件
filename_queue = tf.train.string_input_producer([tfRecord_path])
#用tf.train.string_input_producer建立文件名对列,
#tfRecord_path需告知tfrecords文件放到何处,包括路径和文件名
reader = tf.TFRecordReader() #新建reader
_,serialized_example = reader.read(filename_queue) #读出来的每一个样本保存在serialized_example中
"""
进行解序列化
标签和图片的键名应该和制作tfrecords的键名相同
标签要给出是几分类,例如我们的手写数字是十分类,则写10:tf.FixedLenFeature([10],tf.int64)
"""
features = tf.parse_single_example(serialized_example,
features={
'label':tf.FixedLenFeature([10],tf.int64),
'img_raw':tf.FixedLenFeature([],tf.string)
})
img = tf.decode_raw(features['img_raw'],tf.uint8) #decode_raw()函数img_raw字符串转换到无符号8位字符串整形
img.set_shape([784]) #把img的形状变成1行784列,也就是包含784个元素的一维数组
img = tf.cast(img,tf.float32) * (1./255) #把img的每个元素变为浮点数,并且在0-1之间
label = tf.cast(features['label'],tf.float32) #把label的每个数也变成浮点数,这样img和label就满足上节课模型的输入要求了
return img,label #返回图片和标签
"""
提或取tfrecord文件的函数
能够提或取训练集/测试集中的图片和标签,如果读取训练集,参数写True,测试集写False
还要写出一次读取多少组num
"""
def get_tfrecord(num,isTrain=True):
if isTrain:
tfRecord_path = tfRecord_train
else:
tfRecord_path = tfRecord_test
img,label = read_tfRecord(tfRecord_path) #读取过程用read_tfrecord实现。
"""
图片和标签分别是img、label,tf.train.shuffle_batch()会从总样本数顺序取出capacity组数据,打乱顺序每次输出batch_size组
如果capacity少于min_after_dequeue,会从总样本中取数据填满capacity。整个过程使用了两个线程(num_threads=2)
"""
img_batch,label_batch = tf.train.shuffle_batch([img,label],
batch_size=num,
num_threads=2,
capacity=1000,
min_after_dequeue=700)
return img_batch,label_batch #这个时候返回的图片和标签就是随机抽取出的batch_size组数据了
def main(): #从main函数看进去
generate_tfRecord() #执行此函数
if __name__ == '__main__':
main()
【备注,有人可能会对这段代码有疑惑,应该怎么设置呢?
image_train_path = './mnist_work/t10k-images-idx3-ubyte.gz/'
label_train_path = './mnist_work/t10k-labels-idx1-ubyte.gz/'
tfRecord_train = './mnist_data/mnist_train.tfrecords/'
image_test_path = './mnist_work/train-images-idx3-ubyte.gz/'
label_test_path = './mnist_work/train-labels-idx1-ubyte.gz/'
tfRecord_test = './mnist_data/mnist_test.tfrecords/'
data_path = './mnist_data'
让我们来查阅相关资料,发现:
截图来源:【代码】详解IDX-Ubyte文件格式 及 python读取
其他几个文件路径的设置大家可以根据自己的需求设置了】
我们来具体看下代码,在反向传播中,代码与上节课的变动:
①由于在梯度下降学习率中需要计算多少轮更新一次学习率(train.num_examples / BATCH_SIZE
)即总样本数/BATCH_SIZE,以前用mnist.train.num_examples
可以得到总样本数,现在我们必须手动给出总样本数是60000(即下图第15行和第31行发生了改变):
②以前用xs, ys = mnist.train.next_batch(BATCH_SIZE)
可以直接图片和标签喂给网络,现在要用自己写的get_tfrecord替换:img_batch , label_batch = mnist_generateds.get_tfrecord(BATCH_SIZE,isTrain=True)
,一次批获取BATCH组,因为从训练集中获取,所以isTrain参数为True。
③因为涉及到计算,故采用sess.run: xs, ys = sess.run([img_batch,label_batch])
,为了提高图片和标签的批获取处理效率,调用了线程协调器,后又关闭线程协调器。
②③如图:
在测试的代码中更改的方法,和反向传播的很像:
需要手动给出测试的总样本数是10000:
以及其他类似改变:
执行相关代码看一下,当准确率达到95%以上,运行mnist_app.py。
运行mnist_app结果:
可以看到,仍有数字识别不准确。让我们来看看老师的解释:
如果测试集跑出的准确率很高、应用程序准确率上不去,要考虑图片噪声是否过大,从图片预处理进行优化,使优化后的输入图片尽量接近测试集中的图片,因为测试集和训练集是相对独立的,测试集跑出的准确率对模型是有意义的。
如果电脑运行这段代码非常慢,可以把每1000轮显示loss改为每200轮显示loss,这样显示的速度就会相对快一些。
以下我将把有改动的代码mnist_backward.py、mnist_test.py附上:
mnist_backward.py:
"""
反向传播
"""
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import mnist_forward
import mnist_generateds
import os #os模块
BATCH_SIZE = 200 #定义每轮喂入神经网络多少张图片
LEARNING_RATE_BASE = 0.1 #最开始的学习率
LEARNING_RATE_DECAY = 0.99 #学习率衰减率
REGULARIZER = 0.0001 #正则化系数
STEPS = 50000 #共训练多少轮
MOVING_AVERAGE_DECAY = 0.99 #滑动平均衰减率
MODEL_SAVE_PATH = "mnist_model" #模型的保存路径
#MODEL_SAVE_PATH = "./model/"
MODEL_NAME = "mnist_model" #模型保存的文件名
train_num_examples = 60000
#对于反向传播而言,要训练网络参数
def backward(mnist): #在backward函数中读入mnist
x = tf.placeholder(tf.float32, [None, mnist_forward.INPUT_NODE]) #给x占位
y_ = tf.placeholder(tf.float32, [None, mnist_forward.OUTPUT_NODE]) #给y_占位
y = mnist_forward.forward(x, REGULARIZER) #调用前向传播程序计算输出y
global_step = tf.Variable(0,trainable=False) #给轮数计数器赋初值,设定为不可训练
ce = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=tf.argmax(y_, 1)) # 实现softmax和交叉熵的协同使用
cem = tf.reduce_mean(ce)
loss = cem + tf.add_n(tf.get_collection('losses')) #调用包含正则化的损失函数loss
#定义指数衰减学习率
learning_rate = tf.train.exponential_decay(
LEARNING_RATE_BASE,
global_step,
train_num_examples / BATCH_SIZE,
LEARNING_RATE_DECAY,
staircase=True)
#定义训练过程
train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss,global_step=global_step)
#定义滑动平均
ema = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)
ema_op = ema.apply(tf.trainable_variables())
with tf.control_dependencies([train_step, ema_op]):
train_op = tf.no_op(name='train')
#实例化saver
saver = tf.train.Saver() #tf.compat.v1.train.Saver
img_batch , label_batch = mnist_generateds.get_tfrecord(BATCH_SIZE,isTrain=True)
#在with结构中初始化所有变量
with tf.Session() as sess:
init_op = tf.global_variables_initializer()
sess.run(init_op)
ckpt = tf.train.get_checkpoint_state(MODEL_SAVE_PATH)
if ckpt and ckpt.model_checkpoint_path:
saver.restore(sess, ckpt.model_checkpoint_path)
"""
开启线程协调器
"""
coord = tf.train.Coordinator()
threads = tf.train.start_queue_runners(sess=sess, coord=coord)
#在for循环中迭代STEPS轮
for i in range(STEPS):
xs, ys = sess.run([img_batch,label_batch]) #每次读入BATCH_SIZE组图片和标签
_,loss_value,step = sess.run([train_op,loss,global_step],feed_dict={x:xs,y_:ys}) #喂入神经网络,执行相关过程
if i % 1000 == 0: #每1000轮打印loss值
print("After %d training steps(s),loss on training batch is %g"%(step,loss_value))
saver.save(sess,os.path.join(MODEL_SAVE_PATH,MODEL_NAME),global_step=global_step) #保存模型到当前会话
"""
关闭线程协调器
"""
coord.request_stop()
coord.join(threads)
def main():
mnist = input_data.read_data_sets("mnist_work/", one_hot=True)
#mnist = input_data.read_data_sets("./data/", one_hot=True)
backward(mnist)
if __name__ == '__main__':
main()
mnist_test.py:
import time #为了延时,导入了time模块
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import mnist_forward
import mnist_backward
import mnist_generateds
TEST_INTERVAL_SECS = 5 #定义程序循环的间隔时间是5s
TEST_NUM = 10000
def test():
with tf.Graph().as_default() as g: #绘制计算图中的节点
#给输入图像x和y_占位
x = tf.placeholder(tf.float32, [None, mnist_forward.INPUT_NODE])
y_ = tf.placeholder(tf.float32, [None, mnist_forward.OUTPUT_NODE])
#用前向传播过程计算出y的值
y = mnist_forward.forward(x, None)
#实例化可还原滑动平均的saver,这样所有参数在会话中北加载时会被赋值为各自的滑动平均值
ema = tf.train.ExponentialMovingAverage(mnist_backward.MOVING_AVERAGE_DECAY)
ema_restore = ema.variables_to_restore()
saver = tf.train.Saver(ema_restore)
#计算正确率
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32))
img_batch,label_batch = mnist_generateds.get_tfrecord(TEST_NUM,isTrain=False)
#用get_tfrecord()读取所有测试集10000张图片。由于从测试集中取数据,所以isTrain参数选择False
while True:
with tf.Session() as sess:
#加载训练好的模型ckpt,也就是把滑动平均值赋给各个参数
ckpt = tf.train.get_checkpoint_state(mnist_backward.MODEL_SAVE_PATH)
#如果已有ckpt模型则恢复
if ckpt and ckpt.model_checkpoint_path:
#恢复会话
saver.restore(sess,ckpt.model_checkpoint_path)
#恢复轮数
global_step = int(ckpt.model_checkpoint_path.split('/')[-1].split('-')[-1])
"""
开启线程协调器
"""
coord = tf.train.Coordinator()
threads = tf.train.start_queue_runners(sess=sess, coord=coord)
#执行批获取图片和标签
xs, ys = sess.run([img_batch, label_batch])
#计算准确率
accuracy_score = sess.run(accuracy,feed_dict = {x:mnist.test.images,y_:mnist.test.labels})
#打印提示
print("After %s training step(s),test accuracy = %g"%(global_step,accuracy_score))
"""
关闭线程协调器
"""
coord.request_stop()
coord.join(threads)
#如果没有模型
else:
print('No checkpoint file found') #模型不存在提示
return
time.sleep(TEST_INTERVAL_SECS)
def main():
mnist = input_data.read_data_sets("mnist_work/", one_hot=True)
test(mnist)
if __name__ == '__main__':
main()
第六讲至此结束。