Введение.

Продолжаю делиться опытом освоения нейросетей человеком с гуманитарным образованием и без IT-бэкграунда. На этот раз моей целью было разобраться, как происходит дообучение готовых LLM-моделей, как выстраивать процесс, какие использовать данные, модели, настройки и прочее. Зачем мне это понадобилось?

Во-первых, это вызов самому себе. Юристов, которые смогли бы дообучить языковую модель под свои задачи, думаю, единицы. Хотелось стать одним из тех, кому это по силам, тем более что тема мне интересна.

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

Но для этого нужно понимать, как устроен процесс обучения и из каких элементов он состоит. На момент, когда я взялся за эту тему, у меня были только идея и желание разобраться. Я понимал, что нужно начинать с малого, двигаться небольшими шагами от простого к сложному. Поскольку среди моего окружения не оказалось людей, разбирающихся в этой теме, а понятных гайдов по обучению я не нашел, все мои вопросы по обучению адресовались Grok, Qwen, Deepseek, Gemini и ChatGPT (только бесплатные версии).

Забегая вперед, скажу, что это была далеко не команда мечты. Большинство ошибок произошло из-за недосказанности, галлюцинаций и разных причуд нейросетей. Если бы у меня было хотя бы минимальное понимание того, какие существуют обученные LLM-модели, с какими стоит работать, а каких лучше избегать, я потратил бы на процесс обучения не неделю с небольшим, а справился бы за пару дней.

Для кого эта статья.

Учитывая, что я не эксперт, а мой максимум — это написание простых макросов и скриптов на Python. Она написана простым языком, с моими рассуждениями о нейросетях и личным опытом, полученным в результате множества допущенных ошибок — от установки WebUI Text Generation до получения итогового результата обучения.

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

Если вы уже понимаете, как происходит обучение LLM, эта статья, скорее всего, вас только позабавит. Но если эта тема для вас так же непонятна, как была для меня, и у вас нет опыта работы с обучением LLM, то понимание ошибок, которые вам могут встретиться, поможет в освоении этой темы. Как говорится, лучше учиться на чужих ошибках, чем на своих.

С чего все началось

Теперь по порядку. Не имея идей лучше, кроме как спросить у нейросети, с чего начать обучение, я написал в Grok, как обучить нейросеть, учитывая нулевой опыт и конфигурацию системы: CPU i7-12650H, GPU RTX 4060 (8 ГБ), RAM 16 ГБ. Я понимал, что параметры моей системы не позволят выполнить полный fine-tuning и нужно работать с компактными моделями, но о том, какие бывают модели и методы обучения, представления не было. Поэтому, когда Grok предложил скачать Ollama и LM Studio, сомнений в правильности этого предложения у меня не возникло.

При этом нейросеть не уточнила, что Ollama и LM Studio предназначены только для инференса (запуска моделей), а не для обучения. Тогда я этого не знал, и пара часов, пока я разбирался с этими программами, улетела впустую. На мой вопрос, почему Grok предложил инструменты, в которых невозможно дообучение, он извинился и пояснил, что моего опыта недостаточно для самостоятельного обучения. Поэтому он посоветовал начать с работы с локальными моделями в удобных интерфейсах.

Здесь я хочу обратить ваше внимание на главную ошибку, которую я продолжаю допускать, — веру в то, что машина не может ошибаться. За 10 месяцев работы с нейросетями я неоднократно ловил их на галлюцинациях: они недоговаривают, ошибаются и иногда выдают совершенно недостоверную информацию. Поэтому при взаимодействии с LLM нужно максимально точно формулировать вопрос, включая в него все детали и прописывая желаемый результат. При чем даже это не гарантирует, что нейросеть не выдаст ошибочную информацию. Лучше иметь хотя бы базовое представление об изучаемой теме, чтобы критически оценивать ответы, а не только следовать инструкциям нейросети. В моём случае общее непонимание темы и стало причиной ошибок, о которых я расскажу дальше.

Установка Text Generation WebUI и первая допущенная ошибка

После того как я выяснил, что Ollama и LM Studio не подходят для дообучения LLM, я снова обратился к нейросетям. На этот раз мне порекомендовали Text Generation WebUI (oobabooga). Для тех, кто не знает, кратко поясню: это open-source решение, которое позволяет запускать языковые модели локально и дообучать их с помощью LoRA (Low-Rank Adaptation) - метода тонкой настройки, при котором обновляются только небольшие матрицы, добавленные к весам модели.

