[Перевод] Модели Sequence-to-Sequence Ч.1
Всем добрый день!
И у нас снова открыт новый поток на доработанный курса «Data scientist»: ещё один отличный преподаватель, чуть доработанная исходя из обновлений программа. Ну и как обычно интересные открытые уроки и подборки интересных материалов. Сегодня мы начнём разбор seq2seq моделей от Tensor Flow.
Поехали.
Как уже обсуждалось в туториале RNN (рекомендуем ознакомиться с ним перед чтением этой статьи), рекуррентные нейронные сети можно научить моделировать язык. И возникает интересный вопрос: возможно ли обучение сети на определенных данных для генерации осмысленного ответа? Например, можем ли мы научить нейронную сеть переводить с английского языка на французский? Оказывается, что можем.
Это руководство покажет вам, как создать и обучить такую систему end-to-end. Скопируйте основной репозиторий Tensor Flow и репозиторий моделей TensorFlow с GitHub. Затем, можно начать с запуска программы перевода:
cd models/tutorials/rnn/translate
python translate.py --data_dir [your_data_directory]
Она загрузит данные для перевода с английского на французский с сайта WMT»15, подготовит их для обучения и обучит. Для этого потребуется около 20 гб на жестком диске и довольно много времени на загрузку и подготовку, поэтому вы можете запустить процесс уже сейчас и продолжить читать этот туториал.
Руководство будет обращаться к следующим файлам:
Файл | Что в нем находится? |
---|---|
tensorflow/tensorflow/python/ops/seq2seq.py | Библиотека для создания sequence-to-sequence моделей |
models/tutorials/rnn/translate/seq2seq_model.py | Sequence-to-sequence модели нейронного перевода |
models/tutorials/rnn/translate/data_utils.py | Вспомогательные функции для подготовки данных перевода |
models/tutorials/rnn/translate/translate.py | Бинарник, который обучает и запускает модель перевода |
Основы sequence-to-sequence
Базовая sequence-to-sequence модель, как было представлено Cho et al., 2014 (pdf), состоит из двух рекуррентных нейронных сетей (RNN): encoder (кодер), которая обрабатывает входные данные, и decoder (декодер), которая генерирует данные вывода. Базовая архитектура изображена ниже:
Каждый прямоугольник на картинке выше представляет собой клетку в RNN, обычно клетку GRU — управляемого рекуррентного блока, или клетку LSTM — долгой краткосрочной памяти, (прочтите туториал RNN, чтобы узнать о них подробнее). Кодеры и декодеры могут иметь общие веса или же, чаще, использовать разные наборы параметров. Многослойные клетки успешно используются в sequence-to-sequence моделях, например, для перевода Sutskever et al., 2014 (pdf).
В базовой модели, описанной выше, каждый ввод должен быть закодирован в вектор состояния фиксированного размера, так как это единственное, что передается декодеру. Чтобы дать декодеру более прямой доступ к данным ввода, в Bahdanau et al., 2014 (pdf) был представлен механизм внимания. Мы не будем вдаваться в подробности механизма внимания (для этого можно ознакомиться с работой по ссылке); достаточно сказать, что он позволяет декодеру заглядывать в данные ввода на каждом шаге декодирования. Многослойная sequence-to-sequence сеть с LSTM клетками и механизмом внимания в декодере выглядит следующим образом:
Библиотека TensorFlow seq2seq
Как можно увидеть выше, существуют разные модели sequence-to-sequence. Все они могут использовать разные клетки RNN, но все они принимают данные ввода кодера и данные ввода декодера. Это лежит в основе интерфейса библиотеки TensorFlow seq2seq (tensorflow/tensorflow/python/ops/seq2seq.py). Эта базовая, RNN, кодер-декодер, sequence-to-sequence модель работает следующим образом.
outputs, states = basic_rnn_seq2seq(encoder_inputs, decoder_inputs, cell)
В вызове, указанном выше, encoder_inputs
является списком тензоров, представляющих данные ввода кодера, соответствуя буквам A, B, C с картинки выше. Аналогично, decoder_inputs
— тензоры, представляющие данные ввода декодера. GO, W, X, Y, Z с первой картинки.
Аргумент cell
— инстанс класса tf.contrib.rnn.RNNCell
, который определяет, какая клетка будет использоваться в модели. Можно использовать существующие клетки, например, GRUCell
или LSTMCell
, а можно написать свою. Кроме того, tf.contrib.rnn
предоставляет оболочки для создания многослойных клеток, добавления исключений данным ввода и вывода клетки, или иных трансформаций. Почитайте RNN Tutorial, для ознакомления с примерами.
Вызов basic_rnn_seq2seq
возвращает два аргумента: outputs
и states
. Они оба представляют собой список тензоров той же длины, что и decoder_inputs
. outputs
соответствует данным вывода декодера на каждом временном шаге, на первой картинке это W, X, Y, Z, EOS. Возвращаемый states
представляет внутреннее состояние декодера на каждом временном шаге.
Во многих приложениях, использующих sequence-to-sequence модели, вывод декодера в момент t передается обратно на ввод декодеру в момент t+1. При тестировании, во время декодирования последовательности, именно так конструируется новая. С другой стороны, при обучении принято передавать декодеру правильные данные ввода в каждый временной шаг, даже если декодер ранее ошибся. Функции в seq2seq.py
поддерживают оба режима с помощью аргумента feed_previous
. Например, проанализируем следующее использование вложенной RNN модели.
outputs, states = embedding_rnn_seq2seq(
encoder_inputs, decoder_inputs, cell,
num_encoder_symbols, num_decoder_symbols,
embedding_size, output_projection=None,
feed_previous=False)
В модели embedding_rnn_seq2seq
все данные ввода (как encoder_inputs
, так и decoder_inputs
) являются целочисленными тензорами, отражающими дискретные значения. Они будут вложены в плотное представление (за подробностями по вложению обратитесь к Руководству по Векторным Представлениям), но для создания этих вложений нужно уточнить максимальное количество дискретных символов: num_encoder_symbols
на стороне кодера и num_decoder_symbols
на стороне декодера.
В вызове выше мы задаем feed_previous
значение False. Это значит, что декодер будет использовать тензоры decoder_inputs
в том виде, в котором они предоставляются. Если мы зададим feed_previous
значение True, декодер будет использовать только первый элемент decoder_inputs
. Все прочие тензоры из списка будут проигнорированы, и взамен будет использоваться предыдущее значение вывода декодера. Это применяется для декодирования переводов в нашей модели перевода, но также может использоваться во время обучения, для улучшения устойчивости модели к своим ошибкам. Примерно как у Bengio et al., 2015 (pdf).
Еще один важный аргумент, использованный выше, — output_projection
. Без уточнений выводы вложенной модели будут тензорами формы количество обучающих образцов на num_decoder_symbols
, так как они представляют логиты каждого сгенерированного символа. При тренировке моделей с большими словарями на выходе, например с большим num_decoder_symbols
, хранить эти крупные тензоры становится непрактичным. Вместо этого, лучше возвращать тензоры меньших размеров, которые впоследствии будут спроецированы на большой тензор с помощью output_projection
. Это позволяет использовать наши seq2seq модели c сэмплированными softmax потерями, как описано у Jean et. al., 2014 (pdf).
В дополнении к basic_rnn_seq2seq
и embedding_rnn_seq2seq
в seq2seq.py
существует еще несколько sequence-to-sequence моделей. Обратите на них внимание. Все они обладают схожим интерфейсом, поэтому не будем углубляться в их детали. Для нашей модели перевода ниже используем embedding_attention_seq2seq
.
Продолжение последует.