Перевод с английского на французский с использованием модели кодер-декодер

Эта статья мотивирована этим примером keras и этой статьей о сети кодировщика-декодера . Идея состоит в том, чтобы получить интуитивное и подробное понимание из этого примера. Моя собственная реализация этого примера, упомянутого в этой истории, предоставлена ​​по моей ссылке на github .

Прежде чем мы начнем, может быть полезно просмотреть другой мой пост о LSTM, который помогает понять основы LSTM именно в этом контексте.

Ниже представлена ​​подробная сетевая архитектура, используемая для обучения сети seq2seq Encoder - Decoder. Мы будем ссылаться на эту фигуру до конца.

Сначала займемся обучением сети. Затем мы рассмотрим модели вывода о том, как перевести данное английское предложение на французский. Модель вывода (используемая для прогнозирования входной последовательности) имеет немного другую архитектуру декодера, и мы обсудим это подробно, когда перейдем к ней.

Обучение сети -

Итак, как выглядят данные обучения?

  • У нас есть 10 000 английских предложений и соответствующие 10 000 переведенных французских предложений. Итак, наш nb_samples = 10000.

Общий план тренировки -

  1. Создавайте быстрые вложения символов для предложений на английском и французском языках. Это будут входы для кодировщика и декодера. Встроенные французские горячие символы также будут использоваться в качестве целевых данных для функции потерь.
  2. Подача символа за символом вставляется в кодировщик до конца последовательности предложений на английском языке.
  3. Получите окончательные состояния кодировщика (скрытые состояния и состояния ячеек) и загрузите их в декодер в качестве его начального состояния.
  4. Декодер будет иметь 3 входа на каждом временном шаге - 2 состояния декодера, и французский символ внедряется в него посимвольно.
  5. На каждом шаге декодера выходные данные декодера отправляются на уровень softmax, который сравнивается с целевыми данными.

Подробный алгоритм обучения сети с помощью кода -

См. Фрагмент 1. Обратите внимание, что мы добавили "\ t" для начала предложения на французском языке и "\ n" для обозначения конца предложения на французском языке. Эти добавленные французские предложения будут использоваться в качестве входных данных для декодера. Все английские и французские символы собраны в отдельные наборы. Эти наборы преобразуются в словари уровня символов (полезно для получения значений индекса и символов позже).

См. Фрагмент 2 - Подготовка закладных для ввода кодера, ввода декодера и внедрения целевых данных. Мы создадим горячую кодировку для каждого символа на английском и французском языках отдельно. Во фрагменте кода они называются tokenized_eng_sentences и tokenized_fra_sentences. Это будут входы для кодировщика и декодера соответственно. Обратите внимание, что встраивания французских символов target_data, которые мы сравниваем на выходе слоя softmax, смещены на (t + 1) по сравнению с вложениями входных данных декодера (поскольку в целевых данных нет начального тега - см. выше диаграмма архитектуры для большей ясности). Следовательно, target_data в приведенном ниже фрагменте кода соответственно смещается (обратите внимание на k-1 во втором измерении массива target_data ниже)

См. Фрагмент 2 - Как мы отметили в другом моем сообщении о LSTM, встраиваемые (tokenized_eng_sentences и tokenized_fra_sentences и target_data) - это трехмерные массивы. Первое измерение соответствует nb_samples (= 10 000 в данном случае). Второе измерение соответствует максимальной длине предложения на английском / французском языках, а третье измерение соответствует общему количеству английских / французских символов.

Обратитесь к фрагменту 3: Мы будем вводить символ за символом (конечно, их соответствующие горячие вставки) в сеть кодировщика. Для encoder_LSTM мы установили return_state = True. Мы не выполняли return_sequences = True (и по умолчанию установлено значение False). Это означало бы, что мы получаем только окончательное закодированное состояние ячейки и закодированное скрытое состояние в конце входной последовательности, а не промежуточные состояния на каждом временном шаге. Это будут окончательные закодированные состояния, которые используются для инициализации состояния декодера.

См. Фрагмент 3. Также обратите внимание, что форма ввода была указана как (None, len (eng_chars)). Это означает, что кодировщик LSTM может динамически разворачивать такое количество временных шагов как количество символов, пока не достигнет конца последовательности для этого предложения.