Text Generation WebUI представляет собой графический интерфейс для работы с нейросетями, который поддерживает генерацию текста, чаты и дообучение. Он работает на базе фреймворков, таких как llama.cpp или PyTorch, и поддерживает модели в форматах Safetensors, GGUF, GPTQ. Пользователь загружает модель, настраивает параметры, а WebUI обрабатывает инференс (генерацию текста) или обучение. Если вы работали со Stable Diffusion, интерфейс покажется знакомым, так как оба открываются в браузере на локальном хосте.

WebUI включает семь вкладок: Model, Chat, Training, Notebook, Character, Parameters и Session. Кратко опишу те, с которыми работал:

1.  Model: здесь загружают модель и настраивают её параметры. Можно выбрать LoRA-адаптеры (надстройки для тонкой настройки), настроить квантизацию (уменьшение размера модели для экономии памяти) и указать, использовать GPU или CPU.

2.  Chat: режим диалога с моделью, где можно настроить стиль общения (например, формальный или дружелюбный) и сохранять историю разговоров. Удобно для тестирования модели.

3.  Training: вкладка для дообучения, где загружают датасет и настраивают параметры, такие как learning rate или размер LoRA-матриц.

Если вы зайдёте на GitHub и введёте в поиск «text-generation-webui oobabooga», то найдёте страницу проекта, где внизу описаны варианты установки. Я сразу допустил ошибку: не прочитав инструкции, выбрал первый вариант и скачал усечённую версию без бэкендов (ExLlamaV3, Transformers). Из-за этого, запустив интерфейс, я не смог выбрать необходимый для обучения параметр Transformers в разделе Model Loader и потерял ещё несколько часов, пока не понял свою ошибку.

Ошибка с выбором формата модели

После успешной установки Text Generation WebUI я приступил к выбору модели для дообучения. Нейросеть предложила несколько вариантов моделей, среди которых была Phi-3-mini-4k-instruct.q4_K_M.gguf - компактная модель, которая, как мне казалось, идеально подходила для моей системы из-за небольшого размера.

Однако при загрузке модели в WebUI и выборе бэкенда Transformers в разделе Model Loader я столкнулся с ошибкой: сообщение Traceback (most recent call last) и длинный список строк, включая line 743, in getconfig_dict и line 844, in dictfrom_json_file. Как оказалось бэкенд Transformers не поддерживает формат GGUF, который используется в фреймворке llama.cpp. GGUF - это квантованный формат, оптимизированный для инференса (запуска модели) на системах с ограниченными ресурсами, но неподходящий для дообучения с помощью LoRA. Для обучения необходимы полные веса модели в формате PyTorch, таком как Safetensors или .bin, чтобы обеспечить точность вычислений градиентов - параметров, обновляемых в процессе дообучения.

При этом несмотря на то, что формат модели GGUF не подходит для использования Transformers нейросеть не сдалась и начала предлагать разные обходные методы с изменением кода отдельных файлов WebUI и прочие костыли. К счастью, в этот раз я на это не повелся и просто скачал нужную модель с форматом safetensors. Урок такой - перед загрузкой модели всегда проверяйте её формат и совместимость с задачей.

Выбор задачи для дообучения

Вариантов для дообучения у меня не было. Я понимал, что нужны простые задачи, которые можно легко проверить.  Поэтому снова обратившись к нейросетям получил несколько вариантов, из которых самым простым мне показался обучить модель птичьему языку. Задача казалось идеальной для до обучения, она не требовала большего датасета (достаточно 70 строк) и легко проверялась. Суть состояла в следующем: модель должна преобразовывать русские слова, удваивая гласные по простому правилу. Например, «лиса» превращается в «лиисаа». Чтобы проверить результат дообучения, достаточно ввести запрос, например, «переведи слово кот на птичий язык», и модель, если всё сделано правильно, должна выдать «коот».

Проблемы с подготовкой датасета

После выбора задачи («птичий язык») и загрузки модели я перешёл к подготовке датасета для дообучения. Датасет - это набор данных, на которых модель учится выполнять задачу, в данном случае - преобразовывать слова по правилу удвоения гласных (например, «кот» в «коот»). Однако при попытке загрузить подготовленный файл я столкнулся с проблемой: WebUI не распознавал мой файл.

Проблема возникла из-за неверных инструкций. LLM чётко указала, что необходим датасет в формате JSONL с массивом данных, прописанных следующим образом: {"instruction": "Переведи слово на птичий язык:", "input": "сок", "output": "соок"}. Пришлось потратить немало времени, чтобы разобраться с тем, что WebUI поддерживает только формат JSON.

Разница между форматами заключается в следующем: JSONL (JSON Lines) состоит из отдельных JSON-объектов, разделённых символом новой строки (\n), что удобно для построчной обработки больших датасетов. Однако модуль Training в WebUI использует функцию Python json.load(), которая ожидает единый JSON-объект, такой как массив. JSONL невалиден для этой функции, так как не образует целостной структуры.

