在学习tensorflow的过程中,读取数据这一块很难理解。确实这一块官方的教程比较简略,网上也找不到什么合适的学习材料。今天这篇文章就以图片的形式,用最简单的语言,为大家详细解释一下tensorflow的数据读取机制,文章的最后还会给出实战代码以供参考。
一、tensorflow读取机制图解
首先需要思考的一个问题是,什么是数据读取?以图像数据为例,读取数据的过程可以用下图来表示:

如何解决这个问题?方法就是将读入数据和计算分别放在两个线程中,将数据读入内存的一个队列,如下图所示:

而在tensorflow中,为了方便管理,在内存队列前又添加了一层所谓的“文件名队列”。
为什么要添加这一层文件名队列?我们首先得了解机器学习中的一个概念:epoch。对于一个数据集来讲,运行一个epoch就是将这个数据集中的图片全部计算一遍。如一个数据集中有三张图片A.jpg、B.jpg、C.jpg,那么跑一个epoch就是指对A、B、C三张图片都计算了一遍。两个epoch就是指先对A、B、C各计算一遍,然后再全部计算一遍,也就是说每张图片都计算了两遍。
tensorflow使用文件名队列+内存队列双队列的形式读入文件,可以很好地管理epoch。下面我们用图片的形式来说明这个机制的运行方式。如下图,还是以数据集A.jpg, B.jpg, C.jpg为例,假定我们要跑一个epoch,那么我们就在文件名队列中把A、B、C各放入一次,并在之后标注队列结束。


再依次读入B和C:


二、tensorflow读取数据机制的对应函数
如何在tensorflow中创建上述的两个队列呢?
对于文件名队列,我们使用tf.train.string_input_producer函数。这个函数需要传入一个文件名list,系统会自动将它转为一个文件名队列。
此外tf.train.string_input_producer还有两个重要的参数,一个是num_epochs,它就是我们上文中提到的epoch数。另外一个就是shuffle,shuffle是指在一个epoch内文件的顺序是否被打乱。若设置shuffle=False,如下图,每个epoch内,数据还是按照A、B、C的顺序进入文件名队列,这个顺序不会改变:

如果设置shuffle=True,那么在一个epoch内,数据的前后顺序就会被打乱,如下图所示:

除了tf.train.string_input_producer外,我们还要额外介绍一个函数:tf.train.start_queue_runners。初学者会经常在代码中看到这个函数,但往往很难理解它的用处,在这里,有了上面的铺垫后,我们就可以解释这个函数的作用了。
在我们使用tf.train.string_input_producer创建文件名队列后,整个系统其实还是处于“停滞状态”的,也就是说,我们文件名并没有真正被加入到队列中(如下图所示)。此时如果我们开始计算,因为内存队列中什么也没有,计算单元就会一直等待,导致整个系统被阻塞。


三、实战代码
我们用一个具体的例子感受tensorflow中的数据读取。如图,假设我们在当前文件夹中已经有A.jpg、B.jpg、C.jpg三张图片,我们希望读取这三张图片5个epoch并且把读取的结果重新存到read文件夹中。

对应的代码如下:
-
# 导入tensorflow
-
import tensorflow
as tf
-
-
# 新建一个Session
-
with tf.Session()
as sess:
-
# 我们要读三幅图片A.jpg, B.jpg, C.jpg
-
filename = [
'A.jpg',
'B.jpg',
'C.jpg']
-
# string_input_producer会产生一个文件名队列
-
filename_queue = tf.train.string_input_producer(filename, shuffle=
False, num_epochs=
5)
-
# reader从文件名队列中读数据。对应的方法是reader.read
-
reader = tf.WholeFileReader()
-
key, value = reader.read(filename_queue)
-
# tf.train.string_input_producer定义了一个epoch变量,要对它进行初始化
-
tf.local_variables_initializer().run()
-
# 使用start_queue_runners之后,才会开始填充队列
-
threads = tf.train.start_queue_runners(sess=sess)
-
i =
0
-
while
True:
-
i +=
1
-
# 获取图片数据并保存
-
image_data = sess.run(value)
-
with open(
'read/test_%d.jpg' % i,
'wb')
as f:
-
f.write(image_data)
我们这里使用filename_queue = tf.train.string_input_producer(filename, shuffle=False, num_epochs=5)建立了一个会跑5个epoch的文件名队列。并使用reader读取,reader每次读取一张图片并保存。
运行代码后,我们得到就可以看到read文件夹中的图片,正好是按顺序的5个epoch:


