RAG или дообучение: когда что, и сколько это стоит
Спектр адаптации модели: feature extraction, LoRA, частичное и полное дообучение. Разбираем, когда RAG не хватает и как выбрать метод под задачу и железо.
Почему этот вопрос возникает
Есть два способа вложить в модель то, чего она не знает. Первый — добавить знания снаружи: положить документы в индекс, подобрать релевантные чанки, передать их в контекст. Это retrieval-augmented generation, RAG. Второй — изменить саму модель: взять предобученные веса и переучить часть из них на своих данных. Это fine-tuning.
Документная половина RAG — отдельная работа: привести файл к виду, который модель читает, и не потерять по дороге таблицы, форматирование и объединённые ячейки. У нас эту часть закрывает Contextum.
Обычно RAG выбирают по умолчанию, и на то есть причины: он не требует вычислительного центра, его можно обновить за минуты, и он оставляет в ответе ссылку на источник, по которой можно проверить факт. Дообучение выглядит дороже и менее гибко. Но у RAG есть границы, и в Enterprise-системах они проявляются регулярно.
RAG плохо работает в трёх случаях. Первый: задача требует не факта, а поведения. «Ответь в формате XML по такой-то схеме», «классифицируй обращение по 12 фиксированным категориям», «исправь тон ответа так, чтобы он звучал как наш». Знание тут ни при чём — нужна выученная процедура.
Второй: инструкция длинная и повторяется на каждом вызове. Если требование занимает 800 токенов и уходит в каждый запрос, вы платите за эти токены и задержку при каждом вызове. Выученное поведение в весах стоит один раз.
Третий: требуется изолированный контур. Если данные нельзя отдавать наружу — ни в облачную модель, ни в индекс, — дообучение локальной модели оставляет данные на своей машине.
Спектр, а не один переключатель
Fine-tuning — не один метод, а спектр. Техники различаются тем, сколько параметров модели им разрешено менять, и это напрямую определяет требования к данным, железу и к тому, насколько модель сохранит общие способности.
| Техника | Что меняется | Данные и вычисления | Когда применять |
|---|---|---|---|
| Feature extraction | Веса модели заморожены, обучается только новая «голова» | сотни–тысячи примеров; вычисления низкие, хватает CPU | Классификация или регрессия поверх эмбеддингов, когда задача близка к тому, что модель уже умеет |
| PEFT (LoRA) | Базовая модель заморожена, обучаются небольшие адаптерные матрицы | объём умеренный; вычисления низкие–умеренные, реально на одной потребительской видеокарте или на Apple silicon | Рабочая лошадка большинства прикладных проектов |
| Частичное дообучение | Размораживаются последние слои, ранние остаются заморожены | объём умеренный; вычисления умеренные | Когда возможностей адаптеров не хватает, а обучать всю модель слишком дорого |
| Полное дообучение | Меняются все параметры | тысячи и больше примеров; вычисления высокие, нужна реальная GPU с памятью под градиенты и состояния оптимизатора | Максимальная адаптация, когда есть данные, есть вычислительный бюджет, а задача сильно отличается от предобучения |
Порядок в таблице — не случайный: техники выстроены по тому, сколько весов модели им разрешено менять. Первым идёт feature extraction — он ничего не ломает: исходная модель остаётся нетронутой, рядом появляется маленькая обучаемая надстройка. Если её достаточно, задача закрыта, а общие способности модели остаются нетронутыми.
Следом идёт LoRA: базовая модель заморожена, меняются только адаптерные матрицы. Обучается мало, но не поверх модели, а внутри неё — низкоранговые матрицы встраиваются в слои и подстраивают их поведение.
Дальше граница проходит по числу размороженных весов. Частичное дообучение размораживает последние слои, полное — все параметры. На этом участке спектра растёт и объём требуемых данных, и расход памяти — вплоть до разницы в десятки гигабайт, о которой речь ниже.
Почему LoRA вытеснил полное дообучение
Полное дообучение требует памяти на порядок больше, чем кажется. Чтобы обновить параметр, нужно хранить не только сам вес, но и его градиент, и состояние оптимизатора. Для большой модели это десятки гигабайт VRAM, которые нужно удерживать одновременно с самой моделью.
LoRA решает это принципиально иначе. Базовая модель замораживается, а вместо обновления её весов в сеть добавляются низкоранговые адаптерные матрицы — маленькие trainable-параметры, которые перехватывают путь сигнала. Обучается сотня или тысяча миллионов параметров вместо миллиардов.
По данным исследований, которые приводит Вэй-Мэн Ли в обзоре fine-tuning в журнале CODE, LoRA восстанавливает 95–99% качества полного дообучения на большинстве задач — при существенно меньшем расходе памяти и места на диске. И есть второй эффект, который важнее: поскольку базовые веса не трогаются, общие способности модели сохраняются заметно лучше. Риск катастрофического забывания — когда модель забывает всё, чему её научили раньше, и хороша только в узкой задаче, — при LoRA заметно ниже.
Отсюда практическое правило, которое формулирует Ли: начинайте с feature extraction, если не хватает — сразу переходите к LoRA. Полное дообучение — инструмент для меньшинства случаев, где есть и данные, и аппаратный бюджет, чтобы это оправдать.
Как выглядит код
Стек Hugging Face устроен так, что один и тот же код работает на Windows, macOS и Linux: библиотеки сами выбирают самое быстрое доступное железо. Метод адаптации задаёт PEFT — библиотека, в которой реализован и LoRA. Дообучение начинается с выбора метода.
from peft import LoraConfig, TaskType, get_peft_model
from transformers import AutoModelForCausalLM
base = AutoModelForCausalLM.from_pretrained(
"google/gemma-4-12B-it",
dtype="auto",
device_map="auto",
)
config = LoraConfig(
task_type=TaskType.CAUSAL_LM,
r=16, # ранг адаптера
lora_alpha=32, # масштаб обновления
lora_dropout=0.05,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
)
model = get_peft_model(base, config)
model.print_trainable_parameters()
Последняя строка печатает то, ради чего всё затевалось: сколько параметров обучаемо и какой это процент от общей модели. В примере из обзора на DistilBERT это выглядит как trainable params: 741,893 || all params: 67,699,210 || trainable%: 1.0959 — то есть обучается около процента, а остальное заморожено.
Базовые веса в примере — google/gemma-4-12B-it с Hugging Face.
Полное дообучение отличается одной строкой — вместо адаптера параметры модели размораживаются напрямую:
for parameter in model.parameters():
parameter.requires_grad = True
Именно здесь появляется требование к VRAM: полный набор параметров, их градиенты и состояния оптимизатора должны поместиться одновременно. На потребительской видеокарте это, как правило, недостижимо для моделей рабочего размера.
Разница видна и по тому, что именно сохраняется на диск. При LoRA рядом с базовой моделью лежат файлы адаптера — обычно десятки мегабайт. При полном дообучении сохраняется новая копия всех весов, и это уже десятки гигабайт на каждую версию модели. Практическое следствие для команд: адаптеры можно держать в git рядом с кодом, экспериментировать с ними и откатываться, а полные веса приходится хранить отдельно, как артефакт.
Есть и третий вариант, который в .NET-контуре выглядит естественнее всего: оставить дообучение на стороне Python-сервиса и вызывать его по HTTP. Модель обучается там, где есть GPU, а .NET-приложение работает с уже обученным адаптером через привычный эндпоинт. Оркестратор при этом остаётся в .NET — разделяется только обучение, а не архитектура.
Порядок принятия решения
Пять условий, при которых fine-tuning оправдан. Их проверяют до того, как команда начинает считать бюджет на обучение.
Формат. Задача требует конкретного формата или стиля вывода, который промптинг не обеспечивает надёжно. Классификатор, который обязан вернуть одну из 12 меток, — классический пример.
Инструкция. Поведение нужно «вшить», чтобы не повторять длинную инструкцию в каждом промпте. При заметном объёме вызовов это экономит и токены, и латентность.
Примеры. Паттерн трудно описать словами, но легко показать примерами: тон компании, доменные сущности, специфический разбор документов.
Офлайн. Модель должна работать офлайн, на устройстве, в изолированном контуре — и меньшая дообученная модель на узкой задаче обходит большую универсальную.
Исчерпанный RAG. RAG не дал нужного результата. RAG отлично даёт модели новые факты, но плохо учит новому поведению или стилю. Это принципиальное различие, а не вопрос настройки индекса.
Если ни одно условие не выполняется, начинать нужно с промптинга или RAG: они стоят только времени и итерируются за секунды. Дообучение — это инвестиция времени, данных и вычислений, и на выходе легко получить модель, которая хуже базовой во всём, кроме того, чему её натренировали.
Отдельно стоит сказать про порядок экспериментов, потому что он определяет бюджет. Правильная последовательность — снижать неопределённость на каждом шаге, а не начинать с самого дорогого:
- Сначала промпт. Если модель не соблюдает формат — уточняйте инструкцию, добавляйте примеры, разбирайте ответ на части.
- Затем RAG. Если не хватает фактов — сначала чините извлечение и ранжирование, а не дообучение.
- Затем feature extraction. Если задача сводится к классификации поверх эмбеддингов — обучаете маленькую «голову», базовая модель не трогается.
- Затем LoRA. Когда поведение или стиль закрепить иначе нельзя, и данных хватает хотя бы на сотни примеров.
- Полное дообучение — последний шаг, и только если остальные не дали результата.
Шаг, который чаще всего пропускают, — классификация поверх эмбеддингов. Дорабатывать под классификацию всю модель, когда достаточно обучить поверх эмбеддингов линейный классификатор, — это обычно на порядок дороже при сопоставимом результате. Такое обучение занимает минуты и десятки мегабайт, а не часы и десятки гигабайт.
Как проверить, что дообучение дало эффект
Дообучение меняет модель, поэтому старую методику оценки на месте не заменить. Проверять нужно две вещи отдельно: качество на целевой задаче и сохранность общих способностей.
Целевая задача измеряется на отложенном наборе, которого модель не видела в обучении. Здесь важна честность разделения: примеры из теста, просочившиеся в обучение, дают качество, которого в продакшене не будет.
К формату обучающей выборки стоит отнестись так же строго, как к коду. Данные для дообучения — это тот же интерфейс системы, только для модели: если формат входа различается между обучением и продакшеном, замер на отложенном наборе покажет качество, которого в бою не будет. Практическое правило: набор для обучения и набор для проверки строятся одним кодом из одного источника, а не копируются вручную.
Отдельная тонкость — длина примеров. Длинные диалоги в обучении учат модель воспроизводить их длину, а не решать задачу. Если средняя длина примера в обучении вдвое больше, чем в бою, вы получите модель, которая раздувает ответы. Стоит проверять распределение длин до обучения, а не после.
Общие способности проверяются отдельным набором. Это тот случай, где «модель стала лучше в своей задаче» и «модель стала лучше» — разные утверждения. Замер только целевой задачи покажет рост и промолчит о деградации.
Практический порядок выглядит так: зафиксировать baseline на текущей модели с промптом и RAG, затем обучить LoRA, затем измерить обе оси на одном и том же наборе. Если целевая задача не дала прироста, дообчение не окупило потраченного — и это дешёвый вывод, если сделать его до того, как модель поедет в контур.
Практические выводы
- Fine-tuning — это спектр из четырёх техник, а не один переключатель. Порядок применения: feature extraction, затем LoRA, и только потом полное дообучение.
- LoRA даёт 95–99% качества полного дообучения на большинстве задач при заметно меньшем расходе памяти и меньшем риске катастрофического забывания.
- Полное дообучение требует видеокарты, в которой одновременно помещаются веса, градиенты и состояния оптимизатора. Это вопрос бюджета, а не настройки.
- RAG и дообучение решают разные задачи: RAG даёт новые факты, дообучение учит новому поведению. Одно не заменяет другое.
- Любое дообучение требует двух замеров: прирост на целевой задаче и отсутствие деградации общих способностей.
Похожие статьи
- Архитектура enterprise AI: четыре решения, которые всё определяют
- RAG на .NET: архитектура, которая переживает продакшн
- Оценка качества RAG на .NET: Recall@5, MRR и гейт релиза
- Инженерное лидерство в AI-продуктах: оценка, роли, поставка
CTA
Если задача упирается в «модель не соблюдает формат» или «инструкция слишком дорогая на каждом вызове», начните с замера: зафиксируйте baseline на промпте и на RAG, а решение о дообучении принимайте по разнице. Чаще всего оказывается, что хватает промпта и структуры ответа — и тогда бюджет команды остаётся на измерение качества, а не на обучение.
Разобрать задачу под ваш стек — напишите нам.
Contextum: конвейер документов в Markdown для AI-агента
AnetLab Contextum: конвейер из пяти стадий между агентом и LLM — конвертация в Markdown, сжатие контекста, идемпотентная доставка и честный реестр потерь.
Оценка качества RAG на .NET: Recall@5, MRR и гейт релиза
Как измерять качество RAG на .NET: Golden Set из 50–100 вопросов, расчёт Recall@5 и MRR, измерение faithfulness и eval-как-гейт-релиза в CI.