Поэтому, после смены расширения файла, я получил ошибку: «Invalid JSON! Error: Parse error on line 1: ..., "output": "соок"}{"instruction": "Пре... ^ Expecting 'EOF', '}', ',', ']', got '{'». Чтобы её решить, я написал Python-скрипт, который преобразовал JSONL в JSON, объединив строки в единый массив. После этого WebUI загрузил датасет и начал процесс дообучения.

Несовместимость модели: ошибка KeyError: «phi3».

Когда начался процесс обучения появилась новая проблема – ошибка KeyError: «phi3». Оказалось, что WebUI не поддерживает модель Phi-3-mini. Дело в том, что во внутреннем списке поддерживаемых архитектур WebUI не было записи для Phi-3. Таким образом интерфейс пытался найти ключ 'phi3' в словаре поддерживаемых моделей, где его не было.

Чтобы это исправить, пришлось вручную редактировать функцию list_target_modules в файле training.py, прописав нужные модули для LoRA-обучения (, q_proj, v_proj, k_proj и т.д.).

Ошибка обучения grad_norm: nan и loss = 500000+.

 Это последние ошибки, которые появились у меня в процессе обучения. После запуска через 5 минут обучение прервалось с сообщением: Step: 63 {'loss': 531370.5625, 'grad_norm': nan, ...}. Расшифрую, что это значит.

Step — это один цикл обучения, в котором модель обрабатывает небольшую часть данных (батч) и обновляет параметры. Loss (функция потерь) показывает, насколько предсказания модели отклоняются от правильных ответов. Например, если задать модели промпт: «Переведи на птичий язык слово ‘кот’, должно получиться ‘коот’», и модель выдаёт «коот», то loss будет низким, так как ответ правильный. Если же модель ошибается и выдаёт, например, «коттт» или «собака», loss будет выше, так как ошибка больше. Высокий loss (например, 531370) указывает на значительные ошибки модели. Grad_norm: nan означает, что градиенты, используемые для корректировки параметров, стали неопределёнными, что привело к сбою обучения.

Все эти показатели зависят от качества данных, настроек learning rate, LoRA rank и других параметров. Я не могу точно сказать, как решил эту проблему, но после нескольких перезапусков интерфейса с выгрузкой и повторной загрузкой модели, а также изменения настроек обучения (в частности, learning rate с 1e-4 до 5e-5) проблема исчезла, и обучение завершилось успешно.

 Итоги

 Изначально для обучения Phi-3-mini я использовал набор данных сначала из 70, потом из 400 строк, но результата не было: модель не понимала, что такое птичий язык, считала его английским и переводила слово «кот» в «cat». После этого я увеличил датасет до 1000 строк, а затем до 3000. Но даже с таким объёмом данных результат не появился. Тогда я начал менять настройки обучения: повышал LoRA rank, изменял learning rate, снизил loss до 1.2, но результата всё равно не было.

В итоге, после многочасовых попыток получить вменяемый результат, я пришёл к выводу, что Phi-3-mini не поддаётся обучению для этой задачи. Почему LLM предложила эту модель, мне непонятно. Потому что Phi-3-mini обучена в основном на англоязычных текстах, из-за чего её способность понимать и генерировать русский язык ограничена. Для задач с русским языком лучше сразу выбирать модели с мультиязычной архитектурой, такие как Qwen, Mistral или LLaMA.

Таким образом, я смог успешно завершить обучение и получить ожидаемый результат с помощью двух моделей: первой Mistral-7B-Instruct-v0.3-GPTQ-4bit и второй LLaMA-2. Я не менял объём датасета, использовал уже готовый с 3000 примерами, но думаю, что для этих моделей можно было дообучить и с меньшим — на 400 строк.

Какой вывод можно сделать по результатам такого опыта? Было много ошибок, которые отняли много времени и которых можно было избежать, если бы я обладал пониманием, как с этим работать, или нейронная сеть более достоверно выдавала мне информацию и не опускала определённые факты. Но всё равно это здорово, что человек, не разбирающийся в IT, может с помощью таких инструментов, как WebUI и LLM, дообучить нейронную сеть. И хотя результат по меркам специалистов IT не «ахти» какой, на мой взгляд, современные технологии несколько уравнивают возможности таких «экспертов», как я, с настоящими профессионалами, и в будущем, думаю, эта разница будет постепенно снижаться, но, конечно, профессионалы всегда будут на порядок выше.

Комментарии (2)


  1. Jogker
    06.10.2025 19:25

    "Юристов, которые смогли бы дообучить языковую модель под свои задачи, думаю, единицы." - и заплакал...


  1. aladkoi
    06.10.2025 19:25

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