【转载】 十图详解tensorflow数据读取机制(附代码)

原文地址:机器学习

https://zhuanlan.zhihu.com/p/27238630函数

 

 

 

何之源
深度学习(Deep Learning) 话题的优秀回答者
 
 
 

------------------------------------------------------------------------------------------------------学习

 

 

 

 

 

 

在学习tensorflow的过程当中,有不少小伙伴反映读取数据这一块很难理解。确实这一块官方的教程比较简略,网上也找不到什么合适的学习材料。今天这篇文章就以图片的形式,用最简单的语言,为你们详细解释一下tensorflow的数据读取机制,文章的最后还会给出实战代码以供参考。flex

 

 

 

 

 

 1、tensorflow读取机制图解spa

 首先须要思考的一个问题是,什么是数据读取?以图像数据为例,读取数据的过程能够用下图来表示:线程

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

 

 

 

 

 

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

 

 

 

读取线程源源不断地将文件系统中的图片读入到一个内存的队列中,而负责计算的是另外一个线程,计算须要数据时,直接从内存队列中取就能够了。这样就能够解决GPU由于IO而空闲的问题!对象

 

 

 

 

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

 

 

 为何要添加这一层文件名队列?咱们首先得了解机器学习中的一个概念: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各放入一次,并在以后标注队列结束。

 

 

 

 

 

 

 

 

 

 

 

 程序运行后,内存队列首先读入A(此时A从文件名队列中出队):

 

 

 

 

 

 

 

再依次读入B和C: 

 

 

 

 

 

 

 

 

此时,若是再尝试读入,系统因为检测到了“结束”,就会自动抛出一个异常(OutOfRange)。外部捕捉到这个异常后就能够结束程序了。这就是tensorflow中读取数据的基本机制。    若是咱们要跑2个epoch而不是1个epoch,那只要在文件名队列中将A、B、C依次放入两次再标记结束就能够了。

 

 

 

 

 

 

 

 

 

2、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内,数据的先后顺序就会被打乱,以下图所示:

 

 在tensorflow中,内存队列不须要咱们本身创建,咱们只须要使用reader对象从文件名队列中读取数据就能够了,具体实现能够参考下面的实战代码。

 

 

除了tf.train.string_input_producer外,咱们还要额外介绍一个函数:tf.train.start_queue_runners  。初学者会常常在代码中看到这个函数,但每每很难理解它的用处,在这里,有了上面的铺垫后,咱们就能够解释这个函数的做用了。 

 

 

 

在咱们使用  tf.train.string_input_producer  建立文件名队列后,整个系统其实仍是处于“停滞状态”的,也就是说,咱们文件名并无真正被加入到队列中(以下图所示)。此时若是咱们开始计算,由于内存队列中什么也没有,计算单元就会一直等待,致使整个系统被阻塞。

 

而使用tf.train.start_queue_runners以后,才会启动填充队列的线程,这时系统就再也不“停滞”。此后计算单元就能够拿到数据并进行计算,整个程序也就跑起来了,这就是函数tf.train.start_queue_runners的用处。

 

 

 

 

 

 

 

 

 

 

3、实战代码

 

 

 对应的代码以下:

 

# 导入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:

 

 

 

 

 

 

若是咱们设置filename_queue = tf.train.string_input_producer(filename, shuffle=False, num_epochs=5)中的shuffle=True,那么在每一个epoch内图像就会被打乱,如图所示:

 

 

 

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

 

 

 

 

 

 

 

4、总结

这篇文章主要用图解的方式详细介绍了tensorflow读取数据的机制,最后还给出了对应的实战代码,但愿可以给你们学习tensorflow带来一些实质性的帮助。若是各位小伙伴还有什么疑问,欢迎评论或私信告诉我,谢谢~

 

 

 

 

 

 

------------------------------------------------------------------------------------------------------

相关文章
相关标签/搜索