См. Фрагмент 4 - входными данными для декодера будут встроенные французские символы (содержащиеся в массиве tokenized_fra_sentences) один за другим на каждом временном шаге вместе со значениями предыдущего состояния. Предыдущие состояния для первого шага декодера будут инициализированы окончательными состояниями кодировщика, которые мы собрали ранее в фрагменте 3. По этой причине обратите внимание, что initial_state = encoder_states был установлен в приведенном ниже коде. фрагмент. Начиная с последующего шага, входные данные состояния декодера будут состоянием его ячейки и ее скрытым состоянием.

Также из приведенного выше фрагмента кода обратите внимание, что декодер настроен на return_sequences = True вместе с return_state = True . Таким образом, мы получаем выходные данные декодера и два состояния декодера на каждом временном шаге. Хотя здесь объявлено return_state = True, мы не собираемся использовать состояния декодера при обучении модели. Причина его наличия в том, что они будут использоваться при построении модели вывода декодера (что мы увидим позже). Выходные данные декодера проходят через слой softmax, который научится классифицировать правильный французский символ.

См. Фрагмент 5 - функция потерь - это категориальная перекрестная энтропия, которая получается путем сравнения предсказанных значений из слоя softmax с target_data (вставка одного горячего французского символа).

Теперь модель готова к обучению. Обучите всю сеть за указанное количество эпох.

Тестирование (режим вывода) -

Ниже представлена ​​архитектура, используемая для моделей вывода. Модель вывода будет использовать все параметры сети, полученные во время обучения, но мы определяем их отдельно, потому что входные и выходные данные во время логического вывода отличаются от тех, которые были во время обучения сети.

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

Сравните этот рисунок B с фигурой A на стороне декодера. Основные изменения можно увидеть ниже -

  • На первом временном шаге декодер имеет 3 входа - начальный тег «\ t» и два состояния кодировщика. Мы вводим первый символ как «\ t» (его один горячий вектор внедрения) на первом временном шаге декодера.
  • Затем декодер выводит первый предсказанный символ (предположим, что это «V»).
  • Посмотрите, как синие линии снова подключаются ко входу декодера для следующего временного шага. Таким образом, этот предсказанный символ «V» будет подаваться на вход декодера на следующем временном шаге.
  • Также обратите внимание, что мы получаем только один вектор горячего внедрения предсказанного символа, используя функцию np.argmax на выходе слоя softmax на каждом временном шаге. Поэтому мы выполняем обратный поиск в словаре по индексу, чтобы получить фактический символ «V».
  • Начиная со следующего временного шага, декодер по-прежнему имеет 3 входа, но отличается от первого временного шага. Это - одно горячее кодирование предыдущего предсказанного символа, предыдущее состояние ячейки декодера и предыдущее скрытое состояние декодера.

Учитывая вышесказанное, теперь давайте посмотрим на код -

См. Фрагмент 6. Модель вывода кодировщика довольно проста. Будет выведен только кодировщик_states.

См. Фрагмент 7 - Модель декодера более сложная. Обратите внимание, что мы создаем отдельные «Входы» для скрытого состояния декодера и состояния ячейки декодера. Это связано с тем, что мы собираемся передавать эти состояния на каждом временном шаге (кроме первого временного шага - напомним, что на первом временном шаге мы загружаем только состояния кодировщика) в декодер и вывод декодера. модель - это отдельная автономная модель. И кодировщик, и декодер будут вызываться рекурсивно для каждого символа, который должен быть сгенерирован в транслируемой последовательности.

Обратитесь к фрагменту 8 - мы получаем состояния кодировщика в переменную states_val. При первом вызове внутри цикла while эти скрытые состояния и состояния ячеек из кодировщика будут использоваться для инициализации decoder_model_inf, которые предоставляются в качестве входных данных напрямую в модель. После того, как мы предсказали символ с помощью softmax, мы теперь вводим этот предсказанный символ (используя трехмерный массив target_seq для быстрого внедрения предсказанного символа) вместе с обновленным States_val ( обновлено из предыдущих состояний декодера) для следующей итерации цикла while. Обратите внимание, что мы сбрасываем наш target_seq перед тем, как создавать горячую вставку предсказанного символа каждый раз в цикле while.

Вот и все! Теперь у нас есть обученная модель, которая может переводить английские предложения на французский! Ниже приведены результаты, полученные после обучения сети за 25 эпох.

Если вы планируете использовать какие-либо из приведенных выше рисунков на диаграммах архитектуры, пожалуйста, сделайте это и попросите указать мое имя в кредитном сообщении.

Пожалуйста, покажите свои аплодисменты, удерживая значок хлопка, если вы найдете какой-либо полезный вывод из этой статьи.