-
# 导入tensorflow
-
import tensorflow
as tf
-
-
# 新建一个Session
-
with tf.Session()
as sess:
-
# 我们要读三幅图片A.jpg, B.jpg, C.jpg
-
filename = [
'./data/A.png',
'./data/B.png',
'./data/C.png']
-
# string_input_producer会产生一个文件名队列
-
filename_queue = tf.train.string_input_producer(filename, shuffle=
True, num_epochs=
5)
-
# reader从文件名队列中读数据。对应的方法是reader.read
-
reader = tf.WholeFileReader()
-
key, value = reader.read(filename_queue)
-
# tf.train.string_input_producer定义了一个epoch变量,要对它进行初始化
-
tf.local_variables_initializer().run()
-
# 使用start_queue_runners之后,才会开始填充队列
-
threads = tf.train.start_queue_runners(sess=sess)
-
i =
0
-
while
True:
-
i +=
1
-
# 获取图片数据并保存
-
image_data = sess.run(value)
-
with open(
'data/test_%d.jpg' % i,
'wb')
as f:
-
f.write(image_data)
四、TensorFLow的几种图片读取方法
1.使用gfile读图片,decode输出是Tensor,eval后是ndarray
-
import matplotlib.pyplot
as plt
-
import tensorflow
as tf
-
import numpy
as np
-
-
print(tf.__version__)
-
-
image_raw = tf.gfile.FastGFile(
'test/a.jpg',
'rb').read()
#bytes
-
img = tf.image.decode_jpeg(image_raw)
#Tensor
-
#img2 = tf.image.convert_image_dtype(img, dtype = tf.uint8)
-
-
with tf.Session()
as sess:
-
print(type(image_raw))
# bytes
-
print(type(img))
# Tensor
-
#print(type(img2))
-
-
print(type(img.eval()))
# ndarray !!!
-
print(img.eval().shape)
-
print(img.eval().dtype)
-
-
# print(type(img2.eval()))
-
# print(img2.eval().shape)
-
# print(img2.eval().dtype)
-
plt.figure(
1)
-
plt.imshow(img.eval())
-
plt.show()
2.使用WholeFileReader输入queue,decode输出是Tensor,eval后是ndarray
-
import tensorflow
as tf
-
import os
-
import matplotlib.pyplot
as plt
-
-
def file_name(file_dir):
#来自http://blog.youkuaiyun.com/lsq2902101015/article/details/51305825
-
for root, dirs, files
in os.walk(file_dir):
#模块os中的walk()函数遍历文件夹下所有的文件
-
print(root)
#当前目录路径
-
print(dirs)
#当前路径下所有子目录
-
print(files)
#当前路径下所有非目录子文件
-
-
def file_name2(file_dir):
#特定类型的文件
-
L=[]
-
for root, dirs, files
in os.walk(file_dir):
-
for file
in files:
-
if os.path.splitext(file)[
1] ==
'.jpg':
-
L.append(os.path.join(root, file))
-
return L
-
-
path = file_name2(
'test')
-
#path2 = tf.train.match_filenames_once(path)
-
file_queue = tf.train.string_input_producer(path, shuffle=
True, num_epochs=
2)
#创建输入队列
-
image_reader = tf.WholeFileReader()
-
key, image = image_reader.read(file_queue)
-
image = tf.image.decode_jpeg(image)
-
-
with tf.Session()
as sess:
-
# coord = tf.train.Coordinator() #协同启动的线程
-
# threads = tf.train.start_queue_runners(sess=sess, coord=coord) #启动线程运行队列
-
# coord.request_stop() #停止所有的线程
-
# coord.join(threads)
-
-
tf.local_variables_initializer().run()
-
threads = tf.train.start_queue_runners(sess=sess)
-
-
#print (type(image))
-
#print (type(image.eval()))
-
#print(image.eval().shape)
-
for _
in path+path:
-
plt.figure
-
plt.imshow(image.eval())
-
plt.show()
3.使用read_file,decode输出是Tensor,eval后是ndarray
-
import matplotlib.pyplot
as plt
-
import tensorflow
as tf
-
import numpy
as np
-
-
print(tf.__version__)
-
-
image_value = tf.read_file(
'test/a.jpg')
-
img = tf.image.decode_jpeg(image_value, channels=
3)
-
-
with tf.Session()
as sess:
-
print(type(image_value))
# bytes
-
print(type(img))
# Tensor
-
#print(type(img2))
-
-
print(type(img.eval()))
# ndarray !!!
-
print(img.eval().shape)
-
print(img.eval().dtype)
-
-
# print(type(img2.eval()))
-
# print(img2.eval().shape)
-
# print(img2.eval().dtype)
-
plt.figure(
1)
-
plt.imshow(img.eval())
-
plt.show()