После обучения у вас на диске лежит папка на десятки мегабайт. Это адаптер, а не модель: он работает только вместе с базой, под которую обучен. Большинство инструкций сразу советуют «слить его в базу» — и почти никогда не объясняют, что вы при этом теряете.
#Зачем это нужно и когда не нужно
Слияние нужно ровно в одном случае: движок, в котором вы собираетесь запускать модель, не умеет загружать адаптеры отдельно. Это касается большинства массовых локальных движков — они ожидают один файл модели.
Не нужно, если вы работаете в среде, которая адаптеры понимает. Тогда раздельное хранение даёт то, ради чего адаптеры и придумали: одна база на диске и несколько сменных файлов поведения. Механика этого разобрана в статье про устройство адаптеров.
#Что понадобится
- Папка с адаптером после обучения (файлы адаптера и его конфигурация).
- Та же базовая модель, на которой обучали, — той же версии.
- Свободное место на диске: результат слияния весит столько же, сколько база, то есть гигабайты.
- Оперативная память под загрузку базы в полной точности. Слияние делается не в 4 битах.
#Шаг 1. Слияние
Разовый скрипт. Он загружает базу вместе с адаптером, вычисляет сумму весов и сохраняет результат как обычную модель.
from peft import AutoPeftModelForCausalLM
from transformers import AutoTokenizer
ADAPTER = "out/persona-lora"
MERGED = "out/persona-merged"
model = AutoPeftModelForCausalLM.from_pretrained(ADAPTER)
model = model.merge_and_unload() # поправка складывается с базовыми весами
model.save_pretrained(MERGED)
# Токенизатор переносится отдельно — без него модель не запустится
AutoTokenizer.from_pretrained(ADAPTER).save_pretrained(MERGED)
Две ловушки в этих пяти строках.
Токенизатор. Его надо сохранить явно. Забыли — получите папку с весами, которую нечем токенизировать, и обнаружите это только на запуске.
Точность загрузки. Сливать надо базу в её обычной точности, а не в 4 битах, даже если обучали в QLoRA. Слияние поправки со сжатыми весами вносит дополнительную ошибку — разбор компромисса в статье про LoRA и QLoRA.
#Шаг 2. Что делать с результатом
После слияния у вас обычная модель в формате библиотеки. Для локального запуска её ещё нужно перевести в формат вашего движка и, скорее всего, квантовать — полный маршрут в статье про конвертацию в GGUF и запуск локально.
Адаптер при этом не удаляйте. Он весит мегабайты, а даёт возможность собрать модель заново, если слитая версия окажется испорченной или база обновится.
#Проверка результата
Слияние прошло успешно, если:
- Папка результата содержит и веса, и файлы токенизатора. Отсутствие второго — самая частая поломка.
- Модель загружается без упоминания адаптера в конфигурации.
- Ответы на ваш проверочный набор совпадают с тем, что давала связка «база + адаптер». Не «похожи», а совпадают по манере: расхождение означает, что слияние выполнено в другой точности или с другой версией базы.
- Размер папки соответствует базе, а не адаптеру. Если результат весит десятки мегабайт, слияния не произошло.
Третий пункт проверяйте протоколом слепого сравнения — он же годится и для этой задачи, описан в статье про честную оценку дообученной модели.
#Таблица: когда сливать и когда нет
| Ситуация | Сливать | Почему |
|---|---|---|
| Запуск в массовом локальном движке | да | адаптеры не поддерживаются |
| Конвертация в формат для локального запуска | да | конвертер ожидает цельную модель |
| Передача модели другому человеку | да | одна сущность вместо двух |
| Несколько режимов на одной базе | нет | теряется переключение |
| Место на диске ограничено | нет | база останется одна |
| Планируются новые прогоны обучения | нет | обучать надо от базы, не от слитой версии |
| База может обновиться | нет | адаптер переносится, слитая модель нет |
Строки со «нет» сводятся к одному: слияние — операция односторонняя. Разделить слитую модель обратно на базу и адаптер нельзя.
#Что может пойти не так
- Не сохранён токенизатор. Модель не запускается или выдаёт мусор. Сохраняйте явно, как в скрипте выше.
- Слияние в 4 битах. Результат чуть хуже связки «база плюс адаптер», и разница проявляется не сразу. Сливайте в обычной точности.
- Не хватило памяти. Слияние требует держать базу целиком. Если оперативной памяти мало, делайте слияние там же, где обучали, — на арендованной машине.
- Другая версия базы. Адаптер обучен под конкретные веса; слияние с обновлённой базой формально пройдёт, а поведение будет непредсказуемым.
- Слитая модель отвечает иначе, чем связка. Проверьте точность загрузки и версию базы. Совпадение поведения — критерий корректного слияния.
- Адаптер удалён после слияния. Восстановить его из слитой модели невозможно. Держите оба.
Полный маршрут от датасета до запущенной локально модели, куда этот шаг встроен, — в гайде как обучить вторую копию на QLoRA, а общая рамка — в пилларе про свою модель и дообучение под себя.
#Частые вопросы
#Можно ли разделить слитую модель обратно?
Нет. Слияние складывает поправку с базовыми весами, и восстановить слагаемые из суммы нельзя. Именно поэтому адаптер стоит хранить: он и есть ваша страховка.
#Слитая модель работает быстрее?
Немного, потому что исчезает дополнительное вычисление на каждом затронутом слое. На практике разница обычно теряется на фоне общей скорости генерации — сливают не ради скорости, а ради совместимости.
#Можно ли слить два адаптера сразу?
Технически можно сложить обе поправки, но результат никто не проверял: два независимо обученных адаптера при одновременном применении дают поведение, которого не было ни в одном эксперименте. Нужна комбинация — обучайте один адаптер на смешанном датасете.
#Что делать, если базовая модель обновилась?
Переобучить адаптер на новой базе и слить заново. Перенос старого адаптера на обновлённые веса формально возможен, но проверять его придётся полностью, и обычно это дороже нового прогона.
#Сколько весит результат слияния?
Столько же, сколько базовая модель, — гигабайты. Экономия адаптерного подхода при слиянии исчезает: три слитых режима это три полных копии базы, тогда как три адаптера — это база плюс десятки мегабайт.
#Вывод
Слияние — техническая операция ради совместимости, а не улучшение модели. Качество от него не растёт, а гибкость падает: вы меняете «база плюс сменный файл поведения» на монолит.
Правило простое: сливайте, если ваш движок запуска не понимает адаптеры или если модель надо кому-то передать. Во всех остальных случаях держите базу и адаптер раздельно — и не удаляйте адаптер даже после слияния.