Как стать автором
Обновить

Как создать переводчик, который переводит лучше, чем Google Translate

Время на прочтение8 мин
Количество просмотров30K

Помню, как еще в школе на Basic я писал программу-переводчик. И это было то время, когда ты сам составлял словарь, зашивал перевод каждого слова, а затем разбивал строки на слова и переводил каждое слово в отдельности. В то время я, конечно же, не мог и представить, как сильно продвинутся технологии, и программы-переводчики станут в основе использовать механизмы глубокого обучения с архитектурой трансформера и блоками внимания.

В этот раз я решил окунуться немного в прошлое и сделать то, что хорошо сделать тогда у меня не получилось.

В современных задачах системы перевода нужны достаточно часто, а специалисту, занимающемуся машинным обучением, возможно чаще, чем в других видах деятельности. Потребность в переводчике стала мне необходима, и я решил прибегнуть к использованию облачных решений, в частности Google Translate.

Я программирую на python, и в python есть отличная библиотека translators. Данная библиотека позволяет использовать множество различных сервисов по переводу. Причем абсолютно бесплатно. Однако, все же данное решение не идеально по нескольким причинам:

  1. Перевод выполняется достаточно долго.

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

  3. Качество перевода не самое высокое, как я это проверял, я расскажу в своей статье ниже.

Конечно же, не стоит изобретать велосипед, поэтому я стал использовать отличное готовое решение Argos Translate. Это open-source бесплатное решение для машинного перевода. Поддерживается огромное количество языков. Может работать в разных режимах: и как десктоп приложение, и как веб приложение, и как библиотека к python.

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

Автор проекта Argos Translate поставляет свой скрипт обучения, доступный всем желающим. Для обучения своей модели я взял его за основу, однако в процессе несколько его видоизменил. Далее я покажу основные составляющие процесса обучения модели машинного перевода и разобью их на составные части для лучшего понимания.

Этап 1: Подготовка данных для обучения

Для обучения модели машинного перевода необходимо большое количество параллельных корпусов текстов, к счастью, в сети есть очень большое количество параллельных корпусов текста, за основу я взял датасеты с сайта https://opus.nlpl.eu/

В качестве данных для обучения я использовал список корпусов текстов: ada83.en-ru, bible-uedin.en-ru, Books.en-ru, CCMatrix.en-ru, ELRC_2922.en-ru, EUbookshop.en-ru, GlobalVoices.en-ru, GNOME.en-ru, infopankki.en-ru, KDE4.en-ru, MultiUN.en-ru, News-Commentary.en-ru, OpenSubtitles.en-ru, ParaCrawl.en-ru, PHP.en-ru, QED.en-ru, Tanzil.en-ru, Tatoeba.en-ru, TED2013.en-ru, TED2020.en-ru, tico-19.en-ru, TildeMODEL.en-ru, Ubuntu.en-ru, UN.en-ru, WikiMatrix.en-ru, wikimedia.en-ru, WMT-News.en-ru

В общем итоге у меня получилось собрать тренировочный корпус размером более 80 млн пар предложений.

Для последующей тренировки все данные нам необходимо собрать в файлы:

  1. src-train.txt - все исходные предложения на русском языке,

  2. tgt-train.txt - все переводы предложений на английском языке,

  3. all.txt - все предложения корпуса на двух языках, данный файл необходим для генерации общего словаря,

  4. src-val.txt - 2000 предложений для валидации,

  5. tgt-val.txt - 2000 переводов предложений для валидации.

Этап 2: Генерация словаря токенов

В качестве основного движка модели используется библиотека нейронного машинного перевода OpenNMT-py с открытым исходным кодом на pytorch.

Библиотека для тренировки использует yml конфигурационные файлы, в которых описываются параметры тренировки модели.

Я использовал стандартный конфиг из репозитория argos-train: https://github.com/argosopentech/argos-train/blob/master/config.yml

В моделях перевода argos translate используется общий словарь для обоих языков.

Для генерации словаря, используется следующий скрипт, в нем берутся первые 10 млн случайных предложений из корпуса, и генерируется из них словарь размером в 50 тысяч токенов.

#!/bin/bash
spm_train --input=run/split_data/all.txt --model_prefix=run/sentencepiece --vocab_size=50000 --character_coverage=0.9995 --input_sentence_size=10000000 --shuffle_input_sentence=true --split_digits

В качестве генерации словаря используется очень популярный токенизатор SentencePiece, который использует в качестве словаря наиболее частотные единицы подслов.

Для перевода модели токенизатора SentencePiece в словарь модели используется команда:

#!/bin/bash
onmt_build_vocab -config config.yml -n_sample -1

и часть конфига отвечающая за формирование словаря:

