Миллионы строк или миллионы параметров: как мы искали баланс для kazRush — открытой модели перевода «русский—казахский»

В задаче обучения модели машинного перевода есть приятный момент: вы находите несколько миллионов пар предложений и на секунду чувствуете, что самая трудная часть уже позади. Потом открываете случайную строку. Слева — предложение, справа — фрагмент таблицы. У следующей пары совпадает тема, но не смысл. Ещё чуть дальше кто-то заботливо положил в текст остатки HTML. Датасет приехал с мебелью предыдущих жильцов.
Для малоресурсных языков к этому добавляется ограниченность параллельных данных. Поэтому здесь особенно полезно понимать, что именно модель получает на вход и какую архитектуру мы пытаемся на этом обучить.
На связи Аня Лебедева, разработчик в отделе экспериментальных технологий AI VK, и сегодня я расскажу про kazRush — систему перевода с русского на казахский и обратно. Мы собрали и очистили корпус из открытых источников, обучили модели с нуля и подобрали конфигурацию модели примерно на 197 миллионов параметров. По дороге выяснилось, что более строгая очистка иногда ухудшает результат, бо́льший словарь не обязательно полезнее, а увеличение модели до 738 миллионов параметров само по себе не решает задачу. В общем, несколько вполне разумных идей пришлось проводить обратно до двери, но итоговый результат оправдал усилия. Русско-казахская модель заняла первое место в своём треке на LoResMT 2026 — воркшопе при конференции EACL.
Почему этой языковой паре нужна отдельная работа
Русский и казахский языки используются в образовании, медиа, государственных сервисах и повседневном общении. Практических сценариев для перевода достаточно. С данными ситуация сложнее: наличие миллионов носителей языка ещё не означает наличия сопоставимого количества качественных пар «предложение — перевод».
У казахского есть и особенность, которую приходится учитывать при обучении: это агглютинативный язык. К основе последовательно присоединяются аффиксы, и получается множество словоформ. Для модели это означает больше вариантов, которые нужно связать друг с другом и научиться переводить. Когда примеров мало, часть этих вариантов встречается редко.
Крупные мультиязычные модели дают хорошую отправную точку, но нам было интересно, какого качества можно добиться у специализированной системы, если тщательно подготовить двуязычные данные и подобрать под них архитектуру.
Основным прикладным направлением был перевод с русского на казахский. Обратную модель обучили заодно: к этому моменту корпус и пайплайн уже были готовы. Токенизатор и принципы построения архитектуры у 2 направлений одинаковые, веса — собственные.
Сначала собираем корпус. Потом знакомимся с ним поближе
Данные взяли из всех открытых ресурсов, которые смогли найти:
Источник | Пар предложений до фильтрации, приблизительно |
|---|---|
OPUS | 718 тысяч |
KazParC | 2,15 миллиона |
WMT19 | 5,063 миллиона |
Turkic Interlingua, TIL | 4,403 миллиона |
Получилось около 12,3 миллиона пар до фильтрации и отбора корпусов. Источники помогали расширять покрытие: например, данные WMT добавляли государственные, юридические, институциональные и новостные тексты, а среди ресурсов OPUS были субтитры и выступления TED.
При объединении датасетов мы сохраняли для каждой записи метку источника. Это небольшое решение потом оказалось очень удобным. Если странность повторяется в тысячах строк, полезно выяснить, какой корпус её принёс.
Очистку разделили на общую, которую проходят все данные, и отдельные правила для конкретных корпусов.
Как удалять мусор и вовремя останавливаться
Правило 1: не пренебрегать скучной частью
Сперва пройдёмся по тривиальным способам очистки данных. Они могут быть очевидными, но важно упомянуть, что и мы не пренебрегли ими, так как это основа качественной предобработки.
Удалили точные дубликаты
Убрали HTML-теги, другую разметку, мусорные символы
Применили Unicode-нормализацию NFKC
Убрали дублирующиеся пробелы, разделители и другую лишнюю пунктуацию
Каждое предложение в паре независимо проверяли моделью определения языка fastText. Оставляли пары, в которых предсказанные языки соответствовали ожидаемому направлению перевода. Например: для направления «русский — казахский» убеждались, что source sentence детектится как текст на русском, target sentence — как текст на казахском
В качестве ещё одной проверки использовали LaBSE: получали мультиязычные эмбеддинги source- и target-предложений, вычисляли их скалярное произведение и удаляли пары со значением ниже выбранного вручную порога 0,4. Логично было бы адаптировать порог под каждый датасет, но подбор начал занимать много времени плюс работал всё ещё нестабильно, так что остановились на едином значении, потеряв немного хороших пар и пропустив немного плохих
Сверим числа
Теперь перейдём к нетривиальной части. После стандартной предобработки у нас всё ещё оставалось достаточно много нестыкующихся пар. Нужно было выдумать правила, согласно которым мы сможем отыскивать и удалять такие экземпляры. Одной из идей стала проверка чисел. Мы извлекали последовательности чисел с обеих сторон и сравнивали их как сеты. Сверять упорядоченные списки не было смысла — из-за особенностей грамматики в источнике и переводе числа в предложении могут идти в разном порядке, а так учитывается и состав, и количество повторений, но одинаковый порядок не требуется.
При обнаружении несовпадения пара предложений отправлялась на ручную проверку. Автоматически удалять её было бы слишком жёстко: числа могут быть корректно записаны разными способами, например в источнике — цифрами, а в переводе — словами. Если при ручной проверке выявлялся такой случай, числа приводились к одному формату. А если проверка показывала, что тексты с несовпадением чисел не являются переводами друг друга, пару исключали.
Даже первая буква может оказаться отдельной задачей
После первой итерации обучения обнаружили, что, с какой бы буквы (заглавной или прописной) ни начиналось предложение-источник, перевод всегда стартовал с заглавной. Это некорректно, так как пользователь не всегда переводит предложение целиком, да и в целом, какие бы ни были причины, для перевода важно сохранить формат исходного текста.
Такое поведение оказалось следствием данных, на которых строилось наше обучение: в корпусе нашлось 237 859 предложений на русском языке, начинающихся со строчной буквы, и у половины из них казахский перевод начинался с прописной.
Проверили вручную сотню случайных примеров. В большинстве случаев различие выглядело как артефакт подготовки корпуса; лишь небольшая часть требовала сохранить исходный регистр. Поэтому добавили правило: если исходное предложение начинается со строчной буквы, первый символ перевода тоже переводим в нижний регистр, остальной текст не меняем.
Ещё одна креативная эвристика против мусора
Мы хотели отсеять строки, в которых вместо текста — сотня восклицательных знаков или россыпь тире. Для этого считали долю букв и цифр в каждой строке и делили её на длину строки в степени 0,1:
оценка = (число букв и цифр / число всех символов) / длина строки0,1
Интуиция: чем больше в строке знаков препинания и других небуквенных символов, тем меньше вероятность, что это настоящее предложение. Деление на длину в степени 0,1 дополнительно снижает оценку для длинных строк, поскольку мы оперируем на уровне предложений, и очень-очень длинная строка — это подозрительно. Пару оставляли, если у каждого из двух текстов оценка была не ниже 0,4.
Alignment score из подручных средств
Исследовали гипотезу, что, если источник и перевод сильно отличаются по длине в символах, это, скорее всего, не пара «текст — перевод». После изучения эталонных переводов из датасета FLORES+ и ручной проверки других данных приняли допустимое отношение длин от 0,5 до 2,0. То есть, если одно предложение из пары «source — target» длиннее другого более чем вдвое, пару удаляли.
Точные дубликаты убрали. С почти одинаковыми вышло интереснее
В начале пайплайна мы уже удалили точные дубликаты по всей коллекции. После этого мы попробовали находить близкие дубликаты с помощью MinHash LSH — подхода для поиска похожих записей. Ожидание понятное: меньше повторов — разнообразнее обучение. Но в наших экспериментах после такой обработки качество итоговой модели ухудшилось.
Возможное объяснение — похожие предложения в условиях ограниченных данных работают как естественная аугментация. Модель видит вариации конструкций и получает дополнительные обучающие примеры. Это гипотеза, но практический вывод из эксперимента вполне конкретный: удаление близких дубликатов в финальный рецепт не вошло.
Почему одного набора фильтров не хватило
После общей очистки мы отдельно просматривали корпуса. У субтитров встречались собственные артефакты и мусорные символы, для которых понадобились специальные регулярные выражения. В другом источнике обнаружились искусственные номера в начале предложений. У некоторых длинных пар начало и конец относились к разному содержанию: выравнивание было лишь частичным.
Когда находили повторяющуюся проблему, обновляли обработку и запускали её заново. Метка источника позволяла ограничить специальное правило теми данными, для которых оно было нужно.
В результате состав корпуса изменился:
KazParC сохранили почти целиком после базовой проверки. При ручном просмотре и исходные тексты, и синтетическая часть показали высокое качество
Из OPUS оставили очищенные OpenSubtitles, QED, TED Talks, TED2020, Tatoeba-2022 и Tatoeba-2023
Данные TIL включили после более серьёзной очистки
MultiCCAligned и WikiMatrix исключили из-за проблем с качеством и выравниванием
После всех этапов осталось 9 054 960 пар предложений.
Токенизатор: 8 000 единиц на 2 языка
Токенизатор разбивает текст на небольшие части — токены, с которыми работает модель. Для kazRush обучили общий SentencePiece-токенизатор на русской и казахской частях очищенного корпуса. Размер словаря составил 8 000 подсловных единиц.
Проверили и вариант на 16 000 единиц. Он немного ухудшил итоговое качество, поэтому остановились на предыдущем варианте. Полученный SentencePiece преобразовали в токенизатор T5 из Hugging Face и использовали во всех экспериментах.
Архитектура: куда потратить 197 миллионов параметров
Мы использовали Transformer с энкодером и декодером на базе T5. Энкодер обрабатывает исходное предложение с учётом контекста, декодер поэтапно строит перевод и обращается к представлениям исходного текста через cross-attention. Мы обучали модель перевода на параллельных примерах и выбрали архитектуру, непосредственно подходящую для этой задачи. Попытка получить сильный переводчик на базе decoder-only модели с нуля потребовала бы рассматривать другой баланс данных, размера модели и вычислений.
Все модели обучались с нуля, со случайной инициализации. Готовые предобученные веса T5 не использовались.
Сначала сравнили стандартные конфигурации T5-Small, T5-Base и T5-Large. Затем собрали собственную: сохранили относительно небольшие размеры скрытого представления и внимания, как у T5-Small, но увеличили глубину энкодера и декодера, а также размер feed-forward блоков.
Параметр итоговой модели | Значение |
|---|---|
Слои энкодера/декодера | 12/12 |
Размер скрытого состояния | 512 |
Головы внимания | 8 |
Размерность головы | 64 |
Размер полносвязного слоя | 4 096 |
Размер словаря | 8 000 |
Dropout | 0,1 |
Функции активации | Gated GELU |
Количество параметров | ≈ 197 миллионов |
Предположение было таким: при нашем объёме данных увеличение глубины и ёмкости feed-forward блоков может быть полезнее сильного расширения размерности скрытого состояния и внимания. Почему такая конфигурация может выигрывать? Дополнительные слои позволяют последовательно уточнять представления текста: каждый следующий слой работает с результатом предыдущего. А увеличение промежуточной размерности feed-forward блоков позволяет вычислять больше признаков для каждого токена, что, в свою очередь, позволяет учитывать больше разных сочетаний признаков — например, лексических, грамматических и контекстных — при обновлении представления токена. При этом мы сохраняем компактные размерности скрытого состояния и внимания, ограничивая число параметров, которые нужно обучить на доступных параллельных данных. Так мы направляем дополнительную ёмкость модели в глубину обработки и преобразования признаков. В наших абляционных экспериментах такое распределение параметров оказалось удачнее, чем у стандартных конфигураций T5, включая существенно более крупную T5-Large.
Как обучали и выбирали модель
Максимальную длину последовательности ограничили 128 токенами. Использовали batch size 128 с накоплением градиентов и смешанную точность BF16. Learning rate составлял 0,003, разогрев — 1 000 шагов, label smoothing — 0,1.
Обучение продолжалось до 300 тысяч шагов оптимизации. Чекпойнт выбирали по validation loss.
При сравнениях генерировали переводы одинаково: beam search с тремя лучами, без sampling и с early stopping.
Что показали эксперименты
Для валидации использовали датасет FLORES+ dev, а результаты сравнивали на датасете FLORES+ devtest. Смотрели на три метрики:
BLEU оценивает совпадения последовательностей слов с эталонным переводом и штрафует слишком короткие ответы
chrF сопоставляет символьные n-граммы. Для богатой морфологии это полезно — совпадение части словоформы тоже учитывается
COMET — нейросетевая оценка, которая использует исходный текст, перевод модели и эталон
Для всех трёх метрик значение больше — лучше.
Для направления «русский — казахский» получили следующие результаты:
Модель | Параметры | BLEU | chrF | COMET |
|---|---|---|---|---|
T5-Small | 64,8 млн | 15,6 | 51,2 | 0,8764 |
T5-Base | 220 млн | 16,2 | 51,8 | 0,8822 |
T5-Large | 738 млн | 15,8 | 51,6 | 0,8804 |
Custom-Large | 197 млн | 16,2 | 51,9 | 0,8836 |
Собственная конфигурация оказалась почти в 4 раза меньше T5-Large по числу параметров и получила лучшие chrF и COMET среди этих вариантов. По BLEU она разделила первое место с T5-Base.
В обратном направлении собственная архитектура тоже обошла T5-Small: BLEU вырос с 17,5 до 18,8, chrF — с 47,4 до 48,7, COMET — с 0,8556 до 0,8672.
Результаты для направления «казахский — русский» на FLORES+ devtest:
Модель | BLEU | chrF | COMET |
|---|---|---|---|
T5-Small | 17,5 | 47,4 | 0,8556 |
Custom-Large | 18,8 | 48,7 | 0,8672 |
А как относительно NLLB?
Создавая модель, невозможно не учитывать уже существующих решений. В этом контексте в первую очередь мы должны были провести сравнение с NLLB — большой мультиязычной моделью от Facebook, которая в том числе поддерживает пару «русский — казахский», хотя её вариации и не содержат таких компактных моделей, как наша. Сравнили модели на FLORES+ devtest для обоих направлений. Лучшие значения выделены отдельно для каждого направления.
Русский → казахский
Модель | Параметры | BLEU | chrF | COMET |
|---|---|---|---|---|
NLLB distilled 600M | 600 млн | 13,8 | 48,2 | 0,868 |
NLLB 1.3B | 1,3 млрд | 14,8 | 50,1 | 0,881 |
NLLB distilled 1.3B | 1,3 млрд | 15,2 | 50,2 | 0,884 |
NLLB 3.3B | 3,3 млрд | 15,6 | 50,7 | 0,889 |
kazRush | 197 млн | 16,2 | 51,8 | 0,883 |
Казахский → русский
Модель | Параметры | BLEU | chrF | COMET |
|---|---|---|---|---|
NLLB distilled 600M | 600 млн | 18,0 | 47,3 | 0,856 |
kazRush | 197 млн | 18,8 | 48,7 | 0,867 |
NLLB 1.3B | 1,3 млрд | 20,4 | 49,3 | 0,879 |
NLLB distilled 1.3B | 1,3 млрд | 20,8 | 49,6 | 0,881 |
NLLB 3.3B | 3,3 млрд | 21,5 | 50,7 | 0,887 |
На FLORES+ devtest русско-казахская модель kazRush превзошла все сравниваемые варианты NLLB по BLEU и chrF. Например, у NLLB 3.3B были 15,6 BLEU и 50,7 chrF, у kazRush в таблице этого сравнения — 16,2 и 51,8.
По COMET практически паритет с NLLB 3.3B: 0,889 против 0,883 у kazRush. Но размер моделей несопоставим: использовать NLLB для практической задачи было бы гораздо дольше.
При переводе с казахского на русский kazRush обошла distilled NLLB 600M, но уступила более крупным сравниваемым NLLB на 1,3 и 3,3 миллиарда параметров. Специализированная модель оказалась особенно успешной именно в направлении «русский — казахский».
LoResMT: первое место
Русско-казахская модель участвовала в соревновании LoResMT 2026. Основной метрикой ранжирования была chrF; система заняла первое место в русско-казахском треке.
Тест содержал 4 626 пар, поровну разделённых между публичной и приватной частями. Ниже — результаты на исходном тесте соревнования, все значения приведены в chrF.
Система | Public | Private |
|---|---|---|
kazRush | 76,77 | 76,24 |
NLLB-200 3.3B | 54,56 | 54,08 |
NLLB-200 1.3B | 53,97 | 53,38 |
TranslateGemma 27B | 51,48 | 51,08 |
KazLLM 8B | 51,38 | 51,12 |
Llama-3.1-8B-kkru | 50,35 | 49,64 |
На приватной части теста отрыв от NLLB-200 3.3B составил 22,16 пункта chrF.
Что мы забрали из этой работы
Самые полезные решения оказались довольно конкретными. Сохранять происхождение каждой пары. Смотреть данные руками. Проверять числа, регистр, язык и смысловую согласованность отдельными этапами. Измерять эффект очистки на итоговой модели — в том числе когда новая фильтрация выглядит очевидным улучшением.
С архитектурой сработал похожий подход: сравнили несколько конфигураций и нашли удачное распределение параметров для своей задачи. Модель на 197 миллионов параметров оказалась сильнее более крупной T5-Large в наших внутренних экспериментах, а на соревновании уверенно обошла сравниваемые мультиязычные системы.
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.