Tensorflow数据读取机制
文件队列
参考了这篇博客的内容
为了实现数据读入和数据处理的管线化,tensorflow使用文件队列来独立处理数据读入。在设置好文件队列,以及相应的处理函数以后,tensorflow会自动调度,不断的把文件队列中的数据取出来,并包装成batch,使我们的训练过程始终有数据可用,不存在停下来等数据的情况。这很像一个虹吸管,在你架设好管道以后,数据就会源源不断的流向桶里。实现这个过程主要使用了Tensorflow里面的如下几个方法:
tf.train.string_input_producer #创建文件队列
tf.train.shuffle_batch #创建batch
首先,我们使用tf.train.string_input_producer
方法来将文件列表转化为一个队列:
import tensorflow as tf
import glob
def listfiles(rootpath,ext): #获得文件列表
return glob.glob(rootpath+'/*.{}'.format(ext))
filelist = listfiles('rootfolder','hdr') #获得根目录下所有后缀文件
filelist_queue = tf.train.string_input_producer(filelist,shuffle=True) #利用tf的方法,将filelist(list类型)转化为文件队列。
其中,文件队列filelist_queue
中,每一个entry对应了一个文件的位置,那么下一步应该是将文件队列中的文件取出来。在很多的博客中都使用了下面的方法来从队列中读取一个文件位置对应的图像。
image_reader = tf.WholeFileReader() ##定义一个reader
filename, image_file = image_reader.read(filelist_queue) ##将文件队列作为参数输入,
##每一次执行该函数就读取队列中一个entry对应文件位置的图像,返回
##图像名称和图像文件本身
image = tf.image.decode_jpeg(image_file) ##利用tf自带的jpeg解码器进行解码,得到图像矩阵
上面的方法适合用在png,jpg,tiff这样一些常用的图像格式上,因为tensorflow提供了image_reader,还有相应的decoder,但是如果数据不在这些格式里面的话,怎么办呢?首先想到的是自己定义一个函数,在这个函数里面读取需要的文件
我尝试写一个函数,将文件列表queue作为参数传递给该函数,然后希望该函数能够使用queue.dequeue()方法将文件位置返回出来,但是我发现使用queue.dequeue()的时候,在函数里面取出来的仍然是tensor类型,而不是一个文件路径的字符串。只有使用 content = sess.run(content)之后才能正确的返回这个字符串。我觉得这个可能跟tensorflow的处理机制有关。
为了避免浪费更多的时间,这里使用一种折衷的办法,首先将数据转换为tfrecord格式,然后利用tf自带的对tf的读取方法来解决这个问题。tfrecord跟mxnet中的数据制作过程非常类似,就是利用已经有的数据,制作一个二进制文件。我参考了这篇博客的内容和这篇博客。
TFRecodrs结构
TFRecords将用户的数据以二进制串的方式存储,这也就意味着你要首先定义数据格式。Tensorflow为此提供了两种格式:tf.train.Examples
和tf.train.SequenceExample
,所有需要存储到TFRecord中的数据都要首先转换为这两种格式之一,然后使用 tf.python_io.TFRecordWriter
将这些数据写入磁盘。在Tensorflow的官方网站可以看到tf.train.Examples
和tf.train.SequenceExample
其实都是protocol buffer
文件,而不是python类。
对于输入神经网络的每个数据entry,都由多个feature组成。tf.train.BytesList
, tf.train.FloatList
和tf.train.Int64List
同样为protocol,他们只有一个属性attibute
: value。他们的目的是将数据转化为相应的列表。比如肖申克的救赎和搏击俱乐部两部电影是两个数据entry,分别他们的得分为9.0和9.7。那么这些数据可以通过下面的方式转化为list:
movie_name_list = tf.train.BytesList(value=[b'The Shawshank Redemption', b'Fight Club'])
movie_rating_list = tf.train.FloatList(value=[9.0, 9.7])
注意:Python strings need to be converted to bytes, (e.g. my_string.encode(‘utf-8’)) before they are stored in a tf.train.BytesList. 所以,即使是在用ByteList去处理一般的8bits图像数据的时候,也需要进行img.tobytes()
处理,将图像处理转化为bytes。
tf.Train.Feature
则将某种特殊类型的数据列表包装成Tensorflow理解的格式,它也只有一个属性attibute
,即为bytes_list
,float_list
和int64_list
之一。紧接上面的例子,我们可以将movie_name_list
和 movie_rating_list
包装为Feature
movie_names = tf.train.Feature(bytes_list=movie_name_list)
movie_ratings = tf.train.Feature(float_list=movie_rating_list)
很自然的,Feature可以组成了Features
movie_dict = {
'Movie Names': movie_names,
'Movie Ratings': movie_ratings
}
movies = tf.train.Features(feature=movie_dict)
所有Features都组合完成以后,将其组成一个example,并通过tf.python_io.TFRecordWriter写入文件中,
example = tf.train.Example(features=movies)
with tf.python_io.TFRecordWriter('movie_ratings.tfrecord') as writer:
writer.write(example.SerializeToString())
总结起来,就是一个类似包包装的例子,tf.train.FloastFist-->tf.train.Feature-->tf.train.Features-->tf.train.Example。
TFRecord的读取
参考了Site1997、yyeqiustu以及这篇文章,Youtube上有一个TensorFlow的官方视频也做了非常好的讲解。这个知乎专栏也进行了详细的讲解。
使用Dataset API主要包含如下三个步骤:
- 载入数据:为数据创建一个Dataset实例
- 创建迭代器:使用Dataset实例来构建一个Iterator,这样就能通过其遍历数据
- 使用数据:跟模型对接
由于我们的数据存储在tfrecord中,可以使用如下的代码来创建一个Dataset实例:
dataset = tf.contrib.data.TFRecordDataset(tfrecords)
那么如何从dataset中取出元素呢?方法就是从Dataset实例化一个Iterator,然后对Iterator进行迭代
iterator = dataset.make_one_shot_iterator()
one_element = iterator.get_next() ###one_element 这里是取到的一个tensor
with tf.Session() as sess:
for i in range(5):
print(sess.run(one_element))
但是,这里我们要注意一个问题,在我们制作tfrecord的时候,