#config.yml

## Where the samples will be written
save_data: run/opennmt_data
## Where the vocab(s) will be written
src_vocab: run/opennmt_data/openmt.vocab
tgt_vocab: run/opennmt_data/openmt.vocab


# Should match the vocab size for SentencePiece
# https://forum.opennmt.net/t/opennmt-py-error-when-training-with-large-amount-of-data/4310/12?u=argosopentech
src_vocab_size: 50000
tgt_vocab_size: 50000

share_vocab: True

# Corpus opts:
data:
    corpus_1:
        path_src: run/split_data/src-train.txt
        path_tgt: run/split_data/tgt-train.txt
        transforms: [sentencepiece, filtertoolong]
    valid:
        path_src: run/split_data/src-val.txt
        path_tgt: run/split_data/tgt-val.txt
        transforms: [sentencepiece, filtertoolong]


### Transform related opts:
#### https://opennmt.net/OpenNMT-py/FAQ.html#how-do-i-use-the-transformer-model
#### Subword
src_subword_model: run/sentencepiece.model
tgt_subword_model: run/sentencepiece.model
src_subword_nbest: 1
src_subword_alpha: 0.0
tgt_subword_nbest: 1
tgt_subword_alpha: 0.0
#### Filter
src_seq_length: 150
tgt_seq_length: 150

Этап 3: Тренировка модели

В качестве архитектуры обучаемой модели используется архитектура трансформера, состоящая из кодировщика и декодировщика, каждый размером 6 слоев. 8 голов самовнимания. Размером эмбединга – 512, и размером скрытого связующего состояния между кодировщиком и декодировщиком – 2048.

#config.yml

# Model
encoder_type: transformer
decoder_type: transformer
position_encoding: true
enc_layers: 6
dec_layers: 6
heads: 8
rnn_size: 512
word_vec_size: 512
transformer_ff: 2048
dropout_steps: [0]
dropout: [0.1]
attention_dropout: [0.1]

В качестве оптимизатора используется Adam, в начале обучения в течение первых 8000 шагов используется прогрев модели, с последующим снижением скорости обучения.

Для запуска процесса обучения используется команда:

#!/bin/bash
onmt_train -config config.yml

Обучение моей модели длилось 100 000 шагов и в общем случае занимает от 2 до 5 дней, в зависимости от конфигурации оборудования.

Этап 4: Тестирование качества модели

После обучения нам необходимо проверить, насколько хорошо переводит наша модель.

Для оценки качества модели я использовал отдельный параллельный корпус "Yandex Translate corpus 1m version 1.3" размером в 1 млн пар предложений, в качестве метрики я использовал метрику BLEU Score.

Я использовал следующий скрипт для тестирования модели

#!/bin/bash
#тестирование модели по метрике BLEU
#https://opennmt.net/OpenNMT-py/examples/Translation.html?highlight=bleu

echo "Step 001 Токенизируем наш корпус тестирования" 

spm_encode --model=run/sentencepiece.model \
     < argos-train/csv/test_crps/src-test.txt \
     > argos-train/csv/test_crps/src-test.txt.sp
spm_encode --model=run/sentencepiece.model \
     < argos-train/csv/test_crps/tgt-test.txt \
     > argos-train/csv/test_crps/tgt-test.txt.sp

echo "Step 002 Переведем наш корпус с помощью модели"

for checkpoint in run/openmt.model_step*.pt; do
    echo "# Translating with checkpoint $checkpoint"
    base=$(basename $checkpoint)
    onmt_translate \
        -gpu 0 \
        -batch_size 2048 -batch_type tokens \
        -beam_size 5 \
        -model $checkpoint \
        -src argos-train/csv/test_crps/src-test.txt.sp \
        -tgt argos-train/csv/test_crps/tgt-test.txt.sp \
        -output run/wmt/test.ru.hyp_${base%.*}.sp
done

echo "Step 003 Декодируем перевод из токенов обратно в текст"

for checkpoint in run/openmt.model_step*.pt; do
    base=$(basename $checkpoint)
    spm_decode \
        -model=run/sentencepiece.model \
        -input_format=piece \
        < run/wmt/test.ru.hyp_${base%.*}.sp \
        > run/wmt/test.ru.hyp_${base%.*}
done

echo "Step 004 Сравним два корпуса по оценке BLEU Score"

for checkpoint in run/openmt.model_step*.pt; do
    echo "$checkpoint"
    base=$(basename $checkpoint)
    sacrebleu argos-train/csv/test_crps/tgt-test.txt < run/wmt/test.ru.hyp_${base%.*}
done

echo "Step End"

По итогам обучения моя модель получила метрику BLEU: 21.6

