Своя модель

Axolotl: дообучение по конфигурационному файлу

Обучение, описанное одним YAML-файлом, а не скриптом. Разбираем конфигурацию по строкам и показываем, зачем это нужно: повторить эксперимент через полгода и получить тот же результат.

Через полгода вы захотите повторить обучение на дополненном датасете. И обнаружите, что не помните, какой была длина последовательности, сливали ли вы адаптер и почему ранг был именно такой. Скрипт вы правили по ходу дела, а история правок не сохранилась.

Это и есть задача, которую решает Axolotl: обучение описывается файлом настроек, файл лежит в системе контроля версий, и через полгода вы запускаете ровно тот же эксперимент.

#Зачем это нужно и когда не нужно

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

Общая рамка маршрута — что вообще даёт дообучение и чего от него ждать — в пилларе про свою модель и дообучение под себя.

#Что понадобится

  • Отдельное окружение с CUDA-сборкой PyTorch. Ставить рядом с рабочим приложением не стоит.
  • Датасет в JSONL, одна запись на строку, схема согласована со значением поля type в конфигурации.
  • Файл конфигурации — его мы и разберём.
  • Система контроля версий для этого файла. Без неё смысл подхода теряется наполовину.

#Шаг 1. Конфигурация по строкам

Минимальный рабочий файл для QLoRA. Комментарии объясняют, что делает каждый блок.

# --- база ---
base_model: Qwen/Qwen2.5-1.5B-Instruct
load_in_4bit: true          # QLoRA; false = обычная LoRA, если база влезает в 16 битах

# --- данные ---
datasets:
  - path: data/persona.jsonl
    type: chat_template      # схема записи; должна соответствовать формату файла
    field_messages: messages
val_set_size: 0.1            # отложенная выборка отделяется автоматически
dataset_prepared_path: last_prepared

# --- адаптер ---
adapter: qlora
lora_r: 16                   # ёмкость адаптера
lora_alpha: 32               # обычно 2 * lora_r
lora_dropout: 0.05
lora_target_linear: true     # адаптеры на все линейные слои

# --- режим обучения ---
sequence_len: 1024           # главный рычаг расхода памяти после размера базы
sample_packing: false        # упаковка примеров; на маленьком наборе только мешает
micro_batch_size: 1
gradient_accumulation_steps: 16   # эффективный батч = 1 * 16
num_epochs: 1                # начинайте с одной
learning_rate: 0.0002
lr_scheduler: cosine
warmup_ratio: 0.03
optimizer: paged_adamw_8bit  # экономит память на скромной карте
gradient_checkpointing: true

# --- вывод ---
output_dir: ./out/persona-lora
save_strategy: epoch
logging_steps: 10
seed: 42                     # без него повторить прогон нельзя

Три строки здесь важнее остальных.

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

sequence_len. Определяет расход памяти сильнее всего, кроме размера базы. Если обучение падает по памяти, уменьшать надо здесь, а не в размере батча — он уже равен единице.

type в блоке данных. Задаёт, как читается ваш файл. Несовпадение схемы файла и заявленного типа — самая частая причина того, что обучение идёт, а результат бессмысленный: часть полей просто не доехала. Разбор схем — в статье про форматы обучающих данных.

#Шаг 2. Проверка подготовки данных

Отдельный шаг, ради которого стоит терпеть конфигурационный подход: подготовку данных можно выполнить до обучения и посмотреть на результат.

axolotl preprocess config.yml

Команда рендерит датасет шаблоном базовой модели и складывает подготовленный набор в dataset_prepared_path. Откройте первый пример и прочитайте его глазами: вы должны увидеть служебные маркеры ролей и текст на своих местах. Пять секунд, которые ловят большинство проблем.

#Шаг 3. Запуск

axolotl train config.yml

Первый запуск делайте на урезанном датасете — двадцать примеров и num_epochs: 1. Он займёт минуты и покажет все ошибки конфигурации. Только после этого ставьте полный набор.

