TensorFlow 的数据读取机制

首先需要思考的一个问题是, 什么是数据读取?以图像数据为例, i卖取
数据的过程可以用图2-2 来表示。
这里写图片描述

假设硬盘中有一张图片数据集0001.jpg、0002.jpg、0.0003.jpg……..只需要把它们读取到内存中,然后提供给GPU或是CPU进行计算就可以了。这听起来很窑易,但事实远没离那么简单。事实上,必须先读入数据后才能计算,假设读入用时0.1s,计算用时0.9s,那么意味着每过1s,GPU都会有0.1s无事可做,这大大降低了运算效率。

如何解决这个问题呢?方法就是将读入数据和计算分别放在两个线程中,将数据读入到内存的一个队列中,如图2-3所示。
这里写图片描述

读取线程源源不断地将文件系统中的图片读入一个内存的队列中,而负责计算的是另一个线程,计算需要数据肘,直接从内存队列中取就可以了。这样可以解决GPU 因为1 / 0 而空闲的问题。

而在TensorFlow中,为了方便管理,在内存队列前又添加了一层所谓的“文件名队列”。

为什么要添加这一层文件名队列呢?首先需要了解机器学习中的一个概念: epoch 。对于一个数据集来讲,运行一个epoch 就是将这个数据集中的图片全部计算一遍。如果一个数据集中南三张图片A .jpg 、B . jpg 、C.jpg ,那么运行一个epoch 就是指对A 、B 、C 三张图片都计算一遍。两个ep och就是指先对A 、B 、C 各计算一遍,然后再全部计算一遍, 也就是说每张图片都计算了两遍。

Tensor Flow 使用“文件名队列+内存队列”双队列的形式读入文件,可以很好地管理epoch 。下面用图片的形式来说明这个机制的运行方式。如图2-4 所示,还是以数据集A.jpg 、B.jpg 、C 扣g 为例,假定要运行一个epoc h ,那么就在文件名队列中把A 、B 、C 备放入一次,并在之后标注队列结束。这里写图片描述
程序运行后,内存队列首先读入A (此时A 从文件名队列中出队),如图2-5 所示。
这里写图片描述
再依次读入B 和C ,如图2-6 所示。
这里写图片描述
此时, 如果再尝试读入3 由于系统检测到了“结束”,就会自动抛出一个异常(OutOfR.ange ) 。外部捕捉到这个异常后就可以结束程序了。这就是Tensor Flow 中读取数据的基本机制。如果要运行2 个epoch 而不是1 个epoch,则只要在文件名队列中将A 、B 、C 依次放入两次再标记结束就可以了。

如何在TensorFlow 中创建上述的两个队列呢?

对于文件名队列,使用tf. train. string_ input_producer 函数。这个函数需要传入一个文件名list ,系统会自动将包转为一个文件名队列。

此外,tf. train.string_ input_producer还有两个重要的参数:一个是num_epochs,它就是上文中提到的epoch数;另外一个是shuffle, shuffle 是指在一个epoch 内文件的顺序是否被打乱。若设置s huffle=False ,如图2-7所示,每个epoch 内,数据仍然按照A 、B 、C 的顺序进入文件名队列,这个顺序不会改变。
这里写图片描述

如果设置shuffle=True ,那么在一个epoch 内,数据的前后顺序就会被打乱,如图2-8 所示。
这里写图片描述

在TensorFlow 中,内存队列不需要自己建立,只需要使用reader 对象从文件名队列中读取数据就可以了,具体实现可以参考下面的实战代码。

除了tf. train.string_input_producer 外,还要额外介绍一个函数:tf.train.start_ queue_runners 。初学者会经常在代码中看到这个函数,但往往很难理解宫的用处。高了上面的铺垫后,就可以解释这个函数的作用了。

在使用tf. train.string_input_producer创建文件名队列后,整个系统其实还处于“停滞状态”,也就是说,文件名并没有真正被加入到队列中,如图2-9所示。如果此日才开始计算3 因为内存队列中什么也没高,计算单元就会一直等待,导致整个系统被阻塞。
这里写图片描述
而使用tf. train.start_ queue_ runners 之后,才会启动填充队列的结程,这时系统就不再停滞” 。此后,计算单元就可以拿到数据并进行计算3 整个程序也就运行起来了,这就是函数tf. train.start_ queue_ runners 的用处。

下面用一个具体的例子体会TensorFlow中的数据读取(对应的程序为test.py )。如图2-10所示,假设在当前文件夹中已经再A.jpg 、B.jpg 、C.jpg三张图片,希望读取这三张图片的5个epoch 并且把读取的结果重新存到read 文件夹中。
这里写图片描述

代码如下(对应的文件为test.py ):

# coding:utf-8
import os
if not os.path.exists('read'):
    os.makedirs('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)
# 程序最后会抛出一个OutOfRangeError,这是epoch跑完,队列关闭的标志

这里使用filename_queue = tf.train.string_input_producer(filename, shuffle=False, num_epochs=5)建立了一个会运行5个epoch的文件名队列。并使用reader i卖取, reader 每次读取一张国片并保存。

运行代码后(程序最后会抛出一个OutOfRangeError 异常,不必担心,
这就是epoch 跑完,队列关闭的标志),得到read 文件夹中的图片,正好是
按Jll页序的5 个epoch ,如图2-11所示。
这里写图片描述

如果设置fi lename_queue=tf. train.string_ input_producer( filename, shuffle=
False, n u m _ epochs=5 )中的shuffle=True ,那么在每个epoch 内图像会被打乱,如图2-12 所示。
这里写图片描述

这里只是用三张图片举例,实际应用中一个数据集肯定不止3 张图片,不过涉及的原理都是共通的。

猜你喜欢

转载自blog.csdn.net/czp_374/article/details/81121067