【tensorflow】tf.train.slice_input_producer 和 tf.train.batch 函数

转载至:https://blog.csdn.net/dcrmg/article/details/79776876

计算机如何读取图像?

读取数据的过程可以用下图来表示:

图片描述

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

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

图片描述

tensorflow数据读取机制

在TensorFlow中,为了方便管理,在内存队列前又添加了一层所谓的“文件名队列”。tf在内存队列之前,还设立了一个文件名队列,文件名队列存放的是参与训练的文件名,示例图如下,要训练 N个epoch,则文件名队列中就含有N个批次的所有文件名:

tf.train.slice_input_producer函数

我们使用tf.train.string_input_producer函数创建文件名队列。这个函数需要传入一个文件名list,系统会自动将它转为一个文件名队列。tf.train.string_input_producer还有两个重要的参数,一个是num_epochs,表示epoch数。另外一个就是shuffle是指在一个epoch内文件的顺序是否被打乱。

def slice_input_producer(tensor_list, num_epochs=None, shuffle=True, seed=None,
                         capacity=32, shared_name=None, name=None)
  • 第一个参数 tensor_list:包含一系列tensor的列表,表中tensor的第一维度的值必须相等,即个数必须相等,有多少个图像,就应该有多少个对应的标签。
  • 第二个参数num_epochs: 可选参数,是一个整数值,代表迭代的次数,如果设置 num_epochs=None,生成器可以无限次遍历tensor列表,如果设置为 num_epochs=N,生成器只能遍历tensor列表N次。
  • 第三个参数shuffle: bool类型,设置是否打乱样本的顺序。一般情况下,如果shuffle=True,生成的样本顺序就被打乱了,在批处理的时候不需要再次打乱样本,使用 tf.train.batch函数就可以了;如果shuffle=False,就需要在批处理时候使用 tf.train.shuffle_batch函数打乱样本。
  • 第四个参数seed: 可选的整数,是生成随机数的种子,在第三个参数设置为shuffle=True的情况下才有用。
  • 第五个参数capacity:设置tensor列表的容量。
  • 第六个参数shared_name:可选参数,如果设置一个‘shared_name’,则在不同的上下文环境(Session)中可以通过这个名字共享生成的tensor。
  • 第七个参数name:可选,设置操作的名称。

tf.train.slice_input_producer定义了样本放入文件名队列的方式,包括迭代次数,是否乱序等,要真正将文件放入文件名队列,还需要调用tf.train.start_queue_runners 函数来启动执行文件名队列填充的线程,之后计算单元才可以把数据读出来,否则文件名队列为空的,计算单元就会处于一直等待状态,导致系统阻塞。

样例代码如下:

import tensorflow as tf
 
images = ['img1', 'img2', 'img3', 'img4', 'img5']
labels= [1,2,3,4,5]
 
epoch_num=8
 
f = tf.train.slice_input_producer([images, labels],num_epochs=None,shuffle=False)
 
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    coord = tf.train.Coordinator()
    threads = tf.train.start_queue_runners(sess=sess, coord=coord)
    for i in range(epoch_num):
        k = sess.run(f)
        print ('************************')
        print (i,k)
 
    coord.request_stop()
    coord.join(threads)

运行后的结果如下:

************************
(0, ['img1', 1])
************************
(1, ['img2', 2])
************************
(2, ['img3', 3])
************************
(3, ['img4', 4])
************************
(4, ['img5', 5])
************************
(5, ['img1', 1])
************************
(6, ['img2', 2])
************************
(7, ['img3', 3])

相信有小伙伴在学习tf.train.slice_input_producer函数的同时一定遇到了tf.train.string_input_producer函数,那么这两个有什么区别呢?

他们两者区别可以简单理解为:string_input_producer每次放出一个文件名。slice_input_producer可以既可以同时放出文件名和它对应的label,也可以只放出一个文件名。而在实际应用代码的时候也只是读取文件的方式不一样。

  • string_input_producer加载图片的reader是reader = tf.WholeFileReader() key,value = reader.read(path_queue)其中key是文件名,value是byte类型的文件流二进制,一般需要解码(decode)一下才能变成数组,然后进行reshape操作。
  • slice_input_producer加载图片的reader使用tf.read_file(filename)直接读取。记得图片需要解码和resize成数组,才可以放入内存队列file_queue中等待调用。

tf.train.batch函数

tf.train.batch是一个tensor队列生成器,作用是按照给定的tensor顺序,生成batch_size个数据(tensor)用于后面的训练,即一个batch

def batch(tensors, batch_size, num_threads=1, capacity=32,
          enqueue_many=False, shapes=None, dynamic_pad=False,
          allow_smaller_final_batch=False, shared_name=None, name=None)
  • 第一个参数tensors:tensor序列或tensor字典,可以是含有单个样本的序列;
  • 第二个参数batch_size: 生成的batch的大小;
  • 第三个参数num_threads:执行tensor入队操作的线程数量,可以设置使用多个线程同时并行执行,提高运行效率,但也不是数量越多越好;
  • 第四个参数capacity: 定义生成的tensor序列的最大容量;
  • 第五个参数enqueue_many: 定义第一个传入参数tensors是多个tensor组成的序列,还是单个tensor;
  • 第六个参数shapes: 可选参数,默认是推测出的传入的tensor的形状;
  • 第七个参数dynamic_pad: 定义是否允许输入的tensors具有不同的形状,设置为True,会把输入的具有不同形状的tensor归一化到相同的形状;
  • 第八个参数allow_smaller_final_batch: 设置为True,表示在tensor队列中剩下的tensor数量不够一个batch_size的情况下,允许最后一个batch的数量少于batch_size, 设置为False,则不管什么情况下,生成的batch都拥有batch_size个样本;
  • 第九个参数shared_name: 可选参数,设置生成的tensor序列在不同的Session中的共享名称;
  • 第十个参数name: 操作的名称;