#Шаг 4. Порядок воспроизведения эксперимента

Ради этого раздела и написана статья. Чтобы через полгода получить тот же результат, зафиксируйте пять вещей:

  1. Файл конфигурации — в систему контроля версий, с осмысленным сообщением коммита. Название эксперимента в имени файла, а не в голове.
  2. Датасет — версией или хешем. Тот же конфиг на изменившихся данных даст другой результат, и вы будете искать причину в параметрах.
  3. Версии окружения — вывод pip freeze рядом с конфигурацией. Библиотеки обучения меняют имена аргументов между версиями чаще, чем хотелось бы.
  4. Зерно — seed в конфигурации. Без него пункты 1–3 не спасают.
  5. Результат проверки — в тот же каталог. Не «стало лучше», а список вопросов и ответы обеих моделей; протокол в статье про честную оценку дообученной модели.

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

#Проверка результата

  • Подготовленный датасет читается глазами и содержит правильные маркеры ролей.
  • Отложенная выборка отделилась — val_set_size сработал, и это видно в логе.
  • Обучение завершилось, адаптер сохранён в output_dir — там лежат файлы адаптера, а не копия базовой модели.
  • Слепое сравнение с базой показывает сдвиг в стиле без просадки на посторонних темах.
  • Повторный запуск той же конфигурации даёт тот же результат. Проверьте это хотя бы один раз: именно ради этого свойства вы выбрали конфигурационный подход.

#Что может пойти не так

  • Обучение идёт, качество странное. Почти всегда неверный type в блоке данных: часть полей не прочиталась. Смотрите подготовленный датасет, а не логи.
  • Ошибка нехватки памяти. Уменьшайте sequence_len, потом размер базы. micro_batch_size уже равен единице.
  • Неизвестный ключ конфигурации. Версия инструмента разошлась с примером, который вы скопировали. Сверяйтесь с документацией своей версии, а не с чужой статьёй.
  • Два запуска дают разное. Не зафиксирован seed либо изменился датасет.
  • Модель переобучилась. num_epochs больше одной на маленьком наборе. Признаки и лечение — в статье про гиперпараметры дообучения.
  • Адаптеры не встали на нужные слои. lora_target_linear: true — разумное значение по умолчанию; ручной список слоёв задавайте, только если понимаете, зачем.

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

#Частые вопросы

#Чем это лучше обычного скрипта?

Ничем по возможностям и многим по дисциплине. Скрипт вы правите по ходу и не помните, что меняли; конфигурация лежит в репозитории, и разница между двумя экспериментами видна в отличиях файла. Если прогон один — берите скрипт, разницы не будет.

#Можно ли одной конфигурацией обучать разные модели?

Технически да, меняется одна строка base_model. Практически стоит завести отдельный файл на каждый эксперимент: иначе через месяц вы не восстановите, к какой базе относится результат.

#Как задать свой шаблон чата?

Он берётся из токенизатора базовой модели и применяется автоматически. Задавать свой стоит только для «сырых» баз без инструктивного дообучения — и тогда тот же шаблон обязан использоваться на запуске, иначе модель будет вести себя не так, как обучалась.

#Нужна ли отложенная выборка, если датасет маленький?

Да, особенно если он маленький. Десять процентов от трёхсот примеров — это тридцать вопросов, на которых видно, выучила модель манеру или заучила строки. Без отложенных данных вы оцениваете результат по ощущениям.

#Работает ли это на нескольких видеокартах?

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

#Вывод

Конфигурационный подход не ускоряет обучение и не улучшает результат. Он делает другое: превращает эксперимент в артефакт, который можно повторить, сравнить и передать другому человеку.

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

Хотите ИИ, который помнит вас?

Persona держит контекст между чатами и работает на вашем железе — если развернуть её у себя и подключить локальную модель.

Создать аккаунт