- Оригинальный адрес:РЕКУРРЕНТНЫЕ НЕЙРОННЫЕ СЕТИ (RNN) – ЧАСТЬ 3: КОДЕР-ДЕКОДЕР
- Оригинальный автор:GokuMohandas
- Перевод с:Программа перевода самородков
- Постоянная ссылка на эту статью:GitHub.com/rare earth/gold-no…
- Переводчик:Changkun Ou
- Корректор:zcgeng
Краткое содержание статей из этой серии
- Рекуррентная нейронная сеть RNN, серия 1: базовая RNN против CHAR-RNN
- Рекуррентная нейронная сеть RNN, серия 2: классификация текста
- Рекуррентная нейронная сеть RNN, серия 3: кодировщик, декодер
- Рекуррентная нейронная сеть RNN, серия 4: механизм внимания
- Рекуррентные нейронные сети RNN, серия 5: Пользовательские единицы
Рекуррентная нейронная сеть RNN, серия 3: кодировщик, декодер
В этой статье я представлю базовый кодировщик (encoder) и декодер (decoder) для обработки задач seq2seq, таких как машинный перевод. Мы не будем вводить механизм внимания в этой статье, но реализуем его в следующей статье.
Как показано на рисунке ниже, мы передаем входную последовательность в кодировщик, а окончательное скрытое состояние будет сгенерировано и передано в декодер. То есть последнее скрытое состояние кодера является новым начальным состоянием декодера. Мы будем использовать softmax для обработки вывода декодера и сравнения его с целью для расчета нашей функции потерь. ты можешь начатьэтот пост в блогеУзнайте больше о моем введении в модель, предложенную в оригинальной статье в . Основное отличие здесь в том, что я не добавлял токен EOS (конец предложения) на вход кодировщика, и кодировщик не читал предложение задом наперед.
данные
Я хочу создать очень небольшой набор данных (20 предложений на английском и испанском языках). Основное внимание в этом учебном пособии уделяется пониманию того, как построить систему кодировщика-декодера, а не сосредоточению внимания на том, что система делает для таких задач, как машинный перевод и другая обработка seq2seq. Поэтому я сам написал несколько предложений и перевел их на испанский. Это наш набор данных.
Сначала мы разделяем эти предложения на токены, а затем конвертируем эти токены в идентификаторы токенов. В ходе этого процесса мы собираем словарный словарь и обратный словарь для преобразования токенов и идентификаторов токенов туда и обратно. Для нашего целевого языка (испанский) мы добавим дополнительный токен EOS. Затем мы дополняем исходные и целевые токены до максимальной длины (соответствующей самому длинному предложению в наборе данных). Это входные данные для нашей модели. Что касается кодировщика, мы передаем дополненный исходный контент напрямую и дополнительно обрабатываем целевой контент, чтобы получить ввод и вывод нашего декодера.
Наконец, результат ввода выглядит следующим образом:
Это всего лишь один образец из определенной партии. Где 0 — это дополненное значение, 1 — токен GO, а 2 — токен EOS. На рисунке ниже показано более общее представление преобразования данных. Пожалуйста, не обращайте внимания на веса целей, мы не будем их использовать в реализации.
Кодер
Кодировщик принимает ввод только от кодировщика, и единственное, о чем мы заботимся, — это конечное скрытое состояние. Это скрытое состояние содержит всю входную информацию. Мы не инвертируем вход энкодера, как это предлагается в исходной статье, поскольку мы используемdynamic_rnn
изseq_len
. он будет основан наseq_len
Автоматически возвращается к последнему соответствующему скрытому состоянию.
with tf.variable_scope('encoder') as scope:
# RNN 编码器单元
self.encoder_stacked_cell = rnn_cell(FLAGS, self.dropout,
scope=scope)
# 嵌入 RNN 编码器输入
W_input = tf.get_variable("W_input",
[FLAGS.en_vocab_size, FLAGS.num_hidden_units])
self.embedded_encoder_inputs = rnn_inputs(FLAGS,
self.encoder_inputs, FLAGS.en_vocab_size, scope=scope)
#initial_state = encoder_stacked_cell.zero_state(FLAGS.batch_size, tf.float32)
# RNN 编码器的输出
self.all_encoder_outputs, self.encoder_state = tf.nn.dynamic_rnn(
cell=self.encoder_stacked_cell,
inputs=self.embedded_encoder_inputs,
sequence_length=self.en_seq_lens, time_major=False,
dtype=tf.float32)
Мы будем использовать это конечное скрытое состояние в качестве нового начального состояния декодера.
декодер
Этот простой декодер принимает конечное скрытое состояние кодировщика в качестве своего начального состояния. Мы также будем использовать входные данные декодера и использовать декодер RNN для их обработки. Вывод будет нормализован softmax, а затем сравнен с целью. Обратите внимание, что ввод декодера начинается с токена GO, который используется для предсказания первого целевого токена. Последний соответствующий токен, введенный декодером, используется для прогнозирования целевого токена EOS.
with tf.variable_scope('decoder') as scope:
# 初始状态是编码器的最后一个对应状态
self.decoder_initial_state = self.encoder_state
# RNN 解码器单元
self.decoder_stacked_cell = rnn_cell(FLAGS, self.dropout,
scope=scope)
# 嵌入 RNN 解码器输入
W_input = tf.get_variable("W_input",
[FLAGS.sp_vocab_size, FLAGS.num_hidden_units])
self.embedded_decoder_inputs = rnn_inputs(FLAGS, self.decoder_inputs,
FLAGS.sp_vocab_size, scope=scope)
# RNN 解码器的输出
self.all_decoder_outputs, self.decoder_state = tf.nn.dynamic_rnn(
cell=self.decoder_stacked_cell,
inputs=self.embedded_decoder_inputs,
sequence_length=self.sp_seq_lens, time_major=False,
initial_state=self.decoder_initial_state)
Так что же происходит со значением заполнения? Они также предсказывают некоторые цели по выходу, которые нам не нужны, но все равно влияют на нашу функцию потерь, если мы их учитываем. Затем мы маскируем эти потери, чтобы исключить влияние на целевой результат.
маска потери
Мы проверяем цель и маскируем дополненную часть цели до 0. Поэтому, когда мы получим последний соответствующий токен декодера, целью будет идентификатор токена, представляющий EOS. А для ввода в следующий декодер целью будет PAD ID, с которого начинается маскирование.
# Logit
self.decoder_outputs_flat = tf.reshape(self.all_decoder_outputs,
[-1, FLAGS.num_hidden_units])
self.logits_flat = rnn_softmax(FLAGS, self.decoder_outputs_flat,
scope=scope)
# 损失屏蔽
targets_flat = tf.reshape(self.targets, [-1])
losses_flat = tf.nn.sparse_softmax_cross_entropy_with_logits(
self.logits_flat, targets_flat)
mask = tf.sign(tf.to_float(targets_flat))
masked_losses = mask * losses_flat
masked_losses = tf.reshape(masked_losses, tf.shape(self.targets))
self.loss = tf.reduce_mean(
tf.reduce_sum(masked_losses, reduction_indices=1))
Отметив, что мы можем использовать тот факт, что идентификатор PAD равен 0 в качестве маски, нам просто нужно рассчитать сумму потерь для каждой строки (выборок в партии), а затем взять среднее значение всех потерь выборки, чтобы получить пачка потерь. На этом этапе мы можем тренироваться, минимизируя эту функцию потерь.
Вот результаты обучения:
Мы не будем здесь делать вывод о модели, но вы можете увидеть это в следующей статье о механизмах внимания. Если вы действительно хотите реализовать вывод модели здесь, вы можете использовать ту же модель, но вы также должны передать результат прогнозирования цели в качестве входных данных для следующего модуля декодера RNN. В то же время вы также встроите тот же набор весов в декодер и будете использовать его в качестве еще одного входа в RNN. Это означает, что для первоначального токена GO вы должны встроить какой-то поддельный токен для ввода.
в заключении
Эта модель кодека очень проста, но она необходима для понимания реализации seq2seq. В следующем руководстве по RNN мы рассмотрим модель внимания и ее преимущества по сравнению со структурой модели кодер-декодер.
код
Репозиторий GitHub(Обновление, следите за обновлениями!)
Программа перевода самородковэто сообщество, которое переводит высококачественные технические статьи из Интернета сНаггетсДелитесь статьями на английском языке на . Охват контентаAndroid,iOS,React,внешний интерфейс,задняя часть,продукт,дизайнЕсли вы хотите видеть более качественные переводы, пожалуйста, продолжайте обращать вниманиеПрограмма перевода самородков,официальный Вейбо,Знай колонку.