Для сравнения с другим переводчиком, я перевел 1 млн предложений из тестового корпуса Yandex через библиотеку translators, переводчиком Google Translate.

Отдельно сравнил качество с помощью команды:

#!/bin/bash
sacrebleu argos-train/csv/test_crps/tgt-test.txt < argos-train/csv/test_crps/tgt-test_google.txt

В итоге получил метрику BLEU Score полученного перевода бесплатного Google переводчика BLEU: 18.7

Что означает, что полученная мною модель переводит с русского на английский лучше, чем бесплатный Google Translate.

Этап 5: Упаковываем модель в Argos Translate

Для упаковки модели в формат Argos Translate, необходимо выполнить ряд преобразований.

  1. Конвертация модели из checkpoint:

    #!/bin/bash
    ./../OpenNMT-py/tools/average_models.py -m run/openmt.model_step_100000.pt run/openmt.model_step_100000.pt -o run/averaged.pt
  2. Квантизация модели:

#!/bin/bash
ct2-opennmt-py-converter --model_path run/averaged.pt --output_dir run/model --quantization int8
  1. Конвертация модели в формат argos translate:

    #!/usr/bin/env python3
    
    from pathlib import Path
    import json
    import subprocess
    import shutil
    import sys
    
    import argostrain
    from argostrain.dataset import *
    from argostrain import data
    from argostrain import opennmtutils
    from argostrain import settings
    
    import stanza
    
    from_code = input("From code (ISO 639): ")
    to_code = input("To code (ISO 639): ")
    from_name = input("From name: ")
    to_name = input("To name: ")
    version = input("Version: ")
    package_version = version
    argos_version = "1.5"
    
    package_version_code = package_version.replace(".", "_")
    model_dir = f"translate-{from_code}_{to_code}-{package_version_code}"
    model_path = Path("run") / model_dir
    
    subprocess.run(["mkdir", model_path])
    
    subprocess.run(["cp", "-r", "run/model", model_path])
    
    subprocess.run(["cp", "run/sentencepiece.model", model_path])
    
    # Include a Stanza sentence boundary detection model
    stanza_model_located = False
    stanza_lang_code = from_code
    while not stanza_model_located:
        try:
            stanza.download(stanza_lang_code, dir="run/stanza", processors="tokenize")
            stanza_model_located = True
        except:
            print(f"Could not locate stanza model for lang {stanza_lang_code}")
            print(
                "Enter the code of a different language to attempt to use its stanza model."
            )
            print(
                "This will work best for with a similar language to the one you are attempting to translate."
            )
            print(
                "This will require manually editing the Stanza package in the finished model to change its code"
            )
            stanza_lang_code = input("Stanza language code (ISO 639): ")
    
    
    subprocess.run(["cp", "-r", "run/stanza", model_path])
    
    subprocess.run(["cp", "run/metadata.json", model_path])
    subprocess.run(["cp", "run/README.md", model_path])
    
    package_path = (
        Path("run") / f"translate-{from_code}_{to_code}-{package_version_code}.argosmodel"
    )
    
    shutil.make_archive(model_dir, "zip", root_dir="run", base_dir=model_dir)
    subprocess.run(["mv", model_dir + ".zip", package_path])
    
    # Make .argoscheckpoint zip
    
    latest_checkpoint = opennmtutils.get_checkpoints()[-1]
    print(latest_checkpoint)
    print(latest_checkpoint.name)
    print(latest_checkpoint.num)
    
    print(f"Package saved to {str(package_path.resolve())}")

Далее мы получаем готовую модель файла в виде "translate-en_ru-1_7.argosmodel", которую можно подгрузить и использовать в продукте Argos Translate.

В качестве заключения

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

Кроме того, я показал, что существуют бесплатные продукты вроде Argos Translate, с помощью которых можно переводить тексты, не прибегая к облачным вычислителям.

Так как продукт Argos Translate является бесплатным, то я поделился с сообществом своей обученной моделью в комьюнити продукта. Разработчики Argos Translate приняли мою модель Russian - English и включили в основной репозиторий моделей машинного перевода в качестве основной модели под версией 1.7.

Если у вас есть идеи, как еще можно улучшить качество перевода данной модели, пишите их в комментариях, и мы вместе сможем еще сильнее улучшить данную модель!

Эксперт по Машинному обучению в IT-компании Lad.

Теги:
Хабы:
Всего голосов 61: ↑61 и ↓0+61
Комментарии48

Публикации

Истории

Работа

Data Scientist
78 вакансий

Ближайшие события

7 – 8 ноября
Конференция byteoilgas_conf 2024
МоскваОнлайн
7 – 8 ноября
Конференция «Матемаркетинг»
МоскваОнлайн
15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань