TensorFlow. ——数据读取方式

  • tensortlfow数据读取有三种方式:
  1. placehold feed_dict:从内存中读取数据,占位符填充数据,通常采用用python本地读取文件的方式,这种读取方式是将数据写入内存之后再计算,效率比较低,不建议使用。
  2. Dataset:同时支持内存和硬盘读取数据,适用于小数据。
  3. queue队列:从硬盘读取数据,定义好读取的运算节点,一边读取一边运算。
placehold feed_dict:这是一种常见的方法,在这里不多作讨论,如果一定要使用,尽量在tensorflow里对数据进行预处理会提高效率。
tf.data.Dataset:官方推荐,用tf.data.Dataset读取数据,再用tf内部函数处理数据。

Dataset是创造input pipeline的最佳实践;

  • 第一步:tf.data.Dataset.from_tensor_slices,最好放入文件名列表。
  • 第二步:shuffle:打乱
  • 第三步:map:遍历每一个进行预处理
  • 第四步:batch:得到batch
  • 第五步:得到iterator,batch,Iterator 它提供了主要的方式来从一个dataset中抽取元素。通过Iterator.get_next() 返回的该操作会yields出Datasets中的下一个元素,作为输入pipeline和模型间的接口使用。
# filename是图片的文件名,label是图片对应的标签
dataset = tf.data.Dataset.from_tensor_slices((filenames, labels))
dataset.shuffle(buffer_size=1000) # 如果数据太多,很难完全打乱
dataset = dataset.map(preprocess)
dataset = dataset.batch(batch_size=1)
iterator = tf.compat.v1.data.make_initializable_iterator(dataset)
batch = iterator.get_next()
  • tf.data.TextLineDataset():这个函数的输入是一个文件的列表,输出是一个dataset。dataset中的每一个元素就对应了文件中的一行。可以使用这个函数来读入CSV文件。
  • tf.data.FixedLengthRecordDataset():这个函数的输入是一个文件的列表和一个record_bytes,之后dataset的每一个元素就是文件中固定字节数record_bytes的内容。通常用来读取以二进制形式保存的文件,如CIFAR10数据集就是这种形式。
  • tf.data.TFRecordDataset():顾名思义,这个函数是用来读TFRecord文件的,dataset中的每一个元素就是一个TFExample。

注意:Cannot create a tensor proto whose content is larger than 2GB.
从结果很明显可以看出,是一次放入tensor的张量不能超过2G,可是实际中有很多数据集是超过2GB的,所以要么进行一个切分操作:目的是实现将超过2GB的切分到每个小块不超过2G,然后再一个一个处理就行了。最好的方式还是放入文件名,再遍历读取。

queue队列:如果我们的数据读取算法没有设计多线程的话(即单线程),由于读取数据和处理数据在同一个进程是有先后关系的,意味着数据处理完后必须花时间读取数据,然后才能进行计算处理。这样的一来GPU并没有高效的专一做一件事情,从而大大的降低的效率,queue创建多线程彻底的解决了这个问题。

TensorFlow的Session对象是支持多线程的,可以在同一个会话(Session)中创建多个线程,并行执行。为了充分的利用时间,减少GPU等待的空闲时间,使用了两个线程(文件名队列和内存队列)分别执行数据读入和数据计算。文件名队列源源不断的将硬盘中的图片数据,内存队列负责给GPU送数据,所需数据直接从内存队列中获取。两个线程之间互不干扰,同时运行。

tf.Coordinator和 tf.QueueRunner,这两个类往往一起使用。

如何在tensorflow中使用自己的数据 tensorflow 数据读取_读取数据

  • tf.train.slice_input_producer():创建一个文件名队列tensor list,文件名队列存放的是参与训练的文件名,要训练N个epoch,则文件名队列中就含有N个批次的所有文件名。然后再对文件进行读取。
# slice_input_producer 可以生成多维队列(Expected list)
# string_input_producer会产生一个文件名队列(Expected string and list)
filename_queue = tf.train.string_input_producer(filename, shuffle=False, num_epochs=1)
fn_que, fn1_que, lb_que, lb1_que = tf.train.string_input_producer([filename,filename1,label1,label2], shuffle=False, num_epochs=1)
  • tf.WholeFileReader():图片读取器,批量读取,key表示文件名,value表示读取的内容。注意!!!一定要搭配string_input_producer(),通过文件名批量读取图片,读取后再进行解码,resize,reshape等操作。
# reader从文件名队列中读数据。对应的方法是reader.read
 reader = tf.WholeFileReader()
 key, value = reader.read(queue)
 # 解码	
 image = tf.image.decode_jpeg(value)
 # resize
 image_resize = tf.image.resize_images(image, [200, 200])
 # reshape
 image_resize.set_shape([200, 200, 3])
  • tf.train.batch & tf.train.shuffle_batch():作用是按照给定的tensor顺序,把batch_size个tensor推送到文件队列,作为训练一个batch的数据,等待tensor出队执行计算。
# input_queue可以由解压后的图片和标签构成,输入要是tensor.
fn_batch, fn1_batch, lb_batch, lb2_batch = tf.train.batch([fn_image,fn1_image, lb_que, lb1_que],batch_size=64,num_threads=1, capacity=64)
  • tf.train.Coordinator():创建一个线程管理器(协调器)对象。可以用来同时停止多个工作线程并且向那个在等待所有工作线程终止的程序报告异常,该线程捕获到这个异常之后就会终止所有线程。
  • tf.train.start_queue_runners:启动执行文件名队列填充的线程,由多个或单个线程,之后计算单元才可以把数据读出来,否则文件名队列为空的,计算单元就会处于一直等待状态,导致系统阻塞。
with tf.Session() as sess:
    # 先执行初始化工作
    sess.run(tf.global_variables_initializer())
    sess.run(tf.local_variables_initializer()) # 必要!!
    # 开启一个协调器
    coord = tf.train.Coordinator()
    # 使用start_queue_runners 启动队列填充
    threads = tf.train.start_queue_runners(sess, coord)
    try:
        while not coord.should_stop():
            print('************')
    except tf.errors.OutOfRangeError:  # 如果读取到文件队列末尾会抛出此异常
        print("done! now lets kill all the threads……")
    finally:
        # 协调器coord发出所有线程终止信号
        coord.request_stop() # all threads are asked to stop!
    coord.join(threads)  # 把开启的线程加入主线程,等待threads结束