В сервисе Яндекс Переводчик мы поддерживаем перевод между 102 языками. Наша цель — обеспечивать качественный перевод для самых разных типов данных: текстов, документов, HTML, изображений и видео. Сегодня обсудим ключевой компонент для обучения моделей машинного перевода — данные для обучения.
Современные нейросетевые подходы очень требовательны как к объёму данных в обучении, так и к их качеству. Для получения хорошей переводной модели требуются сотни миллионов, а в идеале миллиарды параллельных предложений (пар из предложения и его перевода). Возникает вопрос: откуда их взять и что это за данные?
В этой статье я расскажу о том, как из общедоступных текстов интернета в 100 ПБ найти терабайты суперчистых данных с переводами между любыми языками. Вы узнаете, почему эта задача требует обучения больше десятка различных вспомогательных ML‑моделей. А ещё коротко подсвечу, какое место в этом процессе занимает наша YandexGPT и что это за зверь такой — YandexGPT‑MT.
Как учатся модели машинного перевода
Стандартный подход такой: берём предложения и их переводы (пары параллельных предложений), берём хорошо работающую архитектуру нейросети (обычно это transformer: либо encoder‑decoder transformer, либо decoder‑only). Далее мы учим нашу модель предсказывать каждый следующий токен (обычно это слово или часть слова).
В процессе обучения есть много тонкостей, но эта статья будет акцентироваться именно на данных для обучения моделей.
Типы текстов для обучения
В идеале мы бы хотели обучать модели только на очень чистых и разнообразных параллельных данных. К сожалению, это не всегда возможно: многие языки в целом имеют малое количество носителей. Кроме того, языки представлены в интернете непропорционально количеству носителей (Resource: Indicators on the Presence of Languages in Internet, 2022). Также часто возникают проблемы и с качеством данных: в интернете много плохих переводов — как сгенерированных системами машинного перевода, так и написанных людьми.
Ниже мы обсудим способы добычи параллельных данных, однако стоит сразу подумать: что, если мы не сможем найти достаточного количества данных? Есть стандартный способ обойти эту проблему: давайте использовать синтетические данные вместо настоящих переводов. Пусть мы хотим обучить модель перевода с английского на казахский. Нам будут нужны две компоненты:
Данные на казахском языке. Данные на конкретном языке найти в интернете не так сложно: в простейшем случае достаточно из всех страниц в интернете выделить те, которые детектор языка определил как страницы на казахском. Кроме того, эти данные часто более разнообразные и качественные, чем данные, для которых в интернете мы найдём переводы. Причина проста: чаще всего переводят свои страницы сайты с товарами или услугами, что создаёт сильный доменный сдвиг.
Модель перевода с казахского на английский.
Данный подход называется Back Translation (Understanding Back‑Translation at Scale). В результате у нас получится корпус параллельных текстов, у которого тексты на казахском языке — хорошие, так как они состоят из реальных текстов, существующих в интернете. Но в среднем не очень хорошие английские тексты, так как они сгенерированы моделью, которая может генерировать тексты с ошибками, а также может генерировать плохо согласованные тексты. Также переводы могут быть неточными, потому что модели машинного перевода могут ошибаться, не полностью сохраняя смысл оригинального текста. Как итог, такой корпус обычно улучшает естественность (гладкость) переводов, так как модель учится восстанавливать естественно звучащие тексты из не очень качественных. К сожалению, это также обычно снижает точность переводов, так как мы обучаем модель на не полностью параллельных данных.
Но и эту проблему можно сильно нивелировать, используя теггирование корпусов (Tagged Back‑Translation): подобный подход работает во многих задачах обработки естественного языка (NLP). Идея довольно простая: давайте разные корпусы помечать разными тегами. Тегом можно выбрать целое число. К примеру, пусть у нас два корпуса: корпус хороших параллельных данных (пометим его тегом «0») и корпус синтетики (back translation, пометим тегом «1»). На стороне модели учитывать тег можно разными способами. Один из самых простых — это использовать обучаемые для каждого тега эмбеддинги, которые мы будем добавлять к эмбеддингам токенов в энкодере.
При этом при инференсе модели мы будем подавать тег самого хорошего корпуса — в примере выше это будет тег «0». Данный процесс можно повторять несколько раз, переобучая модели перевода и перегенирируя корпусы синтетики, это также даёт буст качества.
Если резюмировать, то синтетика может значительно повышать качество перевода, особенно если параллельных данных слишком мало. Мы регулярно применяем этот подход, когда добавляем в Переводчик языки народов России, для которых нет достаточного количества общедоступных данных. Например, так было в случае с чувашским языком, поддержке которого была посвящена отдельная статья.
Источники данных
Давайте теперь поговорим о том, где же найти настоящие параллельные тексты. Первое, что может прийти в голову, это доменно‑специфичные тексты — данные из таких корпусов, как OpenSubtitles, корпус ООН, Библия и т. д.
Бóльшую часть таких корпусов можно найти на сайте opus.nlpl.eu. Их главное ограничение — они часто небольшие и грязные: содержат много непараллельных или частично параллельных текстов (как, например, корпус OpenSubtitles). Также у них часто есть смещение в одну тематику. Например, OpenSubtitles состоит из диалогов фильмов и сериалов, а корпус ООН состоит из канцеляризмов. Если же мы хотим построить модель, которая будет переводить любой текст, то таких корпусов будет недостаточно.
И тут на помощь приходят подходы, в основе которых лежит обработка интернета.
Обработка интернета как источник данных для обучения
Итак, мы понимаем, что нам надо каким‑то образом найти параллельные тексты из всего интернета. Первая проблема, которая сразу приходит в голову, — это то, что интернет очень большой. ОЧЕНЬ большой: обычно придётся обработать от 10 до 100 ПБ (петабайт) данных. На масштабах данных, приближающихся к долям экзобайта (1 экзобайт = 1 миллион терабайт), требования к размеру вычислительного кластера становятся огромными. Но у Яндекса для этого как раз есть необходимая инфраструктура, в основе которой лежит YTsaurus.
Но где же взять тексты из интернета? Конечно, можно вручную обойти весь интернет и скачать каждую страницу, но это очень долго и дорого. Обычно используются два подхода:
Использовать уже имеющиеся у компании тексты из интернета. Если ваша компания — Яндекс или Google, то у вас они, скорее всего, уже есть.
Скачивать готовый открытый датасет из проекта CommonCrawl. Этот проект содержит данные с 2009 года. Данные можно скачать с Amazon S3, где они лежат в открытом доступе. Объём данных очень большой — только за период с 2018-го по 2023 год там 14,5 ПБ данных.
Как будем искать параллельные данные
Тут на помощь приходят различные эвристики для нахождения кандидатов на параллельные документы:
Рассмотрим интернет как граф, где рёбра — это ссылки между документами. Кандидаты на параллельные документы — это два документа на разных языках, между которыми есть ссылка. Эта эвристика работает хорошо, так как часто перевод ссылается на оригинал, а некоторые многоязычные сайты содержат ссылки для быстрого перехода на нужный пользователю язык. Среди открытых реализаций данного подхода есть проект Bitextor, который реализует среди прочих и эту ссылочную эвристику.
На многих сайтах одинаковые страницы на разных языках имеют URL вида
some_site.tld/{language_code}/path_to_page
, где код языка может находиться в разных частях URL — от названия домена до CGI‑параметров. Если токенизировать URL и удалить все токены с названиями языков, можно запустить операцию reduce по нормализованному URL. Кандидаты на параллельные документы в этом подходе — документы с разным языком, но одинаковым нормализованным URL.Если первые две эвристики не работают, то можно подключить более тяжёлый подход: искать редкие n‑граммы (а лучше их хеши), а также переводы этих n‑грамм. Далее, аналогично второму подходу, запускаем reduce‑операцию по хешу от редкой n‑граммы.
Что общего между этими подходами? Мы сначала ищем кандидатов на параллельные документы, а затем находим параллельные предложения внутри найденных пар документов. Можно ли иначе? Да!
В статьях WikiMatrix (2019) и CCMatrix (2020) был предложен обратный подход: давайте вначале искать параллельные предложения! Предложенный в этих статьях пайплайн выглядел так:
Берём все документы, парсим их и нарезаем на предложения.
Превращаем каждое предложение в языконезависимый эмбеддинг с помощью модели LASER (подробнее про LASER будет ниже).
С помощью библиотеки faiss библиотека для поиска ANN — approximate nearest neighbors) ищем предложения с одинаковым смыслом на разных языках.
Оба этих подхода отлично работают. И хотя большая часть корпусов в команде Яндекс Переводчика получена первым способом (сначала ищем пары документов), при построении отдельных доменно‑специфичных корпусов мы используем второй способ, поскольку у него есть важная особенность: он не требует дополнительной информации о документах и может находить больше данных, пусть и тратя заметно больше вычислительных ресурсов.
Препроцессинг документов
Итак, мы обсудили, как находить параллельные документы. Однако сырые HTML‑документы (а также PDF и другие форматы) нужно сначала обработать, приведя их к чистому plain text.
Обычно препроцессинг документов состоит из следующих этапов:
Определение кодировки и перекодирование в UTF-8.
Извлечение основного контента из документа. Обычно мы хотим не извлекать различный бойлерплейт (меню, хедеры, футеры, рекламу). Эта задача не такая простая, как может показаться. В статьях для этого часто используется библиотека trafilatura, однако мы используем для извлечения контента специально обученные ML‑классификаторы, что дополнительно повышает качество извлечения текста со страницы.
Определение языка страницы. Для этого часто используют модели CLD2 (Google), CLD3 (тоже Google), NLLB‑LID. Также мы используем классификаторы языков, обученные внутри. Это один из ключевых этапов: низкая полнота детектирования языка на нужном для нас языке не позволит создать достаточно большой корпус; низкая же точность работы модели может сильно увеличить время работы пайплайна сбора данных.
Разбиение текста на предложения. Обычно это делается с помощью ещё одной, как правило, достаточно лёгкой модели. Мы используем ещё одну кастомную модель для этой задачи.
Кроме извлечения основного контента со страницы, зачастую хочется отсеять плохие документы. Один из самых простых способов — использовать чёрные списки доменов. Например, в научных работах для фильтрации иногда используется этот репозиторий. Этот способ позволяет отфильтровать разные некачественные источники, например порносайты.
Однако такие списки обладают зачастую не очень хорошей полнотой и свежестью. Чтобы дальше очистить корпус от плохих документов, нужно обучать отдельные классификаторы. Эти классификаторы могут значительно повысить качество корпуса, отфильтровывая сомнительные документы. Примеры классификаторов, которые используем мы:
Классификатор узких плохих тематик, например порнотекстов. Порно часто попадает в параллельные корпусы, так как на порносайтах обычно есть автоматический (часто очень низкокачественный) перевод на все языки. Это может привести к тому, что на отдельных запросах (особенно если ничего похожего на запрос не было в обучении), сеть может проигнорировать текст запроса от пользователя и сгенерировать предложения, характерные для порносайтов.
Классификатор плохих текстов. Его цель — отсеять тексты, которые не помогут увеличить качество модели. Это могут быть как просто некачественные или неестественные тексты, так и страницы с перечислением номеров телефонов или гигантскими списками компаний с их ИНН.
Обычно пайплайн обучения подобных классификаторов достаточно стандартный:
Собираем набор критериев: что считаем плохим, что хорошим. Превращаем это в инструкцию для асессоров.
Делаем несколько итераций разметки, улучшая инструкцию, добавляя экзамен и проверочные задания.
Обучаем классификаторы на основе этой разметки. Чаще всего это классификаторы на основе YandexGPT разных размеров.
Проверяем на реальных данных: фильтруем корпус классификатором, обучаем модель, смотрим на метрики обученной модели. Если прироста качества нет, проводим разбор ошибок модели, повторяем пункты 1–3.
Дистиллируем в модель меньшего размера, чтобы классификатор было удобно применять на действительно больших данных.
Выравнивание пар документов
На предыдущих этапах мы нашли кандидатов на параллельные документы, а также подготовили их, превратив в набор последовательных предложений. Следующий этап — взять каждую пару документов и попытаться найти параллельные предложения.
Допущения, которые часто делаются на этом этапе:
Каждое предложение из документов можно использовать только один раз.
В левом и правом документе могут быть предложения, которые не имеют пар.
Монотонность выравнивания: в выровненных текстах предложения идут в том же порядке, что и в исходных документах. Это предположение не всегда верно, но позволяет использовать более эффективные алгоритмы.
Эту задачу можно рассматривать как задачу выравнивания последовательностей Sequence Alignment. Обычно она решается с помощью динамического программирования. Тривиальное решение требует для двух документов памяти и вычислений, где и — количество предложений в первом и втором документе. Часто , и это даёт нам квадратичный от алгоритм по памяти и вычислениям. Память здесь легко оптимизировать: можно хранить не всю матрицу динамического программирования, а только две последние диагонали. Таким образом, вместо квадратичной памяти нам потребуется линейная память .
Если оптимизация памяти относительно проста, то уменьшить количество вычислений сложнее. Одним из подходов является использование Beam Search — проверять не все пути в таблице, а только наиболее перспективные. В некоторых случаях это не позволит найти самое оптимальное решение, но значительно сократит количество вычислений, приводя к асимптотике , где — beam size алгоритма.
Для работы данного алгоритма Sequence Alignment нужна функция, которая бы оценивала схожесть двух предложений на разных языках. Существует два наиболее распространённых подхода к этой проблеме:
На основе фразовых таблиц / статистического машинного перевода. Имея таблицу соответствий и частотности N‑грамм на двух языках, можно получить вероятность того, что одно предложение является переводом другого. Один из примеров такой модели — IBM Model 1. Преимущество этого подхода в том, что можно балансировать качество и скорость: уменьшая размер фразовой модели (в крайнем случае оставляя только 1-граммы), можно достичь очень высокой скорости работы. А это очень полезное свойство, если мы хотим обработать миллиарды пар документов. Известная реализация из этого семейства — библиотека hunalign.
На основе нейросетевых подходов. Самые известные модели для этой задачи — LASER и LaBSE. Конечно, можно было бы использовать обычные модели перевода для оценки схожести предложений или даже метрики качества перевода, такие как BLEURT. Но вызывать инференс модели, которая принимает сразу два предложения на вход, слишком дорого: потребуется в худшем случае запусков модели скоринга. Самое простое решение — использовать модели, которые могут выдать языконезависимый эмбеддинг предложения. В этом случае вычисление схожести двух предложений становится очень простым: достаточно считать косинусное произведение двух эмбеддингов. LASER и LaBSE используются в таких библиотеках для выравнивания, как VecAlign, BertAlign, SentAlign.
В Яндекс Переводчике мы используем смесь этих двух подходов. На первом этапе мы используем методы на основе фразовых таблиц, что обеспечивает высокую скорость обработки. На втором этапе мы применяем уже более тяжёлые модели, пытаясь найти максимальное количество хороших пар предложений. Такой гибридный метод позволяет без снижения качества уменьшить затрачиваемое количество GPU‑месяцев при запуске пайплайна на 1–2 порядка.
Что за нейросетевые модели используются для выравнивания документов? Расскажу подробнее про LASER и LaBSE.
LASER
LASER был представлен в 2019 году. LASER1 поддерживал 93 языка, LASER3 был представлен в статье NLLB: No Language Left Behind и добавил поддержку ещё 148 языков.
LASER1/LASER2 представляет собой 5-слойную BiLSTM с max‑pooling на выходе с последнего слоя. Дополнительные энкодеры в LASER3 — это 12-слойные трансформеры с внутренней размерностью 1024, 4 головами внимания, что в сумме даёт примерно 250 млн параметров.
LaBSE
Модель LaBSE была представлена в статье Language-agnostic BERT Sentence Embedding от Google в 2020 году. Модель поддерживает 109 языков и представляет собой BERT Base модель (12 слоёв, 12 голов, 768 hidden size, 471 млн параметров). После предобучения модель обучалась с помощью Additive Margin Softmax на большом параллельном корпусе из 6 миллиардов предложений. Также существуют облегчённые версии этой модели, представленные в статье LEALLA.
Фильтрация пар предложений
Ура! На выходе предыдущего этапа мы получили для каждой пары документов набор параллельных предложений. Но радоваться пока рано: среднее качество найденных пар предложений пока достаточно низкое. На помощь придёт набор дополнительных классификаторов для оценки качества найденных пар предложений:
Проверка языка предложений. Важно убедиться, что язык предложения соответствует языку документа, который был проставлен ранее. Дело в том, что отдельные документы могут содержать тексты сразу на нескольких языках. Особенно важно перепроверять язык предложения, если использовались LaBSE/LASER, которые не учитывают язык предложения.
-
Эвристики для нахождения явно непараллельных предложений. Например:
Отношение длин двух предложений не должно отличаться более чем в X раз (обычно X = 2–3).
Оба предложения содержат одинаковый набор чисел. Это позволяет отфильтровать непараллельные предложения вида «User 123» ⇒ «Пользователь 187».
Проверка естественности и грамотности предложений. Нужно убедиться, что предложения являются естественными и грамматически правильными.
Дополнительная проверка параллельности предложений. Несмотря на то, что уже был применён классификатор параллельности (например, LaBSE), полезно применить ещё один, более точный классификатор. На этом этапе мы можем применить более тяжёлый классификатор, который будет принимать на вход сразу два предложения. Один из вариантов — использовать QE‑метрику (QE — сокращение от Quality Estimation), например Comet‑Kiwi или BLEURT. QE‑метрики не требуют эталонного перевода, которого у нас нет. Однако применять эти метрики нужно с большой осторожностью: если использовать метрику для фильтрации, на неё нельзя будет ориентироваться при оценке качества модели.
Эти шаги помогут значительно улучшить качество параллельных предложений и создать более точный и надёжный корпус для обучения моделей машинного перевода.
Сбор параллельных параграфов
Современные системы машинного перевода в большинстве случаев достаточно хорошо справляются с переводом отдельных предложений. Кроме того, для перевода предложений существует множество открытых параллельных корпусов и неплохо работающих метрик качества.
Типичный пайплайн перевода в продакшене до недавнего времени состоял из двух этапов: разбиения текста на предложения и перевода предложений независимо. Однако такой подход не позволяет генерировать согласованные тексты: модель не видит ни исходный текст целиком, ни переводы предыдущих предложений. Это приводит к различным ошибкам: от несогласованного перевода именованных сущностей (названий компаний, имён людей и других, которые могут быть записаны немного по‑разному в каждом предложении) до ошибок, затрудняющих понимание смысла исходного текста.
Можно ли обучать модель на парах предложений, а применять модель на параграфах? Можно, но это будет являться для модели out‑of‑domain‑применением и будет приводить к различным ошибкам, таким как недоперевод: из трёх поданных предложений модель может перевести только первое. Можно придумать простой фикс этой проблемы: обучать модель на синтетических данных или на склейках случайных предложений. Хотя этот подход и работает на практике, он даёт заметно худшее качество по сравнению с обучением на реальных параллельных параграфах.
К счастью, у нас почти всё готово, чтобы на выходе из нашего пайплайна собрать параллельные параграфы. Для этого необходимо передать дополнительную информацию в блок сборки параграфов. Но для начала нужно определиться, что же такое параграф:
Параграфом можно считать реальный параграф в документе: мы можем искать содержимое тега
<p>
и других тегов, которые могут использоваться как тег<p>
, например<div>
.Важно отсутствие пропусков в найденных сегментах: если в параграфе из пяти предложений выровнены только первое и пятое, такие данные могут быть не очень полезны для обучения.
Слишком короткие выровненные куски, например из одного предложения, могут быть не так интересны.
Таким образом, для сбора параллельных документов нам потребуется:
информация о XPath предложения;
абсолютный индекс каждого предложения на выходе из препроцессинга.
Сам алгоритм сбора параграфов на основе этих данных достаточно тривиальный: просто последовательно идём по парам выровненных параграфов, проверяя XPath и индекс предложения.
Применение фильтраций на уровне корпуса
Пока мы не применяли фильтрации на уровне всего корпуса, однако такие фильтрации могут значительно улучшить качество моделей.
Основные подходы, которые используются на уровне целого корпуса:
Дедупликация позволяет удалить полностью или частично совпадающие предложения/документы. Самый простой вариант — применить Minhash+LSH для поиска похожих документов, что может заметно повысить качество модели, обученной на дедуплицированном корпусе. Однако можно использовать и более сложные методы (часто в дополнении к Minhash+LSH), такие как суффиксный массив или методы, основанные на K‑Means: SemDeDup или D4. В настоящий момент мы в Яндекс Переводчике используем Minhash+LSH, так как этот метод просто использовать и он вычищает большую часть частичных дубликатов.
Перебалансировка корпуса по тематикам, длинам, доменам или любой другой характеристике. Несмотря на простоту, эти методы очень эффективны, хотя и требуют тщательной настройки параметров перевзвешивания.
Оценка качества корпусов
Но как понять, что собранный нами корпус хороший или плохой? Самый простой и правильный способ — обучить модель на новом корпусе и посмотреть на метрики. Это может занять много времени и требовать большого количества GPU, но при этом мы будем измерять именно целевые бизнес‑метрики, что хорошо. Однако часто хочется ускорить цикл экспериментов, и здесь на помощь приходят прокси‑метрики для отдельных этапов сбора данных. Например, для оценки качества нашего выравнивания предложений мы создали свой собственный тестовый набор и способ оценки точности выравнивания.
Использование данных в YandexGPT
Параллельные данные используются не только для непосредственного обучения моделей машинного перевода. Согласно нашим экспериментам, добавление таких данных в претрейн YandexGPT (напомню, что это самый ресурсоёмкий этап обучения модели) улучшает качество языковых моделей в задачах NLU (Natural Language Understanding), а также помогает модели лучше использовать данные из других языков. К похожему выводу пришёл Google, явно добавив параллельные данные в свою LLM (PaLM 2 Technical Report), даже несмотря на то, что они ранее обнаружили, что в «монолингвальных» данных содержится достаточное количество переводных данных (Searching for Needles in a Haystack: On the Role of Incidental Bilingualism in PaLM»s Translation Capability.
Использование данных в новых моделях Яндекс Переводчика
Новые данные мы используем в новом поколении моделей на основе LLM YandexGPT. Первый релиз на основе новых данных и новых моделей мы сделали в марте 2024 года. Для этого мы взяли модель YandexGPT, дообучили её на большом корпусе параллельных данных (стадия пост‑претрейна), дообучили с помощью RLHF (Reinforcement Learning from Human Feedback), в результате получив модель YandexGPT‑MT, которая значительно обгоняет все наши другие модели машинного перевода и максимально приближается к качеству профессиональных переводчиков. С помощью этой модели мы сгенерировали корпус, на котором дообучили наши продакшеновые (более быстрые и лёгкие) модели.
По нашим метрикам, новые параллельные документы в пост‑претрейне дали улучшение, сравнимое с тем, что дало RLHF поверх претрейн‑модели YandexGPT. Но это был лишь наш первый шаг: выкатить напрямую YandexGPT не представлялось возможным, так как это требовало слишком много GPU, которые всегда являются дефицитным ресурсом. Вместо этого мы дообучили наши продовые модели на синтетических данных, полученных с помощью YandexGPT‑MT, добившись значительного улучшения без дополнительных затрат на GPU.
Наши ближайшие планы — сделать так, чтобы сама модель YandexGPT могла обслуживать большинство запросов пользователей. Об этом читайте в следующих статьях.