- Регистрация
- 21.07.20
- Сообщения
- 40.408
- Реакции
- 1
- Репутация
- 0
Привет. Некоторое время назад я
Что это такое? Допустим, мы хотим обучить переводчик с чувашского на русский. Берем некоторое небольшое количество параллельных данных: предложений, которые есть на двух языках. В нашем случае это 250 тысяч предложений. Обучаем модель в обратную сторону, с русского на чувашский. Теперь у нас есть относительно неплохой переводчик. Далее возьмем много данных на одном языке, в нашем случае это 1 миллион предложений на русском языке. Переведем их на чувашский язык. В результате у нас появится дополнительный синтетический параллельный корпус из 1 миллиона пар предложений. Дальше смешиваем с оригинальным корпусом и обучаем. Получается неплохой профит, при этом метод достаточно прост и не требует изменений в архитектуре сети.
Чтобы показать этот профит, нужно с чем-то сравниться. В качестве бейзлайна возьмем модель, обученную только на параллельных данных. Здесь и далее используем архитектуру
pip install sockeye
Обучаем на GPU
pip install mxnet-cu100mkl
Поскольку чувашский и русский языки сложны морфологически, то собрать словарь из уникальных слов проблематично, поэтому используем Byte Pair Encoding (BPE)
git clone
python subword-nmt/subword_nmt/learn_joint_bpe_and_vocab.py --input /content/train/chv.train_250K.tok /content/train/ru.train_250K.tok -s 10000 -o bpe.codes --write-vocabulary bpe.vocab.chv bpe.vocab.ru
python subword-nmt/subword_nmt/apply_bpe.py -c bpe.codes --vocabulary bpe.vocab.chv --vocabulary-threshold 50 < chv.train_250K.tok > chv.train_250K.bpe
python subword-nmt/subword_nmt/apply_bpe.py -c bpe.codes --vocabulary bpe.vocab.ru --vocabulary-threshold 50 < ru.train_250K.tok > ru.train_250K.bpe
python subword-nmt/subword_nmt/apply_bpe.py -c bpe.codes --vocabulary bpe.vocab.chv --vocabulary-threshold 50 < chv.dev.tok > chv.dev.bpe
python subword-nmt/subword_nmt/apply_bpe.py -c bpe.codes --vocabulary bpe.vocab.ru --vocabulary-threshold 50 < ru.dev.tok > ru.dev.bpe
Параметры обучения такие:
python -m sockeye.prepare_data -s chv.train_250K.bpe -t ru.train_250K.bpe -o chvru_data
python -m sockeye.train -d chvru_data -vs chv.dev.bpe -vt ru.dev.bpe --encoder transformer --decoder transformer --transformer-model-size 512 --transformer-feed-forward-num-hidden 256 --transformer-dropout-prepost 0.1 --num-embed 512 --max-seq-len 100 --decode-and-evaluate 500 -o chvru_model --num-layers 6 --disable-device-locking --keep-last-params 1 --batch-size 1024 --optimized-metric bleu --max-num-checkpoint-not-improved 15
Получается 30,77 BLEU.
Классический обратный перевод
Для нейронных сетей эталонная статья
python subword-nmt/subword_nmt/learn_joint_bpe_and_vocab.py --input /content/train/chv.train_250K_bt_1M.tok /content/train/ru.train_250K_bt_1M.tok -s 10000 -o bpe.codes --write-vocabulary bpe.vocab.chv bpe.vocab.ru
python subword-nmt/subword_nmt/apply_bpe.py -c bpe.codes --vocabulary bpe.vocab.chv --vocabulary-threshold 50 < chv.train_250K_bt_1M.tok > chv.train_250K_bt_1M.bpe
...
python -m sockeye.prepare_data -s chv.train_250K_bt_1M.bpe -t ru.train_250K_bt_1M.bpe -o chvru_data
python -m sockeye.train ...
Даже такой базовый метод сразу дает хороший рост относительно базовой модели: 34,38 BLEU
В дальнейшем было предложено несколько модификаций и альтернатив. Давайте на них тоже посмотрим.
Тегированный обратный перевод
Первый рассматриваемый способ усовершенствования описан в статье
python subword-nmt/subword_nmt/learn_joint_bpe_and_vocab.py --input /content/train/chv.train_250K_tagbt_1M.tok /content/train/ru.train_250K_tagbt_1M.tok -s 10000 -o bpe.codes --write-vocabulary bpe.vocab.chv bpe.vocab.ru
python subword-nmt/subword_nmt/apply_bpe.py -c bpe.codes --vocabulary bpe.vocab.chv --vocabulary-threshold 50 < chv.train_250K_tagbt_1M.tok > chv.train_250K_tagbt_1M.bpe
...
python -m sockeye.prepare_data -s chv.train_250K_tagbt_1M.bpe -t ru.train_250K_tagbt_1M.bpe -o chvru_data
python -m sockeye.train ...
В результате получилось 34,32 BLEU. Примерно столько же, сколько и на классическом обратном переводе. Делаем вывод, что наш синтетический корпус еще не так велик, и можно обратно переводить еще. К сожалению, это по времени затратный процесс, поэтому оставим для дальнейших исследований.
COPY
Очень простой метод, описанный в статье
python subword-nmt/subword_nmt/learn_joint_bpe_and_vocab.py --input /content/train/chv.train_250K_cp_1M.tok /content/train/ru.train_250K_cp_1M.tok -s 10000 -o bpe.codes --write-vocabulary bpe.vocab.chv bpe.vocab.ru
python subword-nmt/subword_nmt/apply_bpe.py -c bpe.codes --vocabulary bpe.vocab.chv --vocabulary-threshold 50 < chv.train_250K_cp_1M.tok > chv.train_250K_cp_1M.bpe
...
python -m sockeye.prepare_data -s chv.train_250K_cp_1M.bpe -t ru.train_250K_cp_1M.bpe -o chvru_data
python -m sockeye.train ...
Такой метод дает 31,19 BLEU на нашем корпусе.
Дальше метод можно чуть усовершенствовать: при подготовке словаря использовать только параллельные данные, чтобы в словаре исходного языка акцент был на чувашский.
python subword-nmt/subword_nmt/learn_joint_bpe_and_vocab.py --input /content/train/chv.train_250K.tok /content/train/ru.train_250K.tok -s 10000 -o bpe.codes --write-vocabulary bpe.vocab.chv bpe.vocab.ru
python subword-nmt/subword_nmt/apply_bpe.py -c bpe.codes --vocabulary bpe.vocab.chv --vocabulary-threshold 50 < chv.train_250K_cp_1M.tok > chv.train_250K_cp_1M.bpe
...
python -m sockeye.prepare_data -s chv.train_250K_cp_1M.bpe -t ru.train_250K_cp_1M.bpe -o chvru_data
python -m sockeye.train ...
И это действительно работает. Подход дает 32,44 BLEU. Хуже чем обратный перевод, но если нет времени на подготовку синтетических данных, то метод хороший.
Дальше авторы статьи предлагают совместить обратный перевод и COPY так, что модель будет обучена на 2 250 000 парах предложений: 250 тысяч — параллельный корпус, 1 миллион — обратный перевод, и еще 1 миллион — COPY. Таким образом предложения на русском языке из моно-корпуса появятся дважды. К сожалению, здесь улучшения на нашем корпусе не получилось: 33,43 BLEU. Авторы показывали, что ухудшение появляется только на больших корпусах. Однако, и у нас на бедном корпусе тоже видно ухудшение. Можно предположить только, что вручную качественно собранные 250 тысяч предложений — это уже достаточно большой корпус
Word-on-Word
Следующую статью
NOISE
Далее рассмотрим еще два способа, описанные в статье Сергея Едунова, который, как он сам
python subword-nmt/subword_nmt/learn_joint_bpe_and_vocab.py --input /content/train/chv.train_250K_noise_1M.tok /content/train/ru.train_250K_1M.tok -s 10000 -o bpe.codes --write-vocabulary bpe.vocab.chv bpe.vocab.ru
python subword-nmt/subword_nmt/apply_bpe.py -c bpe.codes --vocabulary bpe.vocab.chv --vocabulary-threshold 50 < chv.train_250K_noise_1M.tok > chv.train_250K_noise_1M.bpe
...
python -m sockeye.prepare_data -s chv.train_250K_noise_1M.bpe -t ru.train_250K_1M.bpe -o chvru_data
python -m sockeye.train ...
В итоге получается 33,25 BLEU. На нашем корпусе проигрывает классическому обратному переводу. Видимо, подразумеваемся, что синтетический корпус должен быть кратно больше.
SAMPLING
Суть второго метода из статьи в том, чтобы иначе сделать обратный перевод, так как он неидеален. Предлагается брать не лучший по мнению модели перевод, а некоторый случайный из предложенных моделью. Получившиеся предложения будут хуже, но более разнообразными, и дадут более богатый сигнал для обучения. Технически это решается добавлением одного параметра в команду перевода. Было
python -m sockeye.translate --input ru.train_1M.bpe -m ruchv_model --output chv.train_1M.bpe
Стало
python -m sockeye.translate --input ru.train_1M.bpe -m ruchv_model --output chv.train_1M.bpe --sample
Результат: 32,67 BLEU. И это тоже хуже, чем классический обратный перевод. Гипотеза, почему так, примерно такая же, как и с методом выше.
Совмещение с Переносом Знания
В заключении обсудим еще один важный вопрос: можно ли совместить обратный перевод и перенос знания, про который я писал в прошлой статье.
В качестве основы для метода переноса знаний на этот раз используем 3 миллиона предложений казахско-русского корпуса: те, для которых коэффициент соответствия (третья колонка) больше 1. Дочерний корпус — те же 250 тысяч предложений. Для этого объема данных получили 35,24 BLEU. Да-да, это чуть лучше, чем результаты обратного перевода.
По всем законам жанра, дальше должна быть история про то, что комбинация двух методов дала еще более высокий результат. Но нет. Эксперименты с использованием разных вариантов словарей (с добавлением данных обратного перевода и без) и разных дочерних корпусов для дообучения (опять же с добавлением данных обратного перевода и без) показали себя либо чуть хуже, либо значительно хуже. Вплоть до того, что если и в подготовке общего словаря, и в качестве дочернего использовать корпус в 250 тысяч параллельных предложений вместе с 1 миллионом обратно-переведенных, то получим весьма заметное ухудшение 24,73 BLEU. Причем сильнее всего на результат влияет общий словарь. Поле для дальнейших исследований.
Выводы
Итого в результате экспериментов пришли к тому, что классический обратный перевод дает наилучший результат. Равно как и метод переноса знаний. Использовать можно любой из них, в зависимости от имеющихся данных. COPY подходит, когда нет времени и ресурсов переводить обратно, помогает быстро улучшить базовую модель. Остальные методы дают результаты чуть хуже, их надо применять аккуратно, сравнивать на вашем корпусе.
You must be registered for see links
про способ сделать хороший машинный перевод на нейронных сетях, когда в наличии мало данных. Всего таких способа два. И наступило время рассказать про второй. Встречайте: обратный перевод.Что это такое? Допустим, мы хотим обучить переводчик с чувашского на русский. Берем некоторое небольшое количество параллельных данных: предложений, которые есть на двух языках. В нашем случае это 250 тысяч предложений. Обучаем модель в обратную сторону, с русского на чувашский. Теперь у нас есть относительно неплохой переводчик. Далее возьмем много данных на одном языке, в нашем случае это 1 миллион предложений на русском языке. Переведем их на чувашский язык. В результате у нас появится дополнительный синтетический параллельный корпус из 1 миллиона пар предложений. Дальше смешиваем с оригинальным корпусом и обучаем. Получается неплохой профит, при этом метод достаточно прост и не требует изменений в архитектуре сети.
Чтобы показать этот профит, нужно с чем-то сравниться. В качестве бейзлайна возьмем модель, обученную только на параллельных данных. Здесь и далее используем архитектуру
You must be registered for see links
, реализованную на
You must be registered for see links
pip install sockeye
Обучаем на GPU
pip install mxnet-cu100mkl
Поскольку чувашский и русский языки сложны морфологически, то собрать словарь из уникальных слов проблематично, поэтому используем Byte Pair Encoding (BPE)
git clone
You must be registered for see links
python subword-nmt/subword_nmt/learn_joint_bpe_and_vocab.py --input /content/train/chv.train_250K.tok /content/train/ru.train_250K.tok -s 10000 -o bpe.codes --write-vocabulary bpe.vocab.chv bpe.vocab.ru
python subword-nmt/subword_nmt/apply_bpe.py -c bpe.codes --vocabulary bpe.vocab.chv --vocabulary-threshold 50 < chv.train_250K.tok > chv.train_250K.bpe
python subword-nmt/subword_nmt/apply_bpe.py -c bpe.codes --vocabulary bpe.vocab.ru --vocabulary-threshold 50 < ru.train_250K.tok > ru.train_250K.bpe
python subword-nmt/subword_nmt/apply_bpe.py -c bpe.codes --vocabulary bpe.vocab.chv --vocabulary-threshold 50 < chv.dev.tok > chv.dev.bpe
python subword-nmt/subword_nmt/apply_bpe.py -c bpe.codes --vocabulary bpe.vocab.ru --vocabulary-threshold 50 < ru.dev.tok > ru.dev.bpe
Параметры обучения такие:
python -m sockeye.prepare_data -s chv.train_250K.bpe -t ru.train_250K.bpe -o chvru_data
python -m sockeye.train -d chvru_data -vs chv.dev.bpe -vt ru.dev.bpe --encoder transformer --decoder transformer --transformer-model-size 512 --transformer-feed-forward-num-hidden 256 --transformer-dropout-prepost 0.1 --num-embed 512 --max-seq-len 100 --decode-and-evaluate 500 -o chvru_model --num-layers 6 --disable-device-locking --keep-last-params 1 --batch-size 1024 --optimized-metric bleu --max-num-checkpoint-not-improved 15
Получается 30,77 BLEU.
Классический обратный перевод
Для нейронных сетей эталонная статья
You must be registered for see links
про обратный перевод появилась в 2016 году. Обратите внимание, что архитектура сети остается прежней, меняется только входной корпусpython subword-nmt/subword_nmt/learn_joint_bpe_and_vocab.py --input /content/train/chv.train_250K_bt_1M.tok /content/train/ru.train_250K_bt_1M.tok -s 10000 -o bpe.codes --write-vocabulary bpe.vocab.chv bpe.vocab.ru
python subword-nmt/subword_nmt/apply_bpe.py -c bpe.codes --vocabulary bpe.vocab.chv --vocabulary-threshold 50 < chv.train_250K_bt_1M.tok > chv.train_250K_bt_1M.bpe
...
python -m sockeye.prepare_data -s chv.train_250K_bt_1M.bpe -t ru.train_250K_bt_1M.bpe -o chvru_data
python -m sockeye.train ...
Даже такой базовый метод сразу дает хороший рост относительно базовой модели: 34,38 BLEU
В дальнейшем было предложено несколько модификаций и альтернатив. Давайте на них тоже посмотрим.
Тегированный обратный перевод
Первый рассматриваемый способ усовершенствования описан в статье
You must be registered for see links
Известно, что если объем переведенного обратно корпуса будет значительно превышать параллельный, то постепенно результаты будут деградировать. Чтобы помочь модели справиться, предлагается разметить все синтетические предложения на языке источнике тегом BT вначале: «BT На улице сегодня очень жарко, но говорят, что будет дождь.». Тогда, говоря по-простому, модель возьмет все самое хорошее от дополнительных данных, а плохого не возьмет.python subword-nmt/subword_nmt/learn_joint_bpe_and_vocab.py --input /content/train/chv.train_250K_tagbt_1M.tok /content/train/ru.train_250K_tagbt_1M.tok -s 10000 -o bpe.codes --write-vocabulary bpe.vocab.chv bpe.vocab.ru
python subword-nmt/subword_nmt/apply_bpe.py -c bpe.codes --vocabulary bpe.vocab.chv --vocabulary-threshold 50 < chv.train_250K_tagbt_1M.tok > chv.train_250K_tagbt_1M.bpe
...
python -m sockeye.prepare_data -s chv.train_250K_tagbt_1M.bpe -t ru.train_250K_tagbt_1M.bpe -o chvru_data
python -m sockeye.train ...
В результате получилось 34,32 BLEU. Примерно столько же, сколько и на классическом обратном переводе. Делаем вывод, что наш синтетический корпус еще не так велик, и можно обратно переводить еще. К сожалению, это по времени затратный процесс, поэтому оставим для дальнейших исследований.
COPY
Очень простой метод, описанный в статье
You must be registered for see links
Гипотеза здесь такая: что если профит обратного перевода не только в обученной обратной модели, но и в самом дополнительном моно-корпусе. Давайте для этого используем данные целевого языка и просто скопируем в данные исходного языка. Получится как бы русско-русская пара. Далее смешаем с параллельным корпусом.python subword-nmt/subword_nmt/learn_joint_bpe_and_vocab.py --input /content/train/chv.train_250K_cp_1M.tok /content/train/ru.train_250K_cp_1M.tok -s 10000 -o bpe.codes --write-vocabulary bpe.vocab.chv bpe.vocab.ru
python subword-nmt/subword_nmt/apply_bpe.py -c bpe.codes --vocabulary bpe.vocab.chv --vocabulary-threshold 50 < chv.train_250K_cp_1M.tok > chv.train_250K_cp_1M.bpe
...
python -m sockeye.prepare_data -s chv.train_250K_cp_1M.bpe -t ru.train_250K_cp_1M.bpe -o chvru_data
python -m sockeye.train ...
Такой метод дает 31,19 BLEU на нашем корпусе.
Дальше метод можно чуть усовершенствовать: при подготовке словаря использовать только параллельные данные, чтобы в словаре исходного языка акцент был на чувашский.
python subword-nmt/subword_nmt/learn_joint_bpe_and_vocab.py --input /content/train/chv.train_250K.tok /content/train/ru.train_250K.tok -s 10000 -o bpe.codes --write-vocabulary bpe.vocab.chv bpe.vocab.ru
python subword-nmt/subword_nmt/apply_bpe.py -c bpe.codes --vocabulary bpe.vocab.chv --vocabulary-threshold 50 < chv.train_250K_cp_1M.tok > chv.train_250K_cp_1M.bpe
...
python -m sockeye.prepare_data -s chv.train_250K_cp_1M.bpe -t ru.train_250K_cp_1M.bpe -o chvru_data
python -m sockeye.train ...
И это действительно работает. Подход дает 32,44 BLEU. Хуже чем обратный перевод, но если нет времени на подготовку синтетических данных, то метод хороший.
Дальше авторы статьи предлагают совместить обратный перевод и COPY так, что модель будет обучена на 2 250 000 парах предложений: 250 тысяч — параллельный корпус, 1 миллион — обратный перевод, и еще 1 миллион — COPY. Таким образом предложения на русском языке из моно-корпуса появятся дважды. К сожалению, здесь улучшения на нашем корпусе не получилось: 33,43 BLEU. Авторы показывали, что ухудшение появляется только на больших корпусах. Однако, и у нас на бедном корпусе тоже видно ухудшение. Можно предположить только, что вручную качественно собранные 250 тысяч предложений — это уже достаточно большой корпус
Word-on-Word
Следующую статью
You must be registered for see links
упомянем лишь в ознакомительных целях. Когда совсем мало параллельных данных, авторы используют всю силу имеющихся словарей. Предлагается предложение из моно-корпуса на целевом языке перевести пословно. Так, для английского «I like to sing» получилось бы «Я любить [пропуск] петь». На практике для нашего корпуса это малоприменимо по двум причинам. Во-первых, русский и чувашский языки имеют богатую морфологию, поэтому в данном случае задача усложняется. Помимо непосредственного перевода слова, его нужно сначала привести в неопределенную форму одного языка, а затем в нужную форму другого. Если этого не сделать, то в словаре будет много отсутствующих слов. Во-вторых, метод скорее применим для параллельных корпусов менее 100 тысяч предложений. В нашем случае обратный перевод, скорее всего, будет давать более качественный перевод.NOISE
Далее рассмотрим еще два способа, описанные в статье Сергея Едунова, который, как он сам
You must be registered for see links
, много сидит в фейсбуке,
You must be registered for see links
В работе есть и другие варианты, но утверждается, что не все они работают одинаково хорошо. Суть NOISE в том, что в синтетические предложения исходного языка добавляется шум 3 видов: 0,1 слов удаляется, 0,1 слов заменяется условным тегом , часть слов перемешивается, но так, что слово не может сдвинуться дальше, чем на 3 позиции. Реализацию можно посмотреть
You must be registered for see links
поиском по фразе «noise for auto-encoding loss». Например, это может выглядеть так: если на вход подать предложение «На улице сегодня очень жарко, но говорят, что будет дождь.», то на выходе можно получить «На сегодня жарко, но говорят, дождь что будет.»python subword-nmt/subword_nmt/learn_joint_bpe_and_vocab.py --input /content/train/chv.train_250K_noise_1M.tok /content/train/ru.train_250K_1M.tok -s 10000 -o bpe.codes --write-vocabulary bpe.vocab.chv bpe.vocab.ru
python subword-nmt/subword_nmt/apply_bpe.py -c bpe.codes --vocabulary bpe.vocab.chv --vocabulary-threshold 50 < chv.train_250K_noise_1M.tok > chv.train_250K_noise_1M.bpe
...
python -m sockeye.prepare_data -s chv.train_250K_noise_1M.bpe -t ru.train_250K_1M.bpe -o chvru_data
python -m sockeye.train ...
В итоге получается 33,25 BLEU. На нашем корпусе проигрывает классическому обратному переводу. Видимо, подразумеваемся, что синтетический корпус должен быть кратно больше.
SAMPLING
Суть второго метода из статьи в том, чтобы иначе сделать обратный перевод, так как он неидеален. Предлагается брать не лучший по мнению модели перевод, а некоторый случайный из предложенных моделью. Получившиеся предложения будут хуже, но более разнообразными, и дадут более богатый сигнал для обучения. Технически это решается добавлением одного параметра в команду перевода. Было
python -m sockeye.translate --input ru.train_1M.bpe -m ruchv_model --output chv.train_1M.bpe
Стало
python -m sockeye.translate --input ru.train_1M.bpe -m ruchv_model --output chv.train_1M.bpe --sample
Результат: 32,67 BLEU. И это тоже хуже, чем классический обратный перевод. Гипотеза, почему так, примерно такая же, как и с методом выше.
Совмещение с Переносом Знания
В заключении обсудим еще один важный вопрос: можно ли совместить обратный перевод и перенос знания, про который я писал в прошлой статье.
В качестве основы для метода переноса знаний на этот раз используем 3 миллиона предложений казахско-русского корпуса: те, для которых коэффициент соответствия (третья колонка) больше 1. Дочерний корпус — те же 250 тысяч предложений. Для этого объема данных получили 35,24 BLEU. Да-да, это чуть лучше, чем результаты обратного перевода.
По всем законам жанра, дальше должна быть история про то, что комбинация двух методов дала еще более высокий результат. Но нет. Эксперименты с использованием разных вариантов словарей (с добавлением данных обратного перевода и без) и разных дочерних корпусов для дообучения (опять же с добавлением данных обратного перевода и без) показали себя либо чуть хуже, либо значительно хуже. Вплоть до того, что если и в подготовке общего словаря, и в качестве дочернего использовать корпус в 250 тысяч параллельных предложений вместе с 1 миллионом обратно-переведенных, то получим весьма заметное ухудшение 24,73 BLEU. Причем сильнее всего на результат влияет общий словарь. Поле для дальнейших исследований.
Выводы
Итого в результате экспериментов пришли к тому, что классический обратный перевод дает наилучший результат. Равно как и метод переноса знаний. Использовать можно любой из них, в зависимости от имеющихся данных. COPY подходит, когда нет времени и ресурсов переводить обратно, помогает быстро улучшить базовую модель. Остальные методы дают результаты чуть хуже, их надо применять аккуратно, сравнивать на вашем корпусе.