下面给出一个更简单的例子:

import numpy as np
import tensorflow as tf
 
def next_batch():
    datasets =  np.asarray(range(0,20))
    input_queue = tf.train.slice_input_producer([datasets],shuffle=False,num_epochs=2)
    data_batchs = tf.train.batch(input_queue,batch_size=5,num_threads=1,
                                             capacity=20,allow_smaller_final_batch=False)
    return data_batchs
 
if __name__ == "__main__":
    data_batchs = next_batch()
    sess = tf.Session()
    sess.run(tf.initialize_local_variables())
    coord = tf.train.Coordinator()
    threads = tf.train.start_queue_runners(sess,coord)
    try:
        while not coord.should_stop():
            data = sess.run([data_batchs])
            print(data)
    except tf.errors.OutOfRangeError:
        print("complete")
    finally:
        coord.request_stop()
    coord.join(threads)
    sess.close()

有0-19共20个数,batch_size=5,epoch=2。运行结果如下:

但是当有21个数(0-20)时,如果仍设置allow_smaller_final_batch=False,则会发现最后一个19没有出现。原因在于那个batch只有19一个元素,而batch_size=5,不够,因此就忽略掉了。

进一步地,设置allow_smaller_final_batch = True,则有:

之后,给出tensorflow读取数据生成batch的一段简单的代码例子,其中所有的图片放置在imgdir中,为了方便运行我只放置了10张样本,batch_size设置为3,则共有4个batch。tf.train.slice_input_producer中shuttle参数采用False,即按照顺序出队,更方便于我们认识整个过程。num_epochs=None,生成器可以无限次遍历tensor列表。

import tensorflow as tf
import numpy as np  
import os
import matplotlib.pyplot as plt


def show_image(image):
    #显示单张图片
    #如果读取的是灰度图,那么此时image = (64,64,1),需要改变一下维度
    image = np.transpose(image,(2,0,1))  #修改维度之后变为(1,64,64)
    plt.figure()
    plt.imshow(image[0],cmap='Greys_r')    #之后取image的第一维度,即(64,64)显示
    #如果读取的彩色图,此时image = (64,64,3),直接显示即可
    #plt.imshow(image)                                
    plt.show()

def show_batch_image(batch_image):
    #显示一个batch的图片
    for i in range(batch_size):
        image = batch_image[i]
        mage = image.astype(np.uint8) #转换为uint8的格式
        show_image(image)

imgdir = 'E:/testdata/facades/lala'
image_list = GetFileFromThisRootDir(imgdir)           
image_W = 64
image_H = 64
sample_num = 10
batch_size = 3
epoch_num = 3  #迭代的次数
batch_total = int(sample_num/batch_size)+1  #每一个epoch都要按照batch_size的大小将sample_num构成batch,这个就是batch的数目


input_queue = tf.train.slice_input_producer([image_list],shuffle = False)  #生成文件名队列,由于shuffle = True,随机产生一个文件名路径
image_contents = tf.read_file(input_queue[0])               #input_queue[0]是样本的路径,根据该路径读取样本(处于编码下)
image = tf.image.decode_jpeg(image_contents, channels=1)    #进行解码,由于读取的为灰度图,channels=1
#crop_image = tf.image.resize_image_with_crop_or_pad(image, image_W, image_H) #采用切割的方式改变大小
crop_image = tf.image.resize_images(image, [image_W,image_H],method=0)  #采用放缩的方式改变大小,method=0为双线性插值
standard_image = tf.image.per_image_standardization(crop_image)   # 标准化数据
image_batch = tf.train.batch([crop_image], batch_size=batch_size, num_threads=2, capacity=64, allow_smaller_final_batch=False) #生成batch
 
with tf.Session() as sess:
    coord = tf.train.Coordinator()
    threads = tf.train.start_queue_runners(sess, coord)
    try:
        for i in range(epoch_num):  # 每一轮迭代
            print ('epoch is %d'%(i+1))
            for j in range(batch_total): #每一个batch
                #print ('batch num is %d'%(j+1))
                print ('input_queue:%s'%(sess.run(input_queue)))     
                #print ('image_contents:%s'%(sess.run(image_contents)))    
                #picture, crop_picture,standard_picture = sess.run([image,crop_image,standard_image])
                #show_image(picture)
                #show_image(crop_picture)
                #show_image(standard_picture)
                picture_batch = sess.run(image_batch)
                print(picture_batch.shape)
                show_batch_image(picture_batch )
    except tf.errors.OutOfRangeError:  #如果读取到文件队列末尾会抛出此异常
        print("done! now lets kill all the threads……")
    finally:
        # 协调器coord发出所有线程终止信号
        coord.request_stop()
        print('all threads are asked to stop!')
    coord.join(threads) #把开启的线程加入主线程,等待

我的10张样本图如下

运行上段代码后,4个batch显示的图片依次为: 

可以看出,从上至下,从左至右是按照顺序。经过3个batch后由于只剩一下一张样本没有出队,因而会在按顺序从第一张图补进。

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值