#
#作者:韦访
#博客:https://blog.csdn.net/rookie_wei
#微信:1007895847
#添加微信的备注一下是CSDN的
#欢迎大家一起学习
#
1、概述
我们之前讲的都是基于MNIST数据集,而TensorFlow又封装了读取这个数据集的方法,所以我们虽然调用起来非常爽,但是,这也导致了我们还没掌握自己读取数据集数据来训练模型的能力,接下来我将用几讲来专门说这个。首先来看队列。
环境配置:
操作系统:Win10 64位
显卡:GTX 1080ti
Python:Python3.7
TensorFlow:1.15.0
2、队列
概念
目前我们接触的Tensorflow数据读取机制是往占位符placeholder传入feed_dict,这种机制比较简单,前面的例子也用过,这里不赘述。现在我们看队列机制,
如上图,要训练数据,得分两步,第一步,先将数据从硬盘加载到内存中,第二步,拱给CPU或者GPU运算。如果只用一个线程,那么,运行第一步的时候第二步就得等着,运行第二步的时候,第一步就得等着,这样就浪费时间了。
如上图,解决这个问题,就得将第一步和第二步分别放在两个线程中:一个线程不断的把数据读入内存,另一个线程从内存中取出数据进行计算。
为了方便管理,TensorFlow在内存队列前又加了一层“文件名队列”。
函数
对于文件名队列,使用tf.train.string_input_producer 函数,该函数有三个比较重要的参数,string_tensor参数向这个函数传入文件名list,系统就自动将它转为一个文件名队列。num_epochs参数传入的是epoch数,即将传入的list全部运算几遍。shuffle参数决定在一个epoch内,文件的顺序是否被打乱,若shuffle=False,则不打乱,否则打乱,默认是打乱的。
在tensorflow中,内存队列不需要我们自己建立,只需要使用reader对象从文件名队列中读取数据即可。
需要注意的是,使用tf.train.string_input_producer函数创建队列后,程序并没有马上将文件名加入队列,而是要运行tf.train.start_queue_runners函数后,才真正开始工作。
3、代码
为了便于理解,下面给出一个简单的例子,
如上图,有三张图片,分别为1.jpg, 2.jpg,3.jpg,首先来看了当shuffle=False时的读取顺序,代码如下,
#encoding:utf-8
import tensorflow as tf
import os
def delfiles(path):
ls = os.listdir(path)
for i in ls:
c_path = os.path.join(path, i)
if os.path.isdir(c_path):
delfiles(c_path)
else:
os.remove(c_path)
def main(argv=None):
path = "MNIST_QUEUE"
filenames = [os.path.join(path, "images", "%d.jpg" % i) for i in range(1, 4)]
# shuffle=False表示不打乱顺序,num_epochs=3表示整个队列获取三次
queue = tf.train.string_input_producer(filenames, shuffle=False, num_epochs=3)
# 读取文件名队列中的数据
reader = tf.WholeFileReader()
key, value = reader.read(queue)
with tf.Session() as sess:
# 初始局部化变量,注意这个函数跟tf.global_variables_initializer.run()是不一样的
# 因为string_input_producer函数的num_epochs=3传入的是局部变量
tf.local_variables_initializer().run()
threads = tf.train.start_queue_runners(sess=sess)
i = 0
destpath = os.path.join(path, "shuffle_False")
if os.path.exists(destpath):
delfiles(destpath)
else:
os.makedirs(destpath)
while True:
i += 1
data = sess.run(value)
with open(os.path.join(destpath, 'image_%d.jpg' % i), 'wb') as fd:
fd.write(data)
if __name__ == '__main__':
tf.app.run()
运行结果,
Instructions for updating:
To construct input pipelines, use the `tf.data` module.
Traceback (most recent call last):
File "C:\Users\wilf\Anaconda3\lib\site-packages\tensorflow_core\python\client\session.py", line 1365, in _do_call
return fn(*args)
File "C:\Users\wilf\Anaconda3\lib\site-packages\tensorflow_core\python\client\session.py", line 1350, in _run_fn
target_list, run_metadata)
File "C:\Users\wilf\Anaconda3\lib\site-packages\tensorflow_core\python\client\session.py", line 1443, in _call_tf_sessionrun
run_metadata)
tensorflow.python.framework.errors_impl.OutOfRangeError: FIFOQueue '_0_input_producer' is closed and has insufficient elements (requested 1, current size 0)
[[{{node ReaderReadV2}}]]
运行程序时,会抛出上面所示的异常,莫慌,这是因为队列里的数被读完了,系统该“结束”了,所以抛出OutOfRangeError异常,我们也可以去捕获这个异常。来看看MNIST_QUEUE\shuffle_False文件夹下生成了什么,
可以看到,程序是顺序的读取了“1”“2”“3”,读了3次。当shuffle=True时,我们来看结果如何,代码如下,
#encoding:utf-8
import tensorflow as tf
import os
def delfiles(path):
ls = os.listdir(path)
for i in ls:
c_path = os.path.join(path, i)
if os.path.isdir(c_path):
delfiles(c_path)
else:
os.remove(c_path)
def main(argv=None):
path = "MNIST_QUEUE"
filenames = [os.path.join(path, "images", "%d.jpg" % i) for i in range(1, 4)]
# shuffle=True表示打乱顺序,num_epochs=3表示整个队列获取三次
queue = tf.train.string_input_producer(filenames, shuffle=True, num_epochs=3)
# 读取文件名队列中的数据
reader = tf.WholeFileReader()
key, value = reader.read(queue)
with tf.Session() as sess:
# 初始局部化变量,注意这个函数跟tf.global_variables_initializer.run()是不一样的
# 因为string_input_producer函数的num_epochs=3传入的是局部变量
tf.local_variables_initializer().run()
threads = tf.train.start_queue_runners(sess=sess)
i = 0
destpath = os.path.join(path, "shuffle_True")
if os.path.exists(destpath):
delfiles(destpath)
else:
os.makedirs(destpath)
try:
while True:
i += 1
data = sess.run(value)
with open(os.path.join(destpath, 'image_%d.jpg' % i), 'wb') as fd:
fd.write(data)
except:
print("Done...")
if __name__ == '__main__':
tf.app.run()
运行结果,
可以看到,还是读取了3次,但顺序是被打乱了。
4、将MNIST数据集保存成图片
虽然上面已经讲清楚了队列,但是,只有3张图片,真正的数据集肯定不会只有3张图片。所以,还是以MNIST数据集为例,我们将MNIST数据集以图片的形式保存,
如上图所示,共有10个文件夹,每个文件夹下保存着对应的图片,即文件夹名称是以label命名的,例如,0文件夹下的内容如下,
以此类推,受篇幅限制,代码在最后给。
5、通过队列读取数据集并且使用CNN识别
数据集现在有了,接着我们就通过队列读取数据集,并且使用上一讲的CNN进行识别。
先来看读取数据集部分的代码。首先,将数据集中所有图片和其对应的标签导入到两个列表中,代码如下,
# 导入图片名和对应的标签
def load_files(dir):
print("Loading files...")
fileslist = []
labelslist = []
for path, dirs, files in os.walk(dir):
for file in files:
fileslist.append(os.path.join(path, file))
label = os.path.basename(path)
labelslist.append([i == int(label) for i in range(0, 10)])
# return shuffle(np.asarray(fileslist), np.asarray(labelslist))
return np.asarray(fileslist), np.asarray(labelslist).astype(np.float)
接着,我们批量获取数据,代码如下,
# 批量获取数据
def get_batch(fileslist, labelslist, batchsize):
queue = tf.train.slice_input_producer([fileslist, labelslist])
files = queue[0]
labels = queue[1]
imagesrf = tf.read_file(files)
images = tf.image.decode_jpeg(imagesrf, 1)
images = tf.image.resize_image_with_crop_or_pad(images, 28, 28)
images = tf.reshape(images, [28 * 28])
images = tf.cast(images, tf.float32)
images /= 255
image_batch, label_batch = tf.train.batch([images, labels], batch_size=batchsize, num_threads=64)
label_batch = tf.reshape(label_batch, [batchsize, 10])
return image_batch, label_batch
定义上面两个函数以后,我们就要用起来,用法如下,
fileslist, labelslist = load_files("MNIST_QUEUE/all_images")
images_batch, labels_batch = get_batch(fileslist, labelslist, 50)
接着,只要在会话中run images_batch, labels_batch就可以了,完整代码如下,
import os
import tensorflow.compat.v1 as tf
from sklearn.utils import shuffle
import numpy as np
# 导入图片名和对应的标签
def load_files(dir):
print("Loading files...")
fileslist = []
labelslist = []
for path, dirs, files in os.walk(dir):
for file in files:
fileslist.append(os.path.join(path, file))
label = os.path.basename(path)
labelslist.append([i == int(label) for i in range(0, 10)])
# return shuffle(np.asarray(fileslist), np.asarray(labelslist))
return np.asarray(fileslist), np.asarray(labelslist).astype(np.float)
# 批量获取数据
def get_batch(fileslist, labelslist, batchsize):
queue = tf.train.slice_input_producer([fileslist, labelslist])
files = queue[0]
labels = queue[1]
imagesrf = tf.read_file(files)
images = tf.image.decode_jpeg(imagesrf, 1)
images = tf.image.resize_image_with_crop_or_pad(images, 28, 28)
images = tf.reshape(images, [28 * 28])
images = tf.cast(images, tf.float32)
images /= 255
image_batch, label_batch = tf.train.batch([images, labels], batch_size=batchsize, num_threads=64)
label_batch = tf.reshape(label_batch, [batchsize, 10])
return image_batch, label_batch
def main(argv=None):
fileslist, labelslist = load_files("MNIST_QUEUE/all_images")
images_batch, labels_batch = get_batch(fileslist, labelslist, 50)
# 转换后的图片保存的路径
save_dir = 'MNIST_QUEUE/wf_images/'
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
coord = tf.train.Coordinator()
threads = tf.train.start_queue_runners(sess=sess, coord=coord)
while True:
images, labels = sess.run([images_batch, labels_batch])
print(images.shape)
print('label', labels.shape)
if __name__ == '__main__':
tf.app.run()
运行结果,
通过打印images和labels的形状可以看到,我们batch设置为50,图片数据是长度为784的向量,label是one-hot格式的,跟我们之前读取的MNIST数据集格式一样的。接下来就使用上一讲的CNN代码来进行训练和识别,核心代码如下,
def main(argv=None):
fileslist, labelslist = load_files("MNIST_QUEUE/all_images")
images_batch, labels_batch = get_batch(fileslist, labelslist, 50)
# 创建x占位符,用于临时存放MNIST图片的数据,
# [None, 784]中的None表示不限长度,而784则是一张图片的大小(28×28=784)
x = tf.placeholder(tf.float32, [None, 784])
# label 存的是实际图像的标签,即对应于每张输入图片实际的值
label = tf.placeholder(tf.float32, [None, 10])
# 将图片从长度为784的向量重新还原为28×28的矩阵图片,
# 因为MNIST是黑白图片,所以深度为1,
# 第一个参数为-1,表示一维的长度不限定,这样就可以灵活设置每个batch的训练的个数了
x_image = tf.reshape(x, [-1, 28, 28, 1])
# 搭建神经网络结构
acc, op, keep_prob, loss = net(x_image, label)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
coord = tf.train.Coordinator()
# 从这里开始,队列才真正的开始工作
threads = tf.train.start_queue_runners(sess=sess, coord=coord)
for i in range(20000):
images, labels = sess.run([images_batch, labels_batch])
# 将数据传入神经网络,开始训练
sess.run(op, feed_dict={x: images, label:labels, keep_prob: 0.5})
if i % 100 == 0:
train_accuracy = sess.run(acc, feed_dict={x: images, label:labels, keep_prob: 1.0})
print("step %d, training accuracy %g" % (i, train_accuracy))
运行结果,
跟以前效果一样。
6、源码下载
完整代码链接如下,
https://mianbaoduo.com/o/bread/Ypaclpo=
下一讲,我们来看看读取数据集之TFRecord的基本概念和知识。