无法使用 tf.data.Dataset 对组件 0 中具有不同形状的张量进行批处理

Cannot batch tensors with different shapes in component 0 with tf.data.Dataset

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

tensorflow.python.framework.errors_impl.InvalidArgumentError: Cannot batch tensors with different shapes in component 0. First element had shape [2,48,48,3] and element 1 had shape [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 管道是否也可以这样做

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

在这种情况下,生成器生成形状 [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]

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

更新 (03/30/2018): 我删除了之前使用分片的答案,这会大大降低性能(见评论)。

在这种情况下,我们想要连接固定数量的批次。问题是这些批次的大小可变。例如,数据集产生 [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]

tensorflow 2 对我有用的地方:

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

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

来自

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

B) 将 steps/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])