首页 > 其他分享 >10 张图帮你搞定 TensorFlow 数据读取机制

10 张图帮你搞定 TensorFlow 数据读取机制

时间:2023-08-12 15:34:21浏览次数:42  
标签:10 文件名 队列 jpg epoch tensorflow tf TensorFlow 张图

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

一、tensorflow读取机制图解

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

10 张图帮你搞定 TensorFlow 数据读取机制_数据

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

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

10 张图帮你搞定 TensorFlow 数据读取机制_tensorflow_02

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

而在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各放入一次,并在之后标注队列结束。

10 张图帮你搞定 TensorFlow 数据读取机制_tensorflow_03

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

10 张图帮你搞定 TensorFlow 数据读取机制_文件名_04

再依次读入B和C:

10 张图帮你搞定 TensorFlow 数据读取机制_文件名_05

10 张图帮你搞定 TensorFlow 数据读取机制_tensorflow_06

此时,如果再尝试读入,系统由于检测到了“结束”,就会自动抛出一个异常(OutOfRange)。外部捕捉到这个异常后就可以结束程序了。这就是tensorflow中读取数据的基本机制。如果我们要跑2个epoch而不是1个epoch,那只要在文件名队列中将A、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的顺序进入文件名队列,这个顺序不会改变:

10 张图帮你搞定 TensorFlow 数据读取机制_文件名_07

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

10 张图帮你搞定 TensorFlow 数据读取机制_数据_08

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

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

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

10 张图帮你搞定 TensorFlow 数据读取机制_tensorflow_09

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

10 张图帮你搞定 TensorFlow 数据读取机制_数据_10

三、实战代码

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

10 张图帮你搞定 TensorFlow 数据读取机制_文件名_11

对应的代码如下:

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

10 张图帮你搞定 TensorFlow 数据读取机制_tensorflow_12

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

10 张图帮你搞定 TensorFlow 数据读取机制_tensorflow_13

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

四、总结

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

标签:10,文件名,队列,jpg,epoch,tensorflow,tf,TensorFlow,张图
From: https://blog.51cto.com/u_16223856/7059204

相关文章

  • Gitc错误Failed to connect to 127.0.0.1 port 1080 Connection refused拒绝连接错误
    一、git拒绝连接原因分析使用git从远程仓库下载代码出现上述的错误是因为使用了proxy代理,所以要解决该问题,核心操作就是要取消代理二、解决方式1、查看Linux当前有没有使用代理通过git的配置文件查看有无使用代理(没有成功)查询是否使用代理:gitconfig--globalhttp.proxyg......
  • spire.pdf导出图片,只能导出11张图片,而且带有水印
    ///<summary>///有水印///</summary>///<paramname="pdfUrl">pdf文件路径</param>///<paramname="imagePath">输出图片路径</param>publicstaticvoidSpirePdfToImage(string......
  • Hybrid-SORT起飞 | 超过DeepSORT将近10个点的多目标跟踪香不香?
    前言 多目标跟踪(MOT)旨在在帧间检测和关联所有所需的目标。大多数方法通过明确或隐式地利用强大的线索(即空间和外观信息)来完成任务,这些线索表现出强大的实例级别判别能力。然而,当出现目标遮挡和聚类时,由于目标之间的高度重叠,空间和外观信息同时变得模糊不清。在本文中,作者证明MOT......
  • 微信第三方平台前100名
    微信第三方平台也叫公众平台第三方,通俗地讲就是利用微信公众平台给出的开发文档,经过后期的开发,形成一套功能相对完善的系统,可以给公众号提供增值服务的计算机程序。2018伊始,有哪些好用的微信第三方平台呢?小编特意从网络上搜集了一下,整理了前100名微信第三方平台。【排名不分先后......
  • 知识图谱(Knowledge Graph)- Neo4j 5.10.0 Docker 安装
    拉镜像[root@localhost~]#cat/etc/docker/daemon.json{"registry-mirrors":["https://XXX.mirror.aliyuncs.com"]#阿里镜像源}#拉取镜像[root@localhost~]#dockerpullneo4j:5.10.0运行#创建目录[root@localhost~]#mkdir-p/opt/neo4j#--envNEO......
  • 知识图谱(Knowledge Graph)- Neo4j 5.10.0 CentOS 安装
    系统需求版本JDKCPU内存硬盘Neo4j5.x17Intelx86-x64Corei3minimum,Corei7recommended.AMDx86-x64,MacARM.最低2GB,推荐16GB+10G+Neo4j5.x11Neo4j5.x8JDK17下载:https://www.oracle.com/java/technologies/downloads/#java17基于jar的......
  • 安装Virtualbox Install Virtualbox in Ubuntu 13.10/13.04/12.10/12.04 using PPA
    VirtualBoxisapowerfulOpenSourcevirtualizationsoftwarefromOracle,WhichsupportsLinux,WindowsandMacOS.CurrentstablereleaseisVirtualBox4.3.RecommendedwayofinstallingVirtualboxinUbuntu/LinuxMintisbyaddingPPAtoyourrepositor......
  • CodeForces 1610F Mashtali: a Space Oddysey
    洛谷传送门CF传送门比较有启发性的题。首先,设\(a_u\)为与点\(u\)相连的边权和,答案的上界显然是\(\sum\limits_{i=1}^n[a_u\bmod2=1]\)。之后我们把P7816「Stoi2029」以父之名第一篇题解的做法搬过来,也就是:建一个虚点向原图度数为奇数的点连边权为\(1\)的边......
  • 代码随想录算法训练营第十六天| 104.二叉树的最大深度 111.二叉树的最小深度 222.
      104.二叉树的最大深度 (优先掌握递归)    卡哥建议:什么是深度,什么是高度,如何求深度,如何求高度,这里有关系到二叉树的遍历方式。大家要先看视频讲解,就知道以上我说的内容了,很多录友刷过这道题,但理解的还不够。   题目链接/文章讲解/视频讲解:https://programmerc......
  • LeetCode 1049.最后一块石头的重量II
    1.题目:1049. 最后一块石头的重量II有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x<=y。那么粉碎的可能结果如下:如果 x==y,那么两块石头都会被完全粉......