标签: tensorflowtf.train.batchtf.train.batch_join |
分类: 深度学习 |
先看两个函数的官方文档说明
tf.train.batch官方文档地址:
https://www.tensorflow.org/api_docs/python/tf/train/batch
tf.train.batch_join官方文档地址:
https://www.tensorflow.org/api_docs/python/tf/train/batch_join
tf.train.batch
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里的张量的batch。
参数tensors可以是一个张量的列表或者字典。函数的返回值将会和参数tensors的类型一致。
这个函数使用队列来实现。一个用于队列的QueueRunner对象被添加当前图的QUEUE_RUNNER的集合中。
如果enqueue_many设置为False,tensors被认为代表单个样本。那么输入维度(shape)为[x,y,z]的张量,将会输出一个维度为[batch_size,x,y,z]的张量。
如果enqueue_many设置为True,参数tensors被认为是一批次的样本,其中第一维是按样本编索引的(where the first dimension is indexed byexample,这里翻译出来意思有点奇怪),并且tensors中所有成员都应该在第一维上有相同的大小。如果输入的张量的维度是[*,x,y,z],那么输出的张量的维度将会是[batch_size,x,y,z]。参数capacity控制着增长队列时,预先分配的队列长度。
返回的操作是一个出队操作,如果输入的队列被耗尽,那么这个操作会抛出tf.errors.OutOfRangeError的异常。如果这个操作供给了另一个输入队列,那么它的队列运行器(queue runner)会捕捉这个异常,但是如果这个操作在你的主线程中使用,你需要自己捕捉这个异常。
注意:如果dynamic_pad参数为False,你应该确保(1)shapes参数被传递进来,(2)所有在tensors参数中的张量必须有完整定义的形状。如果上述任意一个条件没有满足,那么会抛出ValueError的异常。
如果dynamic_pad参数设置为True,那么知道tensors的秩就足够了,但是tensors的个别维度的形状可能为空(None)。这种情况下,每个有维度值为空的队列可能有一个可变的长度;在出队的时候,输出的张量将在左边填充当前最小批次中的张量的最大形状。对于数字,填充的是数值0.对于字符串,填充的是空字符串。参见PaddingFIFOQueue获得更多信息。
如果allow_smaller_final_batch参数为True,当队列被关闭并且没有足够的元素来填满一批次的数据的情况下,操作将会返回一个数量小于batch_size个数的批次数据,否则待处理的数据将被丢弃。除此之外,通过get_shape方法访问的所有输出的张量的静态形状的第一维将为空,依赖于固定为batch_size大小的操作将会失败。
参数:
返回:
有着与tensors参数相同类型的张量的列表或字典(除了如果输入的是一个元素的列表,那么返回的是一个张量,而不是列表)
抛出:
tf.train.batch_join
batch_join( tensors_list, batch_size, capacity=32, enqueue_many=False, shapes=None, dynamic_pad=False, allow_smaller_final_batch=False, shared_name=None, name=None )
运行张量列表来填充队列,以创建样本的批次。
tensors_list参数是一个张量元组的列表,或者张量字典的列表。在列表中的每个元素被类似于tf.train.batch()函数中的tensors一样对待。
警告:这个函数是非确定性的,因为它为每个张量启动了独立的线程。
在不同的线程中入队不同的张量列表。用队列实现——队列的QueueRunner被添加到当前图的QUEUE_RUNNER集合中。
len(tensors_list)个线程被启动,第i个线程入队来自tensors_list[i]中的张量。tensors_list[i1][j]比如在类型和形状上与tensors_list[i2][j]相匹配,除了当enqueue_many参数为True的时候的第一维。
如果enqueue_many设置为False,那么每个tensors_list[i]被认为是单个样本。一个输入的张量x将被输出为形状为[batch_size] + x.shape的张量。
如果dequeue_many设置为True,那么tensors_list[i]被认为代表一个批次的样本,其中第一维是按样本编索引的,并且tensors_list[i]的所有成员都应该在第一维上有相同的大小。任何输入的张量x的切片都被当做样本,并且输出的张量的形状将是[batch_size]+x.shape[1:]。
capacity参数控制着当增长队列的时候,预分配队列的长度。
函数返回的操作是一个出队操作,并且会再输入的队列耗尽的情况下,抛出tf.errors.OutOfRangeError的异常。如果这个操作供给给其他输入队列,那么那个队列的队列运行器会捕捉这个异常,但是如果这个操作在你的主线程中操作,你需要自己负责捕捉这个异常。
注意:如果dynamic_pad参数为False,你应该确保(1)shapes参数被传递进来,(2)所有在tensors参数中的张量必须有完整定义的形状。如果上述任意一个条件没有满足,那么会抛出ValueError的异常。
如果dynamic_pad参数设置为True,那么知道tensors的秩就足够了,但是tensors的个别维度的形状可能为空(None)。这种情况下,每个有维度值为空的队列可能有一个可变的长度;在出队的时候,输出的张量将在左边填充当前最小批次中的张量的最大形状。对于数字,填充的是数值0.对于字符串,填充的是空字符串。参见PaddingFIFOQueue获得更多信息。
如果allow_smaller_final_batch参数为True,当队列被关闭并且没有足够的元素来填满一批次的数据的情况下,操作将会返回一个数量小于batch_size个数的批次数据,否则待处理的数据将被丢弃。除此之外,通过get_shape方法访问的所有输出的张量的静态形状的第一维将为空,依赖于固定为batch_size大小的操作将会失败。
参数:
返回:
与tensors_list[i]有着相同数量和类型的张量的列表或者字典。
抛出:
看完上述官方文档的说明,其实还是挺蒙的,下面给一段例子,就很容易理解这两个函数了,例子的代码如下:
# -*- coding: utf-8 -*-
import tensorflow as tf
tensor_list = [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16],[17,18,19,20]]
tensor_list2 = [[[1,2,3,4]],[[5,6,7,8]],[[9,10,11,12]],[[13,14,15,16]],[[17,18,19,20]]]
with tf.Session() as sess:
x1 =tf.train.batch(tensor_list, batch_size=4,enqueue_many=False)
x2 =tf.train.batch(tensor_list, batch_size=4,enqueue_many=True)
y1 =tf.train.batch_join(tensor_list, batch_size=4,enqueue_many=False)
y2 =tf.train.batch_join(tensor_list2, batch_size=4,enqueue_many=True)
coord = tf.train.Coordinator()
threads = tf.train.start_queue_runners(sess=sess,coord=coord)
print("x1 batch:"+"-"*10)
print(sess.run(x1))
print("x2 batch:"+"-"*10)
print(sess.run(x2))
print("y1 batch:"+"-"*10)
print(sess.run(y1))
print("y2 batch:"+"-"*10)
print(sess.run(y2))
print("-"*10)
coord.request_stop()
coord.join(threads)
上述例子的输出类似如下这样:
x1batch:----------
[array([[1, 2, 3, 4],
[1, 2, 3, 4],
[1, 2, 3, 4],
[1, 2, 3, 4]]), array([[5, 6, 7, 8],
[5, 6, 7, 8],
[5, 6, 7, 8],
[5, 6, 7, 8]]), array([[ 9, 10, 11, 12],
[ 9, 10, 11, 12],
[ 9, 10, 11, 12],
[ 9, 10, 11, 12]]), array([[13, 14, 15, 16],
[13, 14, 15, 16],
[13, 14, 15, 16],
[13, 14, 15, 16]]), array([[17, 18, 19, 20],
[17, 18, 19, 20],
[17, 18, 19, 20],
[17, 18, 19, 20]])]
x2batch:----------
[array([1,2, 3, 4]), array([5, 6, 7, 8]), array([ 9, 10, 11, 12]), array([13,1
4, 15,16]), array([17, 18, 19, 20])]
y1batch:----------
[array([1, 9, 17, 9]), array([ 2, 10,18, 10]), array([ 3, 11, 19, 11]), arra
y([ 4, 12,20, 12])]
y2batch:----------
[5 6 78]
----------
在enqueue_many参数设置为False(默认值)的时候,tf.train.batch的输出,是batch_size*tensor.shape,其含义就是将tensors参数看做一个样本,那么batch_size个样本,只是单一样本的复制。在其实际应用中,tensors参数一般对应的是一个文件,那么这样操作意味着从文件中读取batch_size次, 以获得一个batch的样本。
而在enqueu_many参数设置为True的时候,tf.train.batch将tensors参数看做一个batch的样本,那么batch_size只是调整一个batch中样本的维度的,因为输出的维度是batch_size*tensor.shape[1:](可以尝试将代码例子中batch_size改成3,再看结果)。
最后需要注意的tf.train.batch的num_threads参数,指定的是进行入队操作的线程数量,可以进行多线程对于同一个文件进行操作,这样会比单一线程读取文件快。
tf.train.batch_join一般就对应于多个文件的多线程读取,可以看到当enqueue_many参数设置为False(默认值)的时候,tensor_list中每个tensor被看做单个样本,这个时候组成batch_size的一个batch的样本,是从各个单一样本中凑成一个batch_size的样本。可以看到由于是多线程,每次取值不同,也就是类似,每个tensor对应一个文件,也对应一个线程,那么组成batch的时候,该线程读取到文件(例子中是tensor的哪个值)是不确定,这样就形成了打乱的形成样本的时候。
而在enqueue_many参数设置为True的时候,取一个batch的数据,是在tensor_list中随机取一个,因为每一个就是一个batch的数据,batch_size只是觉得截断或填充这个batch的大小。
这样就很容易明白了tf.train.batch和tf.train.batch_join的区别,一般来说,单一文件多线程,那么选用tf.train.batch(需要打乱样本,有对应的tf.train.shuffle_batch);而对于多线程多文件的情况,一般选用tf.train.batch_join来获取样本(打乱样本同样也有对应的tf.train.shuffle_batch_join使用)。