Руководство по файн-тюнингу LLM: LoRA, QLoRA, Unsloth, Axolotl

Автоматический перевод Эта статья была автоматически переведена с оригинальной английской версии.

Большинство неудач при файн-тюнинге — это ошибки выбора подхода. Команда начинает обучение, не доказав, что промптинг, retrieval или constrained decoding не решают задачу. Другая команда оценивает модель на распределении, совпадающем с обучающим, или обнаруживает уже после обучения, что получившийся артефакт неудобно использовать в сервинге.

В этом руководстве адаптация рассматривается как эксперимент с заранее определённым операционным выходом. Сначала мы определим границу выбора подхода, а затем пройдём путь через данные, LoRA или QLoRA, оценку на целевой задаче, экспорт и сервинг.

Краткое руководство по выбору подхода см. в статье Fine-Tuning vs RAG vs Prompting.

Нужно ли вообще выполнять файн-тюнинг?

Прежде чем тратить часы GPU, решите, действительно ли файн-тюнинг — подходящий инструмент для вашей задачи.

Блок-схема выбора подходаБлок-схема выбора подхода

Файн-тюнинг и RAG

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

ВозможностьФайн-тюнингRAG (Retrieval-Augmented Generation)
Основная функцияИзменяет внутренние веса, обучая навыкам, стилю или поведениюПередаёт внешний актуальный контекст во время инференса
Лучше всего подходит• Специфический стиль диалога
• Сложное следование инструкциям
• Доменно-ориентированный ризонинг
• Быстро меняющиеся данные (новости, цены акций)
• Снижение галлюцинаций (граундинг)
• Цитирование источников
Работа со знаниямиМеняет статистическое поведение в весах; точное воспроизведение не гарантируетсяИзвлекает записи или фрагменты, которые можно обновлять и цитировать
Частота обновленийДля обновлений требуется повторное обучениеОбновляется сразу после добавления новых документов

Файн-тюнинг и промпт-инжиниринг

Современные LLM хорошо реагируют на ясные промпты и примеры. Проверьте эти варианты, прежде чем вкладываться в файн-тюнинг.

АспектФайн-тюнингПромпт-инжиниринг
Стоимость запускаВысокая (подготовка данных, вычисления на GPU, итерации)Низкая (итеративная доработка промпта)
ГибкостьТребует нового цикла обучения и релизаМеняется вместе с промптом
Формат/стильПовышает вероятность повторяющегося поведенияЧасто достаточен для стиля и простых форматов
ЛатентностьМожет сократить повторяющиеся инструкцииЗависит от длины промпта и кэширования провайдера
Лучше всего подходитСложное поведение, дистилляция, снижение стоимости в масштабеБыстрые итерации, меняющиеся требования

[!TIP] Сначала попробуйте промптинг Начните с промпта и репрезентативных примеров. Если ненадёжен только синтаксис ответа, добавьте constrained decoding, прежде чем менять веса.

Файн-тюнинг и constrained decoding

Библиотеки вроде xgrammar и outlines ограничивают генерацию JSON Schema, регулярным выражением или грамматикой. В зависимости от ограничения и бэкенда они компилируют автомат или грамматику и маскируют недопустимые следующие токены. Обновление весов не требуется.

Это гарантирует принадлежность результата поддерживаемому языку вывода. Но гарантия ничего не говорит о том, будут ли значения истинными, полными или семантически корректными. Синтаксически валидный tool call всё равно может содержать неверный ID клиента.

АспектConstrained DecodingФайн-тюнинг
НастройкаНемедленная — определить схему и задеплоитьТребует подготовки данных, вычислений на GPU и итераций
ГарантияВалидный синтаксис для поддерживаемого ограниченияВыученное поведение; соответствие схеме может различаться
ГибкостьСхему можно менять в любой момент без повторного обученияФиксируется после обучения
ЛатентностьНебольшие накладные расходы (модель может «бороться» со схемой)Ниже (модель естественно выдаёт нужный формат)
Лучше всего подходитJSON, варианты выбора, грамматики, синтаксис tool callПовторяющееся поведение задачи, которого не хватает базовой модели

Практический порядок:

  1. Начните с промптинга и few-shot-примеров для базового форматирования.
  2. Добавьте constrained decoding (xgrammar или outlines), если синтаксис нестабилен.
  3. Выполняйте файн-тюнинг только при необходимости изменить поведение, которое нельзя зафиксировать схемой.

Краткая шпаргалка: сопоставление проблем и решений

ПроблемаПервый механизм для проверкиПочему?
Недостающие знанияRAGМодели галлюцинируют факты. Retrieval даёт заземлённый актуальный контекст
Неверный формат или тонПромпт-инжинирингСовременные модели хорошо следуют указаниям по стилю через few-shot-примеры
Невалидный синтаксис выводаConstrained decodingВо время генерации принудительно соблюдает поддерживаемую схему или грамматику
Повторяющаяся ошибка в задачеФайн-тюнинг (SFT)Обучается на подготовленных парах вход/выход
Несоответствие парных предпочтенийPreference optimizationИспользует примеры chosen/rejected после того, как поведение задачи стало измеримым
Латентность или стоимость в масштабеДистилляция (SFT)Обучает меньшую student-модель на ответах большей teacher-модели
Уменьшение размера моделиКвантизацияОбучение не требуется — веса сжимаются (FP16→INT4) для ускорения инференса

Сделайте бизнес-кейс измеримым

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

[ \text{количество запросов до безубыточности} = \frac{\text{стоимость обучения + оценки + деплоя}} {\text{стоимость запроса baseline} - \text{стоимость запроса tuned-модели}} ]

Если знаменатель мал, отрицателен или основан на неподтверждённом предположении о качестве, у проекта пока нет экономического обоснования.

[!TIP] Гибридная конфигурация Распространённая архитектура — меньшая модель, адаптированная под задачу, плюс retrieval для изменяющихся фактов. Используйте большую модель с промптингом как baseline и оставляйте меньшую модель только в том случае, если она достигает тех же порогов качества и безопасности на целевой задаче.


Виды файн-тюнинга

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

Виды файн-тюнингаВиды файн-тюнинга

1. Continued pre-training (self-supervised)

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

Когда использовать:

  • В домене есть лексика, которой базовая модель никогда не видела (медицина, право, внутренние кодовые базы).
  • У вас много доменного текста, но нет размеченных пар (input, output).
  • Базовая модель плохо справляется с доменной терминологией.

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

2. Supervised fine-tuning (SFT)

SFT обучается на размеченных парах (input, output). Для каждого input вы показываете модели точный output, который хотите получить.

Когда использовать:

  • У вас есть конкретная задача с чистым форматом input/output.
  • У вас есть качественные размеченные данные, пусть даже в небольшом объёме.
  • Вам нужно предсказуемое поведение на известной форме входных данных.

Пример: обучение на парах (описание SQL-запроса, SQL-код) для задачи text-to-SQL.

{
    "input": "Get all users who signed up last month",
    "output": "SELECT * FROM users WHERE signup_date >= DATE_SUB(NOW(), INTERVAL 1 MONTH)"
}

3. Instruction tuning

Instruction tuning — частный случай SFT, предназначенный для того, чтобы модели следовали широкому набору инструкций на естественном языке. Датасет содержит пары (instruction, response) для множества разных задач.

Когда использовать:

  • Вы создаёте ассистента общего назначения (например, ChatGPT или Claude).
  • Модель должна обрабатывать разнообразные открытые запросы.
  • Вы создаёте чат-интерфейс.

Пример: обучение на тысячах разнообразных инструкций вроде «Суммируй эту статью», «Напиши стихотворение о X», «Объясни Y простыми словами».

Сравнение

АспектContinued Pre-trainingSFTInstruction Tuning
ДанныеНеобработанный текстПары (input, output)Пары (instruction, response)
РазметкаНет (unsupervised)Специфичная для задачиРазные задачи
ЦельДоменные знанияПоведение в конкретной задачеСледование любой инструкции
Объём данныхОбычно самый большой корпусОпределяется покрытием задачи и разнообразием ошибокОбычно шире, чем task-specific SFT

[!NOTE] Что делают на практике SFT и instruction tuning используют одну и ту же objective-функцию для следующего токена; различие — в широте и построении датасета. Continued pre-training — отдельный эксперимент, после которого нужно проверить как прирост в домене, так и регресс общих способностей.


Пайплайн файн-тюнинга из 7 этапов

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

Пайплайн из 7 этаповПайплайн из 7 этапов

Каждый этап опирается на предыдущий:

  1. Подготовка данных — определите единицу оценки, разделите данные, затем очистите и отформатируйте их
  2. Выбор модели — выберите подходящую базовую модель и загрузите веса
  3. Настройка обучения — сконфигурируйте оборудование, гиперпараметры и стратегию оптимизации
  4. Файн-тюнинг — запустите обучение SFT, DPO или ORPO
  5. Оценка — измерьте качество на бенчмарках и проверьте качество
  6. Деплой — экспортируйте модель и запустите её сервинг
  7. Мониторинг — отслеживайте качество, сопровождайте модель и выполняйте итерации

[!WARNING] Данные — фундамент Обучение воспроизводит систематические дефекты примеров. Проверьте labels, утечки, покрытие и соответствие политикам до того, как тратить время на перебор конфигураций оптимизатора.


Этап 1: подготовка данных

Многие проекты файн-тюнинга проваливаются именно здесь, а не на обучении. Современная подготовка данных — это больше, чем запуск regex по CSV.

Пайплайн данныхПайплайн данных

Пайплайн данных из 5 этапов

Инструменты вроде DataTrove и Distilabel помогают обрабатывать данные в масштабе. Проектируйте пайплайн на основе таксономии ошибок и data contract; выбор инструмента должен быть следствием этих требований.

1. Загрузка и фильтрация

  • Действие: удалите отказы («I cannot answer that»), повреждённый UTF-8 и тексты нецелевых языков.
  • Инструменты: Trafilatura для извлечения и модели идентификации языка fastText для language ID; распределённые модели lid.176 распознают 176 языков.

2. Политика работы с чувствительными данными

  • Действие: определите, чему модели разрешено обучаться, затем при необходимости замаскируйте, токенизируйте или исключите персональные и конфиденциальные поля.
  • Инструменты: Microsoft Presidio или scrubadub.
  • Зачем: детектор — лишь один из механизмов контроля; требования к происхождению данных, согласию, сроку хранения, доступу и удалению по-прежнему действуют.

3. Дедупликация (MinHash LSH)

  • Действие: удалите почти дубликаты, чтобы модель их не запоминала.
  • Инструменты: DataTrove хорошо подходит для обработки данных терабайтного масштаба.

4. Синтетическое расширение, если необходимо

  • Действие: используйте более сильную teacher-модель (GPT-4o, DeepSeek-V3), чтобы преобразовать необработанные данные в чистые пары instruction-response.
  • Инструменты: Distilabel.
  • Валидация: выберите примеры ответов teacher-модели, проверьте их по той же rubric, что и human labels, и держите синтетические и написанные людьми срезы раздельно при оценке.

5. Форматирование

  • Действие: преобразуйте данные в стандартный формат (Alpaca или ShareGPT).

Примеры форматов данных

Формат Alpaca (следование инструкциям):

{
    "instruction": "Summarize the following text.",
    "input": "The text to be summarized...",
    "output": "This is the summary."
}

Формат ShareGPT/ChatML (диалоговый):

{
    "conversations": [
        { "from": "user", "value": "Hello, who are you?" },
        { "from": "assistant", "value": "I am a helpful AI assistant." }
    ]
}

Что действительно важно

  • Покрытие важнее объёма. Добавляйте примеры, представляющие различные режимы отказа, а не повторы простого большинства.
  • Чистота. Удаляйте нерелевантный текст, нормализуйте пробелы и сохраняйте единообразное форматирование.
  • Баланс. Сохраняйте важные редкие случаи и отчитывайтесь о качестве по каждому срезу.
  • Разделение. Делите данные по источнику, пользователю, документу или времени, если случайное разделение строк создаёт утечки почти дубликатов.
  • Происхождение. Для каждой версии датасета фиксируйте источник, лицензию или разрешение, историю преобразований и путь удаления данных.

Этап 2: выбор модели и оборудования

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

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

  • лицензионные условия и правила распространения для предполагаемого продукта;
  • поведение в нужных языках, домене, tool use и безопасности до адаптации;
  • совместимость токенизатора и chat template с датасетом;
  • максимальное контекстное окно и поведение при truncation, необходимые для реальных примеров;
  • поддержку как в training framework, так и в целевом serving engine.

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

Оценивайте конкретный запуск, а не маркетинговый класс модели

Универсальной таблицы «размер модели → GPU» не существует. Пиковое потребление памяти меняется в зависимости от precision весов, состояния trainable-параметров, длины последовательности, размера micro-batch, activation checkpointing, реализации attention и накладных расходов фреймворка. Начните с оценки памяти, затем выполните короткий smoke test на максимальной длине в точном стеке, который будете использовать.

Компонент памятиПолный файн-тюнингLoRAQLoRA
Веса базовой моделиTraining precisionЗаморожены, обычно BF16/FP16Заморожены, обычно 4-bit NF4
ГрадиентыВсе trainable-весаВеса адаптераВеса адаптера
Состояния оптимизатораВсе trainable-весаВеса адаптераВеса адаптера
АктивацииЗависят от batch и длины последовательности в любом методеТа же зависимостьТа же зависимость

Оригинальная статья о QLoRA показывает, как в конкретной конфигурации разместить модель LLaMA 65B на одной GPU с 48 GB. Это полезная нижняя граница, но не обещание, что любая современная архитектура 70B, длина контекста, kernel или trainer поместятся на том же устройстве.

Расчёт памяти

Для модели с PP параметрами только на веса потребуется примерно 2P2P байт в BF16/FP16 или 0.5P0.5P байт при четырёх битах — до учёта метаданных квантизации и буферов рантайма. Полное обучение в стиле Adam добавляет градиенты, состояния оптимизатора и часто master-веса повышенной точности. LoRA позволяет избежать большей части памяти под trainable-состояния; QLoRA дополнительно уменьшает объём памяти под замороженные веса базовой модели. На длинных последовательностях доминировать всё равно могут активации.

Используйте следующий процесс:

  1. Выберите максимальную длину последовательности и micro-batch, которые обязаны поддерживаться.
  2. Оцените объём весов и trainable-состояний, оставив запас под активации и kernels.
  3. Выполните один шаг forward/backward на максимальной длине.
  4. Зафиксируйте пиковый объём выделенной и зарезервированной памяти.
  5. Только после этого увеличивайте размер batch, rank, длину последовательности или число GPU.

Этап 3: методы обучения (PEFT и LoRA)

Полный файн-тюнинг и PEFT

Полный файн-тюнинг (FFT) обновляет каждый вес, поэтому объём градиентов и состояний оптимизатора масштабируется вместе со всей моделью. Пиковое потребление нельзя вывести только из числа параметров, но оно значительно превышает память, необходимую для загрузки весов при инференсе.

Parameter-efficient fine-tuning (PEFT) обучает только небольшое подмножество параметров, замораживая остальные. Математика становится гораздо проще.

LoRA: отправная точка

LoRA (Low-Rank Adaptation) замораживает предобученную матрицу и представляет её выученное обновление двумя меньшими матрицами. В оригинальной статье это мотивируется гипотезой о том, что полезные обновления при адаптации имеют низкий intrinsic rank.

Архитектура LoRAАрхитектура LoRA

Для замороженной матрицы W0Rdout×dinW_0 \in \mathbb{R}^{d_{out} \times d_{in}} LoRA обучает:

  • ARr×dinA \in \mathbb{R}^{r \times d_{in}}
  • BRdout×rB \in \mathbb{R}^{d_{out} \times r}

Адаптированный слой:

W=W0+αrBAW' = W_0 + \frac{\alpha}{r}BA

Для этой матрицы адаптер имеет r(din+dout)r(d_{in}+d_{out}) trainable-параметров вместо dindoutd_{in}d_{out}. Для квадратной матрицы шириной 4 096 при rank 16 это сокращение в 128 раз для конкретной матрицы, а не в 10 000 раз для произвольной модели. Указанное в статье LoRA сокращение в 10 000 раз относится к конкретной конфигурации GPT-3 175B с адаптацией выбранных матриц.

Сравнение PEFT-методов

МетодЧто меняетсяКогда выбирать
LoRAЗамороженная база плюс trainable-обновления низкого рангаБазовая модель уверенно помещается в память и нужны небольшие артефакты под задачу
QLoRALoRA с замороженной базой, хранящейся в 4-bit форматеПамять под веса базовой модели — ограничивающий фактор
DoRAРазделяет magnitude веса и направление, обновляемое LoRAИзмеренный baseline LoRA оставляет разрыв в качестве, оправдывающий дополнительную сложность
Полный файн-тюнингВсе веса моделиPEFT не достигает цели, а прирост качества оправдывает распределённое обучение и полные чекпоинты

Когда что выбирать

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

DoRA: LoRA с декомпозицией весов

DoRA (Weight-Decomposed Low-Rank Adaptation) разделяет magnitude каждого вектора весов и его направление. В статье о DoRA обновление LoRA применяется к направленной компоненте, а magnitude обучается отдельно.

Архитектура DoRAАрхитектура DoRA

Как это работает:

Вместо того чтобы рассматривать веса как единое целое, DoRA разбивает предобученные веса на две компоненты:

  1. Magnitude — trainable-значение для каждого вектора весов.
  2. Направление — нормализованный вектор, обновляемый с помощью матриц низкого ранга.

В компактной column-wise нотации:

W=mV+BAV+BAcW' = m \frac{V + BA}{\lVert V + BA \rVert_c}

где:

  • m = magnitude (trainable)
  • VV = замороженная матрица направлений
  • BABA = выученное low-rank-обновление направлений
  • c\lVert \cdot \rVert_c = нормализация по столбцам

Что даёт эта дополнительная структура:

  • Больше степеней свободы, чем у стандартной LoRA, поскольку magnitude может меняться независимо.
  • Более высокое качество по сравнению с LoRA в нескольких конфигурациях, о которых сообщают авторы статьи.
  • Дополнительные параметры и вычисления, поэтому прирост нужно проверять на своей задаче и serving path.

Слияние адаптеров для multi-task learning

Отдельные адаптеры позволяют одной замороженной базе поддерживать несколько задач. Можно направлять запросы к нужному адаптеру, обслуживать несколько адаптеров одним движком, если это поддерживается, или создать offline-кандидат после слияния. Слияние может привести к интерференции, поэтому оценивайте объединённый артефакт, а не предполагайте, что исходные адаптеры корректно компонуются.

Распространённые методы слияния:

  1. Concatenation — объединение параметров адаптеров с увеличением effective rank. Быстро и просто.
  2. Linear combination — взвешенная сумма адаптеров. Даёт дополнительные рычаги настройки.
  3. SVD — разложение матрицы для слияния. Гибче, но медленнее.

Пример: один адаптер для суммаризации, другой для перевода, объединённые в единую multi-task-модель.


Этап 4: файн-тюнинг и выравнивание предпочтений

SFT обучает на демонстрациях. Preference optimization обучает на сравнениях вроде «chosen response A лучше, чем rejected response B». Используйте его только тогда, когда парные предпочтения действительно подходят как labels для ошибки; фактическую корректность и соответствие политикам часто нужно проверять более сильными эвалуаторами, чем одной общей оценкой предпочтений.

Методы выравниванияМетоды выравнивания

RLHF на основе PPO

Исходный рецепт представлял собой пайплайн из трёх этапов:

  1. SFT — обучение задаче.
  2. Reward model — обучение на человеческих предпочтениях (chosen против rejected).
  3. PPO (Proximal Policy Optimization) — reinforcement learning для оптимизации policy.

Операционная стоимость возникает из-за множества компонентов:

  • Сложно реализовывать и сопровождать.
  • Дорого: нужно обучать несколько моделей.
  • On-policy sampling и оптимизация reward требуют тщательного контроля стабильности и проверок reward hacking.

DPO

DPO (Direct Preference Optimization) отказывается от явной reward model и RL-цикла. В статье о DPO выводится перепараметризованная objective-функция для максимизации reward с ограничением KL-дивергенции, поэтому оптимизацию можно выполнять на preference pairs вместо reinforcement learning:

{
    "prompt": "Explain quantum computing",
    "chosen": "Quantum computing uses qubits...",   # Preferred response
    "rejected": "Well, it's complicated..."        # Non-preferred response
}

Что меняется с операционной точки зрения:

  • Более простой code path (нет отдельной reward model и RL-цикла).
  • Offline objective на preference pairs вместо on-policy reinforcement learning.
  • Reference policy или эквивалентные reference log probabilities в стандартной формулировке.

DPO проще прототипировать, чем полноценный пайплайн PPO, но это не автоматическое улучшение качества. Результаты зависят от исходной policy, качества пар, настроек loss, эффектов длины и протокола оценки. Сравнивайте DPO с SFT-чекпоинтом на одном и том же наборе отложенных preference- и task-тестов.

ORPO

ORPO (Odds-Ratio Preference Optimization) объединяет SFT-loss отрицательного логарифмического правдоподобия со штрафом odds ratio для rejected-ответов. Он устраняет отдельную reference model и может объединить обучение задаче и preference optimization в одном запуске.

Как это работает: ORPO использует комбинированный loss, который одновременно делает две вещи:

  1. Максимизирует likelihood chosen-ответа (обучает задаче).
  2. Штрафует rejected-ответ с помощью odds-ratio-компоненты (обучает предпочтениям).

Гиперпараметры, которые стоит знать:

from trl import ORPOConfig

config = ORPOConfig(
    learning_rate=8e-6,  # Very low, as recommended by the ORPO paper
    beta=0.1,            # Controls strength of preference penalty
    # ... other params
)
  • Learning rate: в экспериментах статьи использовались низкие значения; настраивайте его под свою модель, batch и данные, а не копируйте одно значение как универсальное правило.
  • Beta: определяет вклад preference-компоненты относительно SFT-компоненты.

Компромиссы:

  • Один этап обучения вместо двух.
  • Reward model не требуется.
  • Forward pass reference model не требуется.
  • Связанный запуск: если обучение задаче или preference-поведение деградирует, в этом пайплайне нет промежуточного SFT-чекпоинта для анализа.

Выбирайте метод на основе дизайна данных и оценки:

  • Используйте DPO, если у вас уже есть удовлетворительный SFT-чекпоинт и нужен более простой offline-эксперимент с предпочтениями.
  • Тестируйте ORPO, если вашим данным и операционным ограничениям подходит reference-free objective в один этап.
  • Используйте RLHF на основе PPO, если online sampling с явным обученным reward является требованием и вы можете отслеживать эксплуатацию reward.

Ни один метод не является универсальным default. Сохраняйте baseline только с SFT и отчитывайтесь как о task-метриках, так и о preference-метриках.


Фреймворки для файн-тюнинга

Фреймворки пересекаются по возможностям и быстро меняются. Выбирайте их под execution path, который обязаны поддерживать, фиксируйте версии и храните конфигурацию обучения в достаточно переносимом виде, чтобы воспроизводить запуск вне ноутбука.

Unsloth — скорость и эффективность использования памяти

Unsloth интегрируется с Hugging Face trl и transformers и предоставляет оптимизированные kernels, checkpointing и квантизированные пайплайны файн-тюнинга для поддерживаемых моделей.

  • Пользовательские Triton GPU kernels для attention, RoPE и cross-entropy, которые обходят накладные расходы PyTorch.
  • Эффективный по памяти backprop: активации пересчитываются во время backward pass вместо хранения в памяти.
  • Fused operations, объединяющие несколько шагов (layer norm + linear и аналогичные) в единые вызовы GPU.
  • 4-bit квантизация, напрямую встроенная в QLoRA path, с оптимизированной деквантизацией.

[!IMPORTANT] Порядок импорта важен Следуйте порядку импорта из примера Unsloth для зафиксированной вами версии. Unsloth применяет patches во время импорта, поэтому импорт до trl и transformers помогает избежать потери оптимизаций и version-specific ошибок.

# Correct order
from unsloth import FastLanguageModel  # Must be first!
from trl import SFTTrainer
from transformers import TrainingArguments

# Avoid this order with Unsloth's patched path
from trl import SFTTrainer
from unsloth import FastLanguageModel

Лучше всего подходит для: обучения на одной GPU, прототипирования, Colab-ноутбуков и всех, кто следит за счётом за GPU.

Опубликованные показатели скорости и памяти зависят от модели, длины последовательности, batch, precision и оборудования. Измеряйте tokens per second и пиковое потребление памяти на собственном запуске, а не воспринимайте рекламное соотношение как свойство фреймворка.

Axolotl — обучение через конфигурацию

# config.yaml - no code required
base_model: meta-llama/Meta-Llama-3-8B
adapter: qlora
lora_r: 32
lora_alpha: 16
datasets:
    - path: data/my_data.jsonl
      type: alpaca
sample_packing: true

Запуск: accelerate launch -m axolotl.cli.train config.yaml

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

Сравнение фреймворков

ИнструментКогда полезенЧто проверить до выбора
UnslothНужен оптимизированный путь для поддерживаемой модели с краткими примерамиМатрицу совместимости модели, GPU, квантизации и distributed-support
AxolotlНужны декларативные конфигурации и встроенные distributed-рецептыТочную схему конфигурации и launcher для зафиксированного релиза
TRLНужен прямой доступ к SFT- и preference-трейнерам Hugging FaceФормат датасета, chat template, loss masking и интеграцию с PEFT
TorchtuneНужны PyTorch-native-рецепты и компонентыПокрытие модельных рецептов и совместимость экспорта

Практический пример: файн-тюнинг с Unsloth

Ниже приведён показательный фрагмент обучения из моего репозитория unsloth-finetune-demo. В демо выполняется файн-тюнинг Nemotron-Nano для function calling. До этого фрагмента src/unsloth_demo/data.py демо загружает и форматирует датасет, а затем training path создаёт версионируемые объекты train_dataset и eval_dataset.

Пайплайн обученияПайплайн обучения

Быстрый старт

# Clone and setup
git clone https://github.com/slavadubrov/unsloth-finetune-demo.git
cd unsloth-finetune-demo

# Install with uv (recommended)
uv sync

# Run fine-tuning (quick test)
uv run finetune --max-samples 1000

Конфигурация

Основные настройки находятся в config.py:

# Model & Dataset
MODEL_NAME = "nvidia/Llama-3.1-Nemotron-Nano-4B-v1.1"  # 4B params, 128K context
DATASET_NAME = "glaiveai/glaive-function-calling-v2"   # 113K examples

# LoRA Configuration
LORA_R = 16        # Adapter capacity; tune against held-out results
LORA_ALPHA = 32    # Update scaling; alpha/r is the classic LoRA scale
MAX_SEQ_LENGTH = 4096

# Candidate modules for this Llama-family model
LORA_TARGET_MODULES = [
    "q_proj", "k_proj", "v_proj", "o_proj",
    "gate_proj", "up_proj", "down_proj",
]

[!NOTE] Соотношение alpha и rank alpha/r масштабирует классическое обновление LoRA. alpha = 2r — распространённая стартовая эвристика в документации некоторых инструментов, а не гарантия стабильности. Перебирайте rank, alpha, learning rate и target modules только после того, как зафиксированы данные и baseline.

Основной код обучения

from unsloth import FastLanguageModel
from trl import SFTConfig, SFTTrainer

# Load model with 4-bit quantization
model, tokenizer = FastLanguageModel.from_pretrained(
    model_name="nvidia/Llama-3.1-Nemotron-Nano-4B-v1.1",
    max_seq_length=4096,
    load_in_4bit=True,
)

# Add LoRA adapters
model = FastLanguageModel.get_peft_model(
    model,
    r=16,
    lora_alpha=32,
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
                    "gate_proj", "up_proj", "down_proj"],
    use_gradient_checkpointing="unsloth",  # Lower activation memory; extra compute
)

# The preceding data step must create these versioned dataset objects.
# Each row has the chat messages and tool schemas expected by current TRL.

# Train with the current TRL configuration surface.
trainer = SFTTrainer(
    model=model,
    processing_class=tokenizer,
    train_dataset=train_dataset,
    eval_dataset=eval_dataset,
    args=SFTConfig(
        output_dir="outputs/nemotron-function-calling",
        max_length=4096,
        packing=True,
        per_device_train_batch_size=2,
        gradient_accumulation_steps=4,
        learning_rate=2e-4,
        num_train_epochs=3,
        bf16=True,
    ),
)
trainer.train()

Файн-тюнинг с Axolotl

[!NOTE] Демо готовится Я работаю над практическим демо для Axolotl. Пока оно не готово, хорошим справочным материалом по стратегиям обучения на нескольких GPU будет Accelerate n-D Parallelism Guide от Hugging Face.

Для конфигураций, ориентированных на config-first подход, и распределённых setup-ов Axolotl делает workflow воспроизводимым:

# axolotl_config.yaml
base_model: meta-llama/Meta-Llama-3-8B
model_type: LlamaForCausalLM

# QLoRA configuration
load_in_4bit: true
adapter: qlora
lora_r: 32
lora_alpha: 16
lora_dropout: 0.05
lora_target_modules:
    - q_proj
    - k_proj
    - v_proj
    - o_proj
    - gate_proj
    - up_proj
    - down_proj

# Dataset
datasets:
    - path: data/training_data.jsonl
      type: alpaca

# Training settings
sequence_len: 4096
sample_packing: true # Benchmark with your length distribution
micro_batch_size: 2
gradient_accumulation_steps: 4
learning_rate: 0.0002
num_epochs: 3

# Current Axolotl key; verify support on the pinned release
bf16: true
attn_implementation: flash_attention_2

Запуск обучения:

axolotl train axolotl_config.yaml

Этап 5: оценка

Зафиксируйте evaluation contract до первого запуска. Как минимум сравнивайте tuned-чекпоинт с точной untuned-базой при одинаковых промпте, настройках декодирования и окружении инструментов. Сводное качество публикуйте только после проверки failure slices, которые должен был улучшить проект.

Отслеживайте четыре группы:

  1. Целевая задача: exact match, успешность выполнения, human rubric или другой результат, связанный с use case.
  2. Регрессии: общие способности и ранее поддерживаемые срезы задач, которые может ухудшить адаптация.
  3. Безопасность и политики: отказы, утечки данных, prompt injection или доменные ограничения.
  4. Операционные показатели: латентность, throughput, память, размер артефакта и стоимость в целевой serving-конфигурации.

Автоматизированные бенчмарки

Используйте lm-evaluation-harness для релевантных стандартизированных задач, но не как замену продуктовой оценке:

lm_eval --model hf \
    --model_args pretrained=./outputs/merged-model \
    --tasks hellaswag,arc_easy,mmlu \
    --batch_size 8

LLM-as-judge

Для субъективного качества большая модель может помогать с оценкой, но откалибруйте её на примерах, проверенных людьми, и скрывайте identity кандидата:

judge_prompt = """
Rate this response from 1-5 on:
- Relevance
- Accuracy
- Formatting

Response: {model_output}
Expected: {ground_truth}
"""

Доменно-ориентированная оценка

Откладывайте реальные примеры по источнику, пользователю, документу или времени, чтобы почти дубликаты не попадали в разные части split. Для function calling проверяйте всю trajectory: выбор инструмента, аргументы, результат выполнения, восстановление после ошибки и финальный ответ. На небольших выборках приводите доверительные интервалы или парные подсчёты побед/поражений и проверяйте каждую регрессию в критичном срезе.


Этап 6: деплой и форматы вывода

Выбирайте артефакт с учётом serving engine и плана rollback, а не только размера файла:

Форматы выводаФорматы вывода

1. LoRA-адаптер

uv run finetune  # Saves ~100-500MB adapter
  • Размер: пропорционален целевым модулям, rank, числу слоёв и dtype; обычно значительно меньше базовой модели.
  • Лучше всего подходит для: разработки, версионируемых task-адаптеров и движков с прямой поддержкой LoRA.
  • Дополнительный плюс: адаптеры можно менять без повторной загрузки базовой модели.

2. Слитая модель

uv run finetune --merge  # Creates a standalone full model
  • Размер: примерно равен полному чекпоинту базовой модели в выбранной output precision.
  • Лучше всего подходит для: движков или каналов распространения, которые не поддерживают отдельный адаптер.
  • Компромисс: артефакт больше, rollout медленнее, зато загрузка одной модели проще.

3. Формат GGUF

uv run finetune --gguf q4_k_m  # Creates ~2-4GB quantized model
  • Размер: зависит от модели; для вариантов Q4 примерно соответствует четырёхбитным весам плюс метаданные.
  • Лучше всего подходит для: инференса на CPU, Ollama, llama.cpp и edge-деплоя.
  • Варианты: q4_k_m (меньше), q5_k_m (выше fidelity весов), q8_0 (больше и выше fidelity). После конвертации измерьте влияние на качество задачи.

Этап 7: сервинг и мониторинг

С vLLM

# Serve the base and expose a PEFT adapter as a model name. This parser and
# template pair is for a Llama 3.1-compatible function-calling adapter.
vllm serve nvidia/Llama-3.1-Nemotron-Nano-4B-v1.1 \
    --enable-lora \
    --lora-modules function-calling=./outputs/adapter \
    --enable-auto-tool-choice \
    --tool-call-parser llama3_json \
    --chat-template examples/tool_chat_template_llama3.1_json.jinja \
    --host 0.0.0.0 \
    --port 8000 \
    --max-model-len 4096

Запрос через OpenAI-compatible API:

from openai import OpenAI

client = OpenAI(base_url="http://localhost:8000/v1", api_key="dummy")
response = client.chat.completions.create(
    model="function-calling",
    messages=[{"role": "user", "content": "Book a flight to Tokyo"}],
    tools=[
        {
            "type": "function",
            "function": {
                "name": "book_flight",
                "description": "Book a flight to a city.",
                "strict": True,
                "parameters": {
                    "type": "object",
                    "properties": {
                        "destination": {"type": "string"},
                    },
                    "required": ["destination"],
                    "additionalProperties": False,
                },
            },
        }
    ],
    tool_choice="auto",
)

Для tool calling guide vLLM требуются auto tool choice и parser, соответствующий модели; используйте совместимый с моделью chat template, если конфигурация её токенизатора не предоставляет. При tool_choice="auto" для constrained arguments также требуется strict: true хотя бы у одной function (при включённой в vLLM настройке strict-tool-calling, которая включена по умолчанию); используйте parameters schema, совместимую с strict-режимом. Без этого opt-in vLLM извлекает calls из raw text, поэтому аргументы могут оказаться malformed или нарушать схему.

С Ollama (локально)

# Create Modelfile
echo 'FROM ./outputs/unsloth-nemotron-function-calling-gguf/model-q4_k_m.gguf' > Modelfile

# Import to Ollama
ollama create my-function-model -f Modelfile

# Run
ollama run my-function-model

С llama.cpp (CPU)

./llama-cli -m ./outputs/model-q4_k_m.gguf \
    -p "What's the weather in Tokyo?" \
    --ctx-size 4096

Мониторинг выпущенной модели

Жизненный цикл не заканчивается на приемлемом training loss. Фиксируйте revision базовой модели, токенизатор и chat template, hash адаптера, версию датасета, конфигурацию обучения и отчёт об оценке как единую release unit. В продакшене отслеживайте успешность задачи, невалидные ответы, нарушения политик, латентность и drift входных данных по тем же срезам, что и offline. Сохраняйте предыдущий артефакт загружаемым и определите rollback threshold до запуска.


Основные выводы

  1. Выполняйте файн-тюнинг только после того, как untuned-baseline и таксономия ошибок покажут, что проблема решается адаптацией весов.
  2. Retrieval управляет изменяющимися источниками; constrained decoding управляет синтаксисом; SFT не заменяет ни один из этих подходов.
  3. LoRA сокращает объём trainable-состояний. QLoRA дополнительно сжимает замороженные веса базовой модели. Не приписывайте показатели памяти QLoRA методу LoRA.
  4. Покрытие данных, целостность split, provenance и loss masking важнее копирования модной конфигурации оптимизатора.
  5. DPO, ORPO и RLHF на основе PPO — разные дизайны экспериментов, а не ступени качества с универсальным default.
  6. Оценивайте целевое поведение, регрессии, безопасность и операционные характеристики относительно той же базовой модели.
  7. До обучения выберите adapter, merged или GGUF output на основе требований к сервингу и rollback.

Ссылки

Статьи и исследования

Инструменты обработки данных

Constrained decoding

  • xgrammar — constrained decoding с FSM
  • outlines — структурированная генерация для LLM

Фреймворки обучения

  • Unsloth — оптимизированный фреймворк для файн-тюнинга
  • Axolotl — обучение через конфигурацию и distributed-launchers
  • TRL — библиотека Hugging Face для SFT и обучения на предпочтениях
  • Torchtune — PyTorch-native-библиотека для файн-тюнинга

Инференс и деплой

  • LoRA-адаптеры vLLM — сервинг одного или нескольких адаптеров с базовой моделью
  • Tool calling в vLLM — согласование auto tool choice, parser, chat template и request schema с моделью
  • Ollama — локальный LLM runner для Mac/Windows/Linux
  • llama.cpp — инференс на CPU/GPU с форматом GGUF

Оценка

  • lm-evaluation-harness — стандартизированный LLM-бенчмаркинг от EleutherAI

Руководства и ресурсы