【问题标题】:Cannot batch tensors with different shapes in component 0 with tf.data.Dataset无法使用 tf.data.Dataset 在组件 0 中批处理具有不同形状的张量
【发布时间】:2018-03-28 09:47:56
【问题描述】:

我的输入管道中有以下错误:

tensorflow.python.framework.errors_impl.InvalidArgumentError: 不能 组件 0 中具有不同形状的批量张量。第一个元素有 形状为 [2,48,48,3],元素 1 的形状为 [27,48,48,3]。

使用此代码

dataset = tf.data.Dataset.from_generator(generator,
                                         (tf.float32, tf.int64, tf.int64, tf.float32, tf.int64, tf.float32))

dataset = dataset.batch(max_buffer_size)

这是完全合乎逻辑的,因为批处理方法试图创建一个 (batch_size, ?, 48, 48, 3) 张量。但是我希望它为这种情况创建一个 [29,48,48,3] 张量。所以连接而不是堆栈。 tf.data 可以吗?

我可以在生成器函数中用 Python 进行连接,但我想知道这是否也可以通过 tf.data 管道实现

【问题讨论】:

  • 所以一个实例(数据点)的形状是(48、48、3)?为什么您的生成器首先会产生大量实例?
  • 因为它们通过消息总线进入消息群。另一种方法确实是在生成器中产生 (48,48,3) 个实例。但是,我需要一种方法来使批量大小可变,因为我需要再次将实例块一起发送。
  • 我明白了。所以团块的大小是可变的,但是你想要连接的团块的数量是固定的?那么我可能有一个解决方案。我会尽快将其发布为答案。
  • 抱歉,我想的解决方案没有成功。
  • 是的,我想用更大的批次通过网络进行前向传递。但不要打破团块

标签: python tensorflow tensorflow-datasets


【解决方案1】:

第一种情况:我们希望输出具有固定的批量大小

在这种情况下,生成器生成形状为[None, 48, 48, 3] 的值,其中第一个维度可以是任何值。我们想要批量处理,以便输出为[batch_size, 48, 48, 3]。如果直接使用tf.data.Dataset.batch会报错,所以需要先unbatch

为此,我们可以在批处理之前像这样使用tf.contrib.data.unbatch

dataset = dataset.apply(tf.contrib.data.unbatch())
dataset = dataset.batch(batch_size)

这是一个完整示例,其中生成器生成 [1][2, 2][3, 3, 3][4, 4, 4, 4]

我们无法直接对这些输出值进行批处理,因此我们先取消批处理,然后再对其进行批处理:

def gen():
    for i in range(1, 5):
        yield [i] * i

# Create dataset from generator
# The output shape is variable: (None,)
dataset = tf.data.Dataset.from_generator(gen, tf.int64, tf.TensorShape([None]))

# The issue here is that we want to batch the data
dataset = dataset.apply(tf.contrib.data.unbatch())
dataset = dataset.batch(2)

# Create iterator from dataset
iterator = dataset.make_one_shot_iterator()
x = iterator.get_next()  # shape (None,)

sess = tf.Session()
for i in range(5):
    print(sess.run(x))

这将打印以下输出:

[1 2]
[2 3]
[3 3]
[4 4]
[4 4]

第二种情况:我们想要连接可变大小的批次

更新(2018 年 3 月 30 日):我删除了之前使用分片的答案,这会大大降低性能(请参阅 cmets)。

在这种情况下,我们想要连接固定数量的批次。问题是这些批次的大小可变。例如,数据集产生[1][2, 2],我们希望得到[1, 2, 2] 作为输出。

解决这个问题的一种快速方法是创建一个新的生成器来包裹原来的生成器。新的生成器将产生批处理数据。 (感谢Guillaume 的想法)


这是一个完整示例,其中生成器生成 [1][2, 2][3, 3, 3][4, 4, 4, 4]

def gen():
    for i in range(1, 5):
        yield [i] * i

def get_batch_gen(gen, batch_size=2):
    def batch_gen():
        buff = []
        for i, x in enumerate(gen()):
            if i % batch_size == 0 and buff:
                yield np.concatenate(buff, axis=0)
                buff = []
            buff += [x]

        if buff:
            yield np.concatenate(buff, axis=0)

    return batch_gen

# Create dataset from generator
batch_size = 2
dataset = tf.data.Dataset.from_generator(get_batch_gen(gen, batch_size),
                                         tf.int64, tf.TensorShape([None]))

# Create iterator from dataset
iterator = dataset.make_one_shot_iterator()
x = iterator.get_next()  # shape (None,)


with tf.Session() as sess:
    for i in range(2):
        print(sess.run(x))

这将打印以下输出:

[1 2 2]
[3 3 3 4 4 4 4]

【讨论】:

  • 感谢您的回答。关键是我不想将生成器中的初始团块分成多个批次。因此,如果有办法在这一行中制作 2 变量,这可能会起作用:dataset = dataset.batch(2)。对于这个例子,它应该是 1(批处理 [] 和 [1]),而不是 5(批处理 [2,2] 和 [3,3,3])等等。
  • 您总是想将它们 2 2 合并?
  • 谢谢,你知道分片对性能有什么影响吗?整个想法是获得更快的推理管道
  • @Derk:非常好的问题。我检查了实现,shard 将运行生成过程num_shards 次,这将非常慢......我不知道如何解决这个问题。
  • @Derk: 使用通过包装生成器起作用的解决方案更新了答案
【解决方案2】:

tensorflow 2 对我有用:

1)在创建数据集时设置repeat()函数,因此替换

data = tf.data.Dataset.from_tensor_slices(x)

通过

data = tf.data.Dataset.from_tensor_slices(x).repeat()

B) 将 step/epoch 参数传递给 fit 方法,因此替换

history = model.fit(dataset, epochs=EPOCHS, callbacks=[checkpoint_callback])

通过

history = model.fit(dataset, epochs=EPOCHS, steps_per_epoch=data[0]/BUFFER_SIZE, callbacks=[checkpoint_callback])

【讨论】:

    猜你喜欢
    • 2021-01-06
    • 2019-06-08
    • 1970-01-01
    • 1970-01-01
    • 2020-11-18
    • 2018-08-03
    • 1970-01-01
    • 1970-01-01
    • 2018-04-24
    相关资源
    最近更新 更多