Последовательность за последовательностью (seq2seq)[1] - это универсальная структура, способная на многие вещи (языковой перевод, суммирование текста [2], субтитры [3] и т. д.). Для краткого введения в seq2seq, вот несколько хороших сообщений: [4] [5] .
Sean Robertson’s tutorial notebook[6] а также Jeremy Howard’s лекции [6] [7] являются отличной отправной точкой, чтобы получить четкое представление о технических деталях seq2seq. Тем не менее, я постараюсь избежать реализации всех этих деталей самостоятельно, когда имею дело с реальными проблемами. Изобретать колесо, как правило, не очень хорошая идея, особенно если вы новичок в этой области. Я обнаружил, что проект OpenNMT очень активен, имеет хорошую документацию и может быть использован "из коробки":
Существуют также более общие рамки (например, [8]), но, возможно, потребуется некоторая настройка, чтобы она работала над вашей конкретной проблемой.
Существует две официальные версии OpenNMT:
OpenNMT-Lua (a.k.a. OpenNMT): основной проект, разработанный с помощью LuaTorch. Оптимизированный и стабильный код для производственных и масштабных экспериментов.
OpenNMT-py : облегченная версия OpenNMT с использованием PyTorch .
Первоначально созданная исследовательской группой Facebook AI в качестве примера проекта для PyTorch, эта версия проще в расширении и подходит для исследовательских целей, но не включает в себя все функции.
Мы собираемся использовать версию PyTorch в следующих разделах. Мы проведем вас через шаги, необходимые для создания очень простой системы перевода с набором данных среднего размера.
Клонируйте репозиторий OpenNMT-py git на Github в локальную папку:
Возможно, вы захотите раскошелиться на Github, если вы планируете настроить или расширить его позже. Также предлагается в README:
Codebase приближается к стабильной версии 0.1. В настоящее время мы рекомендуем разветвить, если вы хотите стабильный код.
Здесь мы собираемся использовать набор данных из AI Challenger - English-китайский конкурс машинного перевода. Это набор данных с 10 миллионами англо-китайских пар предложений. Английская копора - это разговорный английский, извлеченный из веб-сайтов по изучению английского языка и субтитров к фильмам. Насколько я понимаю, большая часть перевода представлена энтузиастами, а не профессионалами. Переведенные китайские предложения проверяются аннотаторами-людьми.
English-Chinese Machine Translation - AI Challenger
English-Chinese Machine Translation - Prize:¥300,000 - Improve the performance of English-Chinese machine translation… challenger.ai
Для загрузки набора данных требуется регистрация учетной записи и, возможно, проверка идентификатора (не могу вспомнить, является ли последний обязательным). Если это проблема для вас, вы можете попробовать datasets from WMT17 .
У набора данных AI Challenger есть некоторые проблемы: 1. Качество перевода не соответствует. 2. Поскольку многие предложения взяты из субтитров фильма, перевод часто зависит от контекста (относится к предыдущему или следующему предложению). Однако в наборе данных нет контекстной информации.
Давайте посмотрим, как стандартная модель работает с этим набором данных. Из-за ограничений памяти я сократил набор данных до 1 миллиона предложений.
«Предположим, что вы поместили intofolder набора данных challenger в корневой каталог OpenNMT».
Набор данных для проверки и тестирования поставляется в формате XML. Нам нужно преобразовать его в простые текстовые файлы, где строка состоит из одного предложения. Простой способ сделать это - использовать BeautifulSoup. Вот пример кода:
with open(input_file, "r") as f:
soup = BeautifulSoup(f.read(), "lxml")
lines = [
(int(x["id"]), x.text.strip()) for x in soup.findAll("seg")]
# Ensure the same order
lines = sorted(lines, key=lambda x: x[0])
Входное предложение должно быть маркировано токенами через пробел.
Для английского есть несколько токенизаторов на выбор. Одним из примеров является nltk.tokenize.word_tokenize
:
with open(output_file, "w") as f:
f.write(
"\\n".join([
" ".join(word\_tokenize(l[1]))
for l in lines
])
)
Получается “It’s a neat one — two. Walker to Burton.” в “It ‘s a neat one — two . Walker to Burton .”.
Для китайского языка мы используем простейший токенизация на уровне символов, то есть каждый символ рассматривается как токен:
with open(output_file, "w") as f:
f.write(
"\\n".join([
" ".join([c if c != " " else "<s>" for c in l[1]])
for l in lines
])
)
Получается “我就一天24小时都得在她眼皮子底下。” в “我 就 一 天 2 4 小 时 都 得 在 她 眼 皮 子 底 下 。”. (Обратите внимание, поскольку токен разделен пробелом, нам нужен специальный токен «<s>» для представления символов пробела.)
(Я не предоставил полный код для шага 3 и шага 4, потому что это действительно программирование на Python начального уровня. Вы должны быть в состоянии выполнить эти задачи самостоятельно.)
Просто запустите следующую команду в корневом каталоге:
python preprocess.py -train_src challenger/train.en.sample \
-train_tg challenger/train.zh.sample \
-valid_src challenger/valid.en \
-valid_tgt challenger/valid.zh \
-save_data challenger/opennmt -report_every 10000
Скрипт предварительной обработки будет проходить через набор данных, отслеживать частоты токенов и составлять список слов. У меня возникла проблема с памятью, и мне пришлось уменьшить выборку обучающего набора данных до 1 миллиона строк, но я думаю, что необработанный набор данных должен уместиться в 16 ГБ памяти с некоторой оптимизацией.
python train.py -data challenger/opennmt \
-save_model models/baseline -gpuid 0 \
-learning_rate 0.001 -opt adam -epochs 20
Он будет использовать ваш первый графический процессор для обучения модели. Структура модели по умолчанию:
NMTModel (
(encoder): RNNEncoder (
(embeddings): Embeddings (
(make\_embedding): Sequential (
(emb\_luts): Elementwise (
(0): Embedding(50002, 500, padding\_idx=1)
)
)
)
(rnn): LSTM(500, 500, num\_layers=2, dropout=0.3)
)
(decoder): InputFeedRNNDecoder (
(embeddings): Embeddings (
(make\_embedding): Sequential (
(emb\_luts): Elementwise (
(0): Embedding(6370, 500, padding\_idx=1)
)
)
)
(dropout): Dropout (p = 0.3)
(rnn): StackedLSTM (
(dropout): Dropout (p = 0.3)
(layers): ModuleList (
(0): LSTMCell(1000, 500)
(1): LSTMCell(500, 500)
)
)
(attn): GlobalAttention (
(linear\_in): Linear (500 -> 500)
(linear\_out): Linear (1000 -> 500)
(sm): Softmax ()
(tanh): Tanh ()
)
)
(generator): Sequential (
(0): Linear (500 -> 6370)
(1): LogSoftmax ()
)
)
Размер словаря исходных и целевых корпусов составляет 50,002 и 6370 соответственно. Исходный словарь явно урезан до 50000. Целевой словарь относительно невелик, потому что не так много распространенных китайских иероглифов.
python translate.py \
-model models/baseline_acc_58.79_ppl_7.51_e14 \
-src challenger/valid.en -tgt challenger/valid.zh \
-output challenger/valid_pred.58.79 -gpu 0 -replace_unk
Замените models/baseline_acc_58.79_ppl_7.51_e14
своей собственной моделью. Наименование модели должно быть очевидным: это модель после 14 эпох обучения, с 58,79 точностью и 7,51 недоумением при проверке набора.
Вы также можете рассчитать балл BLEU с помощью следующего:
$ wget https://raw.githubusercontent.com/moses-smt/mosesdecoder/master/scripts/generic/multi-bleu.perl
$ perl multi-bleu.perl challenger/valid.zh \
< challenger/valid_pred.58.79
Теперь у вас есть работающая система перевода!
Если вы хотите отправить перевод в AI Challenger, вам нужно отменить шаг 4, а затем шаг 3. Опять же, они должны быть довольно просты в реализации.
English: You knew it in your heart you haven’t washed your hair
Chinese(pred): 你心里清楚你没洗头发
Chinese(gold): 你心里知道你压根就没洗过头
English: I never dreamed that one of my own would be going off to a University, but here I stand,
Chinese(pred): 我从来没梦到过我的一个人会去大学,但是我站在这里,
Chinese(gold): 我从没想过我的孩子会上大学,但我站在这,
English: We just don’t have time to waste on the wrong man.
Chinese(pred): 我们只是没时间浪费人。
Chinese(gold): 如果找错了人我们可玩不起。
Вышеприведенные три примера: сверху вниз, семантически правильные, частично правильные и совершенно непонятные. Изучив несколько примеров, я обнаружил, что большинство машинно-переведенных предложений были частично правильными, и было удивительное количество семантически правильных. Неплохой результат, учитывая, как мало усилий мы приложили до сих пор.
Если вы отправите результат, вы должны обойти .22 BLEU. Текущий максимальный балл BLEU .33, так что есть много возможностей для улучшения. Вы можете проверить opts.py
в корневой папке для большего количества встроенных параметров модели. Или углубиться в кодовую базу, чтобы выяснить, как все работает и где можно улучшить.
Другие способы включают в себя применение сегментации слов к китайским предложениям, добавление распознавания именованных сущностей, использование словаря произношения [\ 10] для угадывания перевода. невидимые английские имена и т. д.
(Обновление от 2017/10/14: если вы используете jieba и jieba.cut с настройками по умолчанию для токенизации предложения на китайском языке, вы получите возможность обойти .20 BLEU на общественном leaderboad. Одна из возможных причин для падения оценки является намного больше китайского размером словаря. Вы можете сказать, из числа <УНК> на выходе.)
- Sutskever, I., Vinyals, O., & Le, Q. V. (2014). Sequence to Sequence Learning with Neural Networks .
- Nallapati, R., Zhou, B., dos Santos, C., & Xiang, B. (2016). Abstractive Text Summarization using Sequence-to-sequence RNNs and Beyond.
- Venugopalan, S., Rohrbach, M., Donahue, J., Mooney, R., Darrell, T., & Saenko, K. (2015). Sequence to Sequence
- Sequence to sequence model: Introduction and concepts
- seq2seq: the clown car of deep learning
- Practical PyTorch: Translation with a Sequence to Sequence Network and Attention
- Cutting Edge Deep Learning For Coders, Part 2, Lecture 12 — Attention Models
- Cutting Edge Deep Learning For Coders, Part 2, Lecture 13 — Neural Translation
- Google/seq2seq: A general-purpose encoder-decoder framework for Tensorflow
- The CMU Pronouncing Dictionary