Невозможно группировать тензоры с разными формами в компоненте 0 с tf.data.Dataset

В моем конвейере ввода есть следующая ошибка:

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


person Derk    schedule 28.03.2018    source источник
comment
Итак, один экземпляр (точка данных) имеет форму (48, 48, 3)? Почему ваш генератор вообще выдает группы экземпляров?   -  person lballes    schedule 28.03.2018
comment
Потому что они входят через шину сообщений в группы сообщений. Другой способ - создать (48,48,3) экземпляров в генераторе. Однако мне нужен способ сделать переменную размера пакета, потому что мне нужно снова отправить группы экземпляров вместе.   -  person Derk    schedule 28.03.2018
comment
Понятно. Значит, сгустки имеют переменный размер, но количество сгустков, которые вы хотите объединить, фиксировано? Тогда у меня может быть решение. Я скоро отправлю это как ответ.   -  person lballes    schedule 28.03.2018
comment
Извините, решение, которое я придумал, не сработало.   -  person lballes    schedule 28.03.2018
comment
Да, я хочу сделать прямой проход через сеть с большим пакетом. Но не разбивай сгустки   -  person Derk    schedule 28.03.2018


Ответы (2)


Первый случай: мы хотим, чтобы результат имел фиксированный размер партии

В этом случае генератор генерирует значения формы [None, 48, 48, 3], где первое измерение может быть любым. Мы хотим сгруппировать это так, чтобы на выходе было [batch_size, 48, 48, 3]. Если мы используем напрямую tf.data.Dataset.batch, у нас будет ошибка, поэтому сначала нам нужно разблокировать.

Для этого мы можем использовать 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]

Второй случай: мы хотим объединить партии переменного размера

Обновление (30.03.2018): я удалил предыдущий ответ, в котором использовалось сегментирование, которое значительно снижает производительность (см. комментарии).

В этом случае мы хотим объединить фиксированное количество пакетов. Проблема в том, что эти партии имеют переменные размеры. Например, набор данных дает [1] и [2, 2], и мы хотим получить [1, 2, 2] в качестве вывода.

Вот быстрый способ решить эту проблему - создать новый генератор, обернутый вокруг исходного. Новый генератор будет выдавать пакетные данные. (Спасибо Гийому за идею)


Вот полный пример, где генератор дает [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]
person Olivier Moindrot    schedule 28.03.2018
comment
Спасибо за Ваш ответ. Дело в том, что я не хочу разделять исходные сгустки из генератора на несколько партий. Таким образом, это может сработать, если есть способ сделать 2 переменной в этой строке: dataset = dataset.batch (2). В этом примере это должно быть 1 (для партии [] и [1]), затем 5 (для партии [2,2] и [3,3,3]) и т. Д. - person Derk; 28.03.2018
comment
Вы всегда хотите объединить их 2 на 2? - person Olivier Moindrot; 28.03.2018
comment
Да, это значение будет фиксированным (будет 10) - person Derk; 28.03.2018
comment
@Derk: я добавил этот второй случай с решением, использующим сегментирование, zip и конкатенацию :) - person Olivier Moindrot; 28.03.2018
comment
Спасибо, вы знаете, как влияет шардинг на производительность? Вся идея состоит в том, чтобы получить более быстрый конвейер вывода - person Derk; 29.03.2018
comment
@Derk: очень хороший вопрос. Я проверил реализацию, и сегмент запустит процесс генерации num_shards раз, что будет очень медленно ... Тогда я не знаю, как это исправить. - person Olivier Moindrot; 29.03.2018
comment
@Derk: обновил ответ решением, которое работает путем упаковки генератора - person Olivier Moindrot; 30.03.2018
comment
Еще одна ошибка, ValueError: Cannot unbatch an input with scalar components. - person rosefun; 13.05.2021

Что сработало для меня с tenorflow 2:

1) установка функции repeat () при создании набора данных, поэтому замена

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

by

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

B) Передача параметра steps / epoch методу подгонки с заменой

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

by

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