В вашем коде есть два разных набора проблем, которые могут быть классифицированы как синтаксические и архитектурные проблемы. Возникшая ошибка (т. Е. No gradients provided for any variable
) связана с синтаксическими проблемами, которые я в основном рассмотрю ниже, но я постараюсь дать вам несколько советов об архитектурных проблемах и после этого.
Основная причина Синтаксические проблемы - использование именованных входов и выходов для модели. Именованные входы и выходы в Keras в основном полезны, когда модель имеет несколько входных и / или выходных слоев. Однако ваша модель имеет только один входной и один выходной слой. Поэтому здесь может быть не очень полезно использовать именованные входы и выходы, но если это ваше решение, я объясню, как это можно сделать правильно.
Прежде всего, вы должны иметь в виду, что при использовании Keras В моделях данные, генерируемые из любого входного конвейера (будь то генератор Python или tf.data.Dataset
), должны быть представлены в виде кортежа, т.е. (input_batch, output_batch)
или (input_batch, output_batch, sample_weights)
. И, как я уже сказал, это ожидаемый формат везде в Keras при работе с входными конвейерами, даже когда мы используем именованные входы и выходы в качестве словарей.
Например, если я хочу использовать именование входов / выходов, и в моей модели есть два входных слоя с именами «слова» и «важность», а также два выходных слоя с именами «output1» и «output2», они должны быть отформатированы следующим образом:
({'words': words_data, 'importance': importance_data},
{'output1': output1_data, 'output2': output2_data})
Итак, как вы можете видеть выше, это кортеж, в котором каждый элемент кортежа является словарем; первый элемент соответствует входным данным модели, а второй элемент соответствует выходным данным модели. Теперь, согласно этому пункту, давайте посмотрим, какие изменения должны быть сделаны в вашем коде:
В sample_generator
мы должны вернуть набор слов, а не слова. Итак:
example = tuple([
{'inputs': text_encoder.encode(inputs) + [EOS_ID]},
{'targets': text_encoder.encode(targets) + [EOS_ID]},
])
В make_dataset
функции входные аргументы tf.data.Dataset
должны учитывать это:
output_types=(
{'inputs': tf.int64},
{'targets': tf.int64}
)
padded_shapes=(
{'inputs': (None,)},
{'targets': (None,)}
)
подпись prepare_example
и ее тело также должны быть изменены:
def prepare_example(ex_inputs: dict, ex_outputs: dict, params: dict):
# Make sure targets are one-hot encoded
ex_outputs['targets'] = tf.one_hot(ex_outputs['targets'], depth=params['vocab_size'])
return ex_inputs, ex_outputs
И, наконец, метод call
для подклассовой модели:
return {'targets': x}
И еще одна вещь: мы должны также поместить эти имена в соответствующие входные и выходные слои, используя аргумент name
при построении слоев (например, Dense(..., name='output')
; однако, поскольку мы используем Model
Подклассы здесь, чтобы определить нашу модель, в этом нет необходимости.
Хорошо, это решило бы проблемы ввода / вывода, и ошибка, связанная с градиентами, исчезла бы, однако если вы запустите код после применения вышеуказанных модификаций, вы все равно получите ошибку, касающуюся несовместимых фигур. Как я уже говорил ранее, в вашей модели есть архитектурные проблемы, которые я бы кратко рассмотрел ниже.
Как Вы упомянули, это предположительно Ред, чтобы быть последовательной моделью. Следовательно, на выходе получается последовательность кодированных векторов с горячим кодированием, где длина каждого вектора равна размеру словаря (целевых последовательностей). В результате классификатор softmax должен иметь столько же единиц, сколько и размер словарного запаса, например так (Примечание: никогда в любой модели или проблеме не используйте слой softmax только с одной единицей; это все неправильно! Подумайте, почему это неправильно!):
self.out_layer = keras.layers.Dense(params['vocab_size'], activation='softmax')
Следующее, что нужно учитывать, это то, что мы имеем дело с одномерными последовательностями (то есть последовательностью токенов / слов). Поэтому использование слоев 2D-свертки и 2D-пула здесь не имеет смысла. Вы можете использовать их одномерные аналоги или заменить их чем-то другим, например слоями RNN. В результате этого слой Lambda
также должен быть удален. Кроме того, если вы хотите использовать свертку и объединение в пул, вам следует правильно отрегулировать количество фильтров в каждом слое, а также размер пула (т. Е. Один фильтр извлечения, Conv1D(1,...)
, вероятно, не оптимален, а размер пула 1 не делает смысл).
Кроме того, слой Dense
перед последним слоем, который имеет только одну единицу, может серьезно ограничить репрезентативную способность модели (т. е. это, по сути, узкое место вашей модели). Либо увеличьте его количество единиц, либо удалите его.
Другое дело, что нет причин для того, чтобы не кодировать ярлыки набора dev-hot-up. Скорее, они должны быть закодированы как метки в тренировочном наборе. Следовательно, либо аргумент training
make_generator
должен быть полностью удален, либо, если у вас есть какой-то другой вариант использования, набор данных dev должен быть создан с аргументом training=True
, переданным функции make_dataset
.
Наконец, после всех этих изменений ваша модель может работать и начать подгонку к данным; но по прошествии нескольких партий вы снова можете получить ошибку несовместимых фигур. Это потому, что вы генерируете входные данные с неизвестным измерением, а также используете подход с мягким заполнением, чтобы заполнить каждый пакет настолько, насколько это необходимо (т. Е. С помощью (None,
) для padded_shapes
). Чтобы решить эту проблему, вы должны выбрать фиксированное измерение ввода / вывода (например, с учетом фиксированной длины для последовательностей ввода / вывода), а затем скорректировать архитектуру или гиперпараметры модели (например, размер ядра conv, дополнение заполнения, размер пула добавив больше слоев и т. д. c.), а также аргумент padded_shapes
соответственно. Даже если вы хотите, чтобы ваша модель поддерживала последовательности ввода / вывода переменной длины, вам следует учитывать это в архитектуре и гиперпараметрах модели, а также в аргументе padded_shapes
. Так как это решение зависит от задачи и желаемого дизайна в вашем уме, и нет универсальных решений, я бы не стал комментировать это далее и предоставлю вам возможность разобраться в этом. Но вот рабочее решение (которое может и не быть, а может быть и не оптимальным) просто для того, чтобы дать вам представление:
self.out_layer = keras.layers.Dense(params['vocab_size'], activation='softmax')
self.model_layers = [
keras.layers.Embedding(params['vocab_size'], params['vocab_size']),
keras.layers.Conv1D(32, 4, padding='same'),
keras.layers.TimeDistributed(self.out_layer)
]
# ...
padded_shapes=(
{'inputs': (10,)},
{'targets': (10,)}
)