KEN’S CAT LOG
▣

Я создал с нуля 300-миллионную LLM для RAG на хорватском языке. Она работает на CPU

После предыдущей GPT-2 я решил с нуля создать LLM для хорватского языка на одной RTX 4090. Цель — RAG: модель, которая ищет ответ в переданном ей тексте и отвечает, цитируя одно предложение-основание. И при этом она должна работать в продакшене на CPU.

Сразу к выводам.

  • От нуля до готовой модели — около 68 часов на GPU (включая эксперименты). Из них предобучение заняло 51 час
  • 320 млн параметров. Правильно отвечает на 71% вопросов по абзацам и на 44% вопросов по длинным текстам порядка 3 000 токенов
  • Только на CPU отвечает за 3–16 секунд на вопрос. Текст появляется на экране со скоростью, за которой можно следить глазами

Время, затраченное на обучение

Этап Время Содержание
Генерация обучающих данных Около 4,5 часа 45 тыс. пар вопрос-ответ по Wikipedia и 50 тыс. переводов диалогов с помощью EuroLLM-9B (vLLM)
Токенайзер и токенизация Около 2,5 часа (CPU) Самостоятельно создан словарь из 32 тыс. единиц для хорватского языка
Предобучение 50 часов 46 минут 7 млрд токенов. В последних 10% обучения контекст увеличен до 4 096
SFT Около 35 минут Обучение отвечать с цитатой и отказываться при отсутствии ответа
Обучение с подкреплением (GRPO) Около 11 часов 400 шагов, затем ещё 300 шагов настройки

Самодельный токенайзер представляет хорватский язык примерно вдвое меньшим числом токенов, чем токенайзер GPT-2. То есть в 4 096 токенов помещается около 17 тыс. символов.

Получившаяся модель

Пункт Содержание
Параметры 320 млн (24 слоя, размерность 1024)
Архитектура Семейство Llama / Qwen3 (GQA, SwiGLU, RoPE, RMSNorm + QK-Norm)
Длина контекста 4 096 токенов
Формат вывода Ответ + Izvor: „одно предложение, дословно взятое из контекста“. Если ответа нет: «U tekstu nema odgovora na ovo pitanje.(В тексте нет ответа на этот вопрос)」
Запуск Расчёт на CPU (около 1,3 ГБ в fp32)

Кроме того, снаружи добавлен механизм, который программно проверяет, действительно ли сгенерированная цитата есть в контексте; если нет — заменяет ответ на «ответа нет». Важно не доверять модели полностью, а сделать основание ответа проверяемым.

Проблемы, выявленные при разработке

1. Даже без ответа модель каждый раз что-то выдумывает. Сначала я просто обучил её отвечать на вопросы. Точность выросла, но при подаче текста без ответа модель в 100% случаев придумывала правдоподобный ответ. Для RAG это самая неприятная проблема. После обучения цитированию и «отказу» доля выдуманных ответов снизилась до нескольких процентов.

2. Слишком часто отказывается. Я задал оценку для обучения с подкреплением: «ошибся — −0,5, отказался — 0». На сложных длинных текстах модель усвоила, что выгоднее отказаться и уйти от ответа, и стала отказываться в 70% случаев. Она точно находит лазейки в правилах оценки. Когда штраф за отказ изменили на −0,3, маятник качнулся в другую сторону: модель почти перестала отказываться, а доля выдумок выросла до 40%. Получились постоянные качели.

3. Ошибки при копировании цитаты. В длинных текстах ответ бывает верным, но модель меняет порядок слов в цитате или цитирует первое предложение статьи. В длинном контексте и ответ, и цитата были верны примерно в половине случаев.

4. Ошибки, проходящие проверку. Модель точно процитировала добавленную в контекст из другой статьи фразу «родился в ○○» и назвала место рождения другого человека. Цитата настоящая, поэтому такая проверка её не остановит.

5. Недочёты в данных для оценки. Встречались случаи, когда в контексте, который должен был быть «без ответа», оставалась часть ответа: модель отвечала правильно, но это засчитывалось как «выдумка».

Итоговая производительность

По 300 вопросов в каждом наборе. Использовалась модель после 100-го шага скорректированного обучения с подкреплением — в ней лучше всего соблюдался баланс между отказами и ответами.

Контекст Верно Ошибка Отказ
Абзац (около 100–400 токенов) 71,3% 16,7% 12,0%
Длинный контекст (1 500–3 500 токенов) 44,0% 20,3% 35,7%
Контекст без ответа Корректный отказ Выдумка
Абзац 97,7% 2,3%
Длинный контекст 92,3% 7,7%

После прохождения проверки доля правильных среди данных ответов составила 73,8%. До цели в 90% ещё далеко.

Кстати, если давать модели решить один вопрос 8 раз и считать успехом хотя бы один правильный ответ, результат доходил до 91% для абзацев и 74% для длинных текстов (измерено на последнем чекпойнте). До и после обучения с подкреплением эти числа почти не менялись; изменилось то, может ли модель решать задачу каждый раз. Цифры ясно показали: обучение с подкреплением не столько добавляет новые способности, сколько позволяет стабильно решать задачи, которые раньше удавалось решить лишь иногда.

Запуск на CPU

Я сделал тестовый веб-интерфейс и запустил модель только на CPU (i7-13700K). Скорость появления символов на экране соответствует скорости генерации.

Абзац (263 токена): 0,3 секунды до первого символа, около 11 токенов/с, 7 секунд всего

Демонстрация ответа на вопрос по абзацу на CPU再生

Длинный контекст (3 215 токенов): около 5 секунд на чтение текста, около 5 токенов/с, 19 секунд всего. Жёлтым выделено цитируемое предложение: модель действительно находит правильное одно предложение среди 3 000 токенов

Демонстрация ответа из длинного контекста на CPU再生

Видео немного медленнее, потому что велась запись. При замере по 20 вопросов без записи получилось: 15 токенов/с и 3,4 секунды на вопрос для абзацев, 7 токенов/с и 16 секунд на вопрос для длинных контекстов. Ответы короткие, около 50 токенов, поэтому даже на CPU скорость, похоже, практична.

Выводы

  • Для маленькой модели важнее всего научиться говорить «я не знаю». В RAG уменьшение числа выдумок важнее точности. Сочетание цитаты, отказа и внешней проверки оказалось весьма эффективным
  • 320 млн параметров — это предел для длинных текстов. Даже при 8 попытках длинный контекст достигает лишь 74%. Чтобы стремиться к 90% на длинных текстах, желательно около 1 млрд параметров
  • Недостаточно проверять лишь то, существует ли цитата на самом деле. Нужно удостоверяться, что в ней есть число или имя из ответа и что подлежащее совпадает с человеком из вопроса
  • Модель на 320 млн параметров, работающая на CPU, хорошо подходит для вопросов и ответов по документам, которые нельзя выкладывать в интернет. На ноутбуке или сервере без GPU можно заставить её отвечать, опираясь только на локальные документы. Скорость — от нескольких до десятков секунд на вопрос; после квантизации она, вероятно, станет ещё выше (не проверено)

Далее я хочу попробовать версию на 1 млрд параметров и нацелиться на 90% в длинном контексте.