← LLM fine-tuning: LoRA, QLoRA, and when not to do it
06 / 07 · 50 min

Serving: adapter merging, quantization, cost

Merge the adapter into the weights or serve a fleet of LoRAs on one base, GGUF versus AWQ, re-running the eval after quantization — and an honest cost calculation of your own GPU versus an API.

Lesson content is currently available in Russian.

What you will learn

  • →Выбирать между merge_and_unload и multi-LoRA-сервингом под свой сценарий.
  • →Квантовать модель под инференс (GGUF, AWQ) и перепроверять качество на квантованном артефакте.
  • →Считать стоимость самостоятельного сервинга против API из пропускной способности и загрузки.

Мёрж или адаптеры

После обучения у вас база и адаптер на десятки мегабайт. Дальше два пути.

Мёрж: merge_and_unload() вливает поправку B·A прямо в веса — получается обычная standalone-модель без LoRA-зависимостей:

from peft import PeftModel
from transformers import AutoModelForCausalLM

base = AutoModelForCausalLM.from_pretrained(
    "Qwen/Qwen2.5-7B-Instruct", torch_dtype="bfloat16"
)
merged = PeftModel.from_pretrained(base, "out/adapter").merge_and_unload()
merged.save_pretrained("out/merged")

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

Критичная деталь: мёржить нужно в полновесную (bf16/fp16) базу. Влить адаптер в 4-битную базу без потерь нельзя — веса уже округлены; порядок всегда «мёрж в bf16 → потом квантизация результата».

Раздельный сервинг: движки вроде vLLM умеют держать одну базу и подключать много LoRA-адаптеров одновременно (--enable-lora), маршрутизируя запросы по имени адаптера. Это выигрывает, когда специалистов много: десять адаптеров по 50 МБ на общей базе против десяти полных копий модели. Цена — небольшой оверхед латентности и привязка к движку с поддержкой LoRA.

Эвристика: один специалист в проде — мёржьте; парк специалистов или мультитенантность — сервите адаптеры раздельно.

Квантизация под инференс

Квантизация обучения (NF4 в QLoRA) и квантизация сервинга — разные вещи с разными форматами. Для инференса выбор диктуется железом.

GGUF (llama.cpp) — формат для CPU, Apple Silicon и малых GPU. Уровни именуются как Q4_K_M, Q5_K_M, Q8_0: для 8B-модели Q4_K_M весит около 5 ГБ и считается сбалансированным дефолтом, Q8_0 — почти без потерь, но вдвое тяжелее. Конвертация — скриптом convert_hf_to_gguf.py из репозитория llama.cpp плюс утилита квантизации.

AWQ и GPTQ — 4-битные форматы для GPU-сервинга под vLLM и другие движки: калибруются на небольшом наборе данных и держат высокую пропускную способность батчевого инференса.

Главное правило: после квантизации ваш eval прогоняется заново на квантованном артефакте. Perplexity, которой любят отчитываться, — слабый прокси: она может почти не сдвинуться, в то время как именно ваш узкий навык — тот самый тюненый формат или точность разбора — просядет заметно. Квантизация срезает «тонкие» веса, а тонкой как раз может оказаться ваша свежеобученная поправка. Сравните win rate fp16 против Q4: просадка на пару пунктов обычно приемлема, двузначная — повод подняться на уровень выше (Q5/Q6 или 8 бит) или пересмотреть формат.

Стоимость: свой GPU против API

Стоимость самостоятельного сервинга считается из трёх чисел: цена GPU-часа, пропускная способность и загрузка.

цена за 1 млн токенов = (цена GPU в час) / (токенов_в_сек × 3600 / 1 000 000)

Пример: L4 или A10G в облаке стоит порядка 0,6–1,2 доллара в час. Квантованная 8B под vLLM с батчингом выдаёт на такой карте порядка 1000–2500 токенов в секунду суммарной пропускной способности. При полной загрузке выходит примерно 0,1–0,3 доллара за миллион выходных токенов — конкурентно с ценами API на модели этого класса.

Ключевое слово — «при полной загрузке». GPU, арендованный на месяц и загруженный на 5%, умножает вашу фактическую цену за токен в двадцать раз, и API становится дешевле при всех его наценках. Отсюда правила:

  • Малый или рваный трафик → API или serverless-инференс со scale-to-zero; постоянный GPU не окупится.
  • Стабильный высокий трафик → свой GPU с батчингом; следите за метрикой утилизации, а не только за ценой инстанса.
  • Средний случай → хостинг тюненых весов у провайдеров (многие сервят кастомные модели с оплатой по токенам) как компромисс без DevOps.

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

vLLM в бою и последняя ловушка

Поднять смёрженную модель в vLLM — одна команда:

vllm serve out/merged --max-model-len 8192

# или парк адаптеров поверх общей базы:
vllm serve Qwen/Qwen2.5-7B-Instruct \
  --enable-lora \
  --lora-modules reviewer=out/adapter

Сервер отдаёт OpenAI-совместимый API: клиентский код с чат-комплишенами работает без изменений, для мульти-LoRA имя адаптера передаётся в поле model.

Последняя ловушка развёртывания — снова chat-шаблон. Движок применяет шаблон из tokenizer_config.json; если при обучении вы кастомизировали шаблон или спецтокены, а в прод уехал дефолтный, получите ту же тихую деградацию, что и в уроке про данные, — только теперь в проде. Смоук-тест обязателен: отправьте на боевой эндпоинт три точных промпта из held-out и сравните ответы с локальной генерацией.

Финальный чек-лист сервинга: мёрж в bf16 → квантизация → повторный eval на артефакте → совпадение chat-шаблона → смоук-тест на боевом эндпоинте → мониторинг латентности и утилизации. После этого модель можно отдавать пользователям.

Practice

Задеплоить и переоценить

Смёржите адаптер в bf16-базу, квантуйте (GGUF Q4_K_M или AWQ), поднимите эндпоинт (llama.cpp или vLLM), прогоните eval из прошлого урока на квантованном артефакте и посчитайте стоимость миллиона токенов для вашего трафика.

Deliverable

Работающий эндпоинт, таблица «fp16 против квант» по вашей метрике, расчёт цены за миллион токенов при реальной загрузке и вывод: свой GPU, serverless или API.

Checkpoint

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

  • ✓ Выбор мёрж/мульти-LoRA обоснован сценарием: один специалист или парк адаптеров.
  • ✓ Eval прогнан заново на квантованном артефакте, а не только на fp16.
  • ✓ Стоимость миллиона токенов посчитана из реальной пропускной способности и загрузки.

Check yourself

Почему нельзя мёржить адаптер в 4-битную базу?

01Почему нельзя мёржить адаптер в 4-битную базу?

Когда раздельный сервинг адаптеров выгоднее мёржа?

02Когда раздельный сервинг адаптеров выгоднее мёржа?

Что обязательно сделать после квантизации под инференс?

03Что обязательно сделать после квантизации под инференс?

GPU арендован помесячно и загружен на 5%. Что с ценой за токен?

04GPU арендован помесячно и загружен на 5%. Что с ценой за токен?

Какой формат нужен для инференса через llama.cpp?

05Какой формат нужен для инференса через llama.cpp?

Sources

Serving: adapter merging, quantization, cost · LLM fine-tuning: LoRA, QLoRA, and when not to do it · VORCL