TensorFlow的数据读入方式(二)

相信大家大部分还在使用tf,placeholder来进行数据的读入,虽然这种方法很直观,但是效率比较低。

事实上TensorFlow有三种数据读入的方式,在我们的不断的学习中我们应该不断的升级我们的认知,将学习的进度从直观、方便转入高效的代码编辑。

Tensorflow中之前主要用的数据读取方式主要有:

placehold feed_dict:从内存中读取数据,占位符填充数据
queue队列:从硬盘读取数据
Dataset:同时支持内存和硬盘读取数据

1、建立placeholder,然后使用feed_dict将数据feed进placeholder进行使用。使用这种方法十分灵活,可以一下子将所有数据读入内存,然后分batch进行feed;也可以建立一个Python的generator,一个batch一个batch的将数据读入,并将其feed进placeholder。这种方法很直观,用起来也比较方便灵活jian,但是这种方法的效率较低,难以满足高速计算的需求。

2、使用TensorFlow的QueueRunner,通过一系列的Tensor操作,将磁盘上的数据分批次读入并送入模型进行使用。这种方法效率很高,但因为其牵涉到Tensor操作,不够直观,也不方便调试,所有有时候会显得比较困难。使用这种方法时,常用的一些操作包括tf.TextLineReader,tf.FixedLengthRecordReader以及tf.decode_raw等等。如果需要循环,条件操作,还需要使用TensorFlow的tf.while_loop,tf.case等操作。

3、上面的方法我觉得已经要被tensorflow放弃了,现在官方推荐用tf.data.Dataset模块,使其数据读入的操作变得更为方便,而支持多线程(进程)的操作,也在效率上获得了一定程度的提高。

1、第一种方法,tf.placeholder()+feed_dict

原理:

先用placehold 占位数据,在Graph中读取数据,数据直接内嵌到Graph中,然后当Graph传入Session是,用feed_dict喂补数据。当数据量比较大的时候,Graph的传输会遇到效率底下问题,特别是数据转换。

基本格式:

x =  tf.placeholder(shape=[], name='', dtype=tf.float32)
x =  tf.placeholder(shape=[], name='', dtype=tf.int32)

train_data , label_data= load_data(imgs, labels, batch_size)

sess.run(train_op,feed_dict={x:train_data, y:label_data}
2、第二种方法,Queen队列

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

tensorflow中为了充分的利用时间,减少GPU等待的空闲时间,使用了两个线程(文件名队列和内存队列)分别执行数据读入和数据计算。文件名队列源源不断的将硬盘中的图片数据,内存队列负责给GPU送数据,所需数据直接从内存队列中获取。两个线程之间互不干扰,同时运行。
  
因此 tensorflow 在内存队列之前,还要使用tf.train.slice_input_producer函数,创建一个文件名队列,文件名队列存放的是参与训练的文件名,要训练N个epoch,则文件名队列中就含有N个批次的所有文件名

# 主要代码
# 使用到 tf.train.slice_input_producer 函数创建文件名队列。在N个epoch的文件名最后是一个结束标志,当tf读到这个结束标志的时候,会抛出一个OutofRange 的异常,外部捕获到这个异常之后就可以结束程序了。
input_queue = tf.train.slice_input_producer([images, labels], epoch_nums=None, shuffle=True)

with tf.Session() as sess:
	# 创建一个线程 
    coord = tf.train.Coordinator()
    # 开启队列
    threads = tf.train.start_queue_runners(sess, coord)
    try:
   	    # tf.train.batch是一个tensor队列生成器,作用是按照给定的tensor顺序,把batch_size个tensor推送到文件队列,作为训练一个batch的数据,等待tensor出队执行计算
        imgs_data, labels_data = tf.train.batch((imgs, labels), batch_size=batch_size,num_threads=1, capacity=64)
    finally:
    coord.request_stop()
    coord.join(threads)
第三种方法、tf.data.Dataset()

第三种方法的核心函数为tf.data.Dataset.from_tensor_slices()

对Dataset中的元素做变换

Dataset支持一类特殊的操作:Transformation。一个Dataset通过Transformation变成一个新的Dataset。通常我们可以通过Transformation完成数据变换,打乱,组成batch,生成epoch等一系列操作。

常用的Transformation有:

Map
batch
shuffle
repeat

1 .map
map接收一个函数,Dataset中的每个元素都会被当作这个函数的输入,并将函数返回值作为新的Dataset,如我们可以对dataset中每个元素的值加1:

dataset = tf.data.Dataset.from_tensor_slices(np.array([1.0, 2.0, 3.0, 4.0, 5.0]))
dataset = dataset.map(lambda x: x + 1) # 2.0, 3.0, 4.0, 5.0, 6.0

2.batch
batch就是将多个元素组合成batch,如下面的程序将dataset中的每个元素组成了大小为32的batch:

dataset = dataset.batch(32)

3.shuffle
shuffle的功能为打乱dataset中的元素,它有一个参数buffersize,表示打乱时使用的buffer的大小:

dataset = dataset.shuffle(buffer_size=10000)

4.repeat
repeat的功能就是将整个序列重复多次,主要用来处理机器学习中的epoch,假设原先的数据是一个epoch,使用repeat(5)就可以将之变成5个epoch:

dataset = dataset.repeat(5)

如果直接调用repeat()的话,生成的序列就会无限重复下去,没有结束,因此也不会抛出tf.errors.OutOfRangeError异常:dataset = dataset.repeat()

# 函数的功能时将filename对应的图片文件读进来,并缩放到统一的大小
def _parse_function(filename, label):
  image_string = tf.read_file(filename)
  image_decoded = tf.image.decode_image(image_string)
  image_resized = tf.image.resize_images(image_decoded, [28, 28])
  return image_resized, label

# 图片文件的列表
filenames = tf.constant(["/var/data/image1.jpg", "/var/data/image2.jpg", ...])
# label[i]就是图片filenames[i]的label
labels = tf.constant([0, 37, ...])

# 此时dataset中的一个元素是(filename, label)
dataset = tf.data.Dataset.from_tensor_slices((filenames, labels))

# 此时dataset中的一个元素是(image_resized, label)
dataset = dataset.map(_parse_function)

# 此时dataset中的一个元素是(image_resized_batch, label_batch)
dataset = dataset.shuffle(buffersize=1000).batch(32).repeat(10)

优秀的文章:

tf.data.Dataset的使用:https://blog.csdn.net/ssmixi/article/details/80572813

TensorFlow读取数据的三种方法:https://www.cnblogs.com/LXP-Never/p/11460000.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值