Tensorflow-数据读取的三种方式

参考原文:1.https://blog.csdn.net/chengshuhao1991/article/details/78644966

2.https://blog.csdn.net/u014061630/article/details/80712635

最近在学习Tensorflow文档。在对QueueRunner的学习过程中顺便了解了一下Tensorflow中常见的数据读取方式。

在处理数据的过程中,由于现在硬件性能的极大提升,数值计算的性能可以通过加强硬件性能来提升,因此数据读取即IO往往会成为系统运行性能的瓶颈。在Tensorflow框架中提供了三种数据读取的方式:

  1. Preloaded data:预加载数据。constant...
  2. Feeding:由占位符代替数据,运行时填入数据。placeholder,feed_dict...
  3. Reading from file:从文件中直接读取

1.Preloaded data 预加载数据

constant...

数据直接添加进Graph中,由Graph传入session中运行。

import tensorflow as tf

# 设计graph
x = tf.constant([1,2,3], name='x')
y = tf.constant([2,3,4], name='y')
z = tf.add(x,y, name='z')

# 打开一个session,计算z
with tf.Session() as sess:
    print(sess.run(z))

在设计Graph时,x和y就已经被定义成了两个有具体值的constant,在计算z的时候则直接取Graph中的x和y的值。

2.Feeding 由占位符代替数据,运行时填入数据

placeholder,feed_dict...

在Graph中仅仅是加入占位符placeholder,可以理解为外部数据进入Graph的一个接口,或者说是Buff,由feed_dict将数据填入placeholder即Buff中,从而实现将外部的数据加入Graph中从而完成计算

import tensorflow as tf

# 设计graph,用占位符代替
x = tf.placeholder(tf.int16)
y = tf.placeholder(tf.int16)
z = tf.add(x,y, name='z')

# 打开一个session
with tf.Session() as sess:
    #创建数据
    xs = [1,2,3]
    ys = [2,3,4]
    # 运行session,用feed_dict来将创建的数据传递进占位符
    print(sess.run(z, feed_dict={x: xs, y: ys}))

在这里x, y只是占位符,没有具体的值,运行时将使用sess.run()中的feed_dict参数,将Python产生的数据喂给后端,并计算z。

3.Reading from file 从文件中直接读取

前两种方法很方便,但是在遇到大型数据时会很吃力,即使是Feeding,中间环节的增加也是不小的开销。比如数据类型转换等等。最好的解决方法就是在Graph中定义好文件读取的方法,让TF自己去从文件中读取数据,并解码成可使用的样本集。

也就是说,直接在Graph中添加文件读取的相关操作,之后直接在运行Graph的时候实现文件的读取。相比于Feeding方式,就省去了将数据喂入占位符这个Buff的开销,而是直接从指定的文件中获取数据。因为如果是Feeding方式,要先将数据加入placeholder中,再在Graph中,从placeholder处获取数据执行相关的操作。而相比之下,直接从文件中获取数据的方式,就省去了先把数据加入占位符placeholder中这个步骤,而是直接在Graph中,从文件中获取数据并利用数据执行相应的操作。

这种从文件中直接读取数据的方式需要借助Queue才能较好地解决IO瓶颈的问题。

Queue有三个特点:

  1. producer-consumer pattern(生产消费模式)
  2. 独立于主线程执行
  3. 异步IO:reader.read(queue),tf.train.batch()

首先由一个单线程将文件名加入文件名队列,两个Reader同时从文件名队列中取出文件名并且读取数据。Decoder将读出的数据解码后加入样本队列,最后单个或者批量地取出样本。

从文件名队列中取出文件名并读取数据再加入样本队列 这一系列的过程是由Tensorflow自动进行完成的,并不需要手动完成。

Reader是对文件名队列进行的操作。这也就是Tensorflow直接读取文件获取数据的方式的特殊之处:在使用Reader将文件数据读入内存之前,会先创建一个文件名队列。

这里通过三段代码逐步实现上图的数据流。不使用随机出队列方式,让结果更清晰。

3.1.文件准备

$ echo -e "Alpha1,A1\nAlpha2,A2\nAlpha3,A3" > A.csv  
$ echo -e "Bee1,B1\nBee2,B2\nBee3,B3" > B.csv  
$ echo -e "Sea1,C1\nSea2,C2\nSea3,C3" > C.csv  
$ cat A.csv  
Alpha1,A1  
Alpha2,A2  
Alpha3,A3  

3.2.1.单个Reader,单个样本

import tensorflow as tf  
# filenames为文件名列表,使用tf.train.string_input_producer函数将文件名列表 
# 转化为输入文件名队列,该队列为先进先出队列
filenames = ['A.csv', 'B.csv', 'C.csv']  
filename_queue = tf.train.string_input_producer(filenames, shuffle=False) 
# 定义Reader 。一个Reader
reader = tf.TextLineReader()  
key, value = reader.read(filename_queue)
# 定义Decoder
example, label = tf.decode_csv(value, record_defaults=[['null'], ['null']]) 
# 运行Graph  
with tf.Session() as sess:  
    coord = tf.train.Coordinator()  #创建一个协调器,管理线程
# 启动QueueRunner, 此时文件名队列已经进队。    
    threads = tf.train.start_queue_runners(sess=sess, coord=coord)  
    for i in range(10): 
# 取样本的时候,一个Reader先从文件名队列中取出文件名,读出数据,
# Decoder解析后进入样本队列。 
        print example.eval()    
    coord.request_stop()  
    coord.join(threads) 

3.2.2.单个Reader,多个样本

import tensorflow as tf  
filenames = ['A.csv', 'B.csv', 'C.csv'] 
# 可使用tf.train.match_filenames_once函数,检索符合正则表达式的相应名称文件
# 并返回得到一个相关文件名的列表
# filenames = tf.train.match_filenames_once('.\data\*.csv') 
filename_queue = tf.train.string_input_producer(filenames, shuffle=False)  
# 一个Reader
reader = tf.TextLineReader()  
key, value = reader.read(filename_queue)  
# 定义Decoder
example, label = tf.decode_csv(value, record_defaults=[['null'], ['null']])  
# Decoder解码后数据会进入样本队列,再批量出队。  
# 虽然这里只有一个Reader,但可以设置多线程,
# 相应增加线程数会提高读取速度,但并不是线程越多越好。  
# 在这里体现多个样本:batch
example_batch, label_batch = tf.train.batch(  
      [example, label], batch_size=5)  
with tf.Session() as sess:  
    coord = tf.train.Coordinator()  
    threads = tf.train.start_queue_runners(coord=coord)  
    for i in range(10):  
        print example_batch.eval()  
    coord.request_stop()  
    coord.join(threads)  

3.2.3.多个Reader,多个样本

import tensorflow as tf  
filenames = ['A.csv', 'B.csv', 'C.csv']  
filename_queue = tf.train.string_input_producer(filenames, shuffle=False)  
reader = tf.TextLineReader()  
key, value = reader.read(filename_queue)  
# Reader设置为2。通过解码后样本列表内元素个数为2来体现。
# 因为一个Reader读取的是一个样本。
# 所以两个Reader对应的就是一个样本列表中的两个元素
# 每个样本列表的元素的格式为[example, label]
example_list = [tf.decode_csv(value, record_defaults = [['null'], ['null']]  )  
                  for _ in range(2)]
# 使用tf.train.batch_join(),可以使用多个reader,并行读取数据。
# 每个Reader使用一个线程。  
example_batch, label_batch = tf.train.batch_join(  
      example_list, batch_size=5)  
with tf.Session() as sess:  
    coord = tf.train.Coordinator()  
    threads = tf.train.start_queue_runners(coord=coord)  
    for i in range(10):  
        print example_batch.eval()  
    coord.request_stop()  
    coord.join(threads)

注意,这里定义的是操作的方式,并不是定义操作的过程。也就是说,虽然定义的是多个Reader进行读取,但是实际的读取过程是由Tensorflow完成的,并不是在计算图中定义的。

tf.train.batch与tf.train.shuffle_batch函数用于单个Reader,但是可以多线程读取。而tf.train.batch_join'和tf.train.shuffle_batch_join函数可以设置多个Reader读取,而每个Reader使用一个线程。如果处理器是双核的。关于两种方法的效率,单个Reader时,2个线程就达到了速度的极限。多Reader时,2个Reader就达到了极限。并不是线程越多越快,甚至更多的线程反而会使效率下降。

猜你喜欢

转载自blog.csdn.net/weixin_39721347/article/details/87372966