Про LoRA обычно рассказывают так: «обучаются не все веса, а маленькая добавка». Объяснение верное и бесполезное — из него не следует ни одного практического решения. Если разобрать механику на один уровень глубже, из неё сразу выпадает следствие, которое меняет то, как вы храните и используете свои дообученные модели.
#Короткий ответ
PEFT — семейство методов «параметрически эффективного дообучения» и одноимённая библиотека, которая их реализует. Общий принцип: базовые веса замораживаются, рядом добавляется небольшой обучаемый блок, обучается только он.
Практическое следствие, ради которого стоит читать дальше: результат обучения — это отдельный файл на десятки мегабайт, не связанный с базой физически. Значит, одну базовую модель можно держать на диске один раз и подключать к ней разные адаптеры — рабочий, личный, экспериментальный, — переключая поведение без перекачивания гигабайтов.
#Как это устроено
Возьмём один линейный слой модели. Он умножает вход на матрицу весов W — большую, размером, скажем, 4096 на 4096, то есть около семнадцати миллионов чисел.
LoRA не трогает W. Вместо этого рядом ставятся две матрицы: A размером 4096 на r и B размером r на 4096, где r — ранг, обычно 8, 16 или 32. Выход слоя становится суммой:
выход = x @ W + (x @ A) @ B * (alpha / r)
Первое слагаемое — исходное поведение модели, оно неизменно. Второе — обучаемая поправка. При r = 16 две матрицы вместе содержат около ста тридцати тысяч чисел вместо семнадцати миллионов, то есть меньше процента.
Три детали, которые объясняют, почему это работает:
Aинициализируется случайно,Bнулями. В начале обучения второе слагаемое равно нулю, и модель ведёт себя ровно как базовая. Обучение начинается не с испорченной модели, а с исходной.- Множитель
alpha / rмасштабирует вклад поправки. Именно поэтомуalphaпринято брать пропорциональноr— иначе изменение ранга незаметно меняет ещё и силу воздействия. - Ранг — это ёмкость. Он ограничивает, сколько разного поведения адаптер способен вместить. Низкий ранг на сложной задаче не выучит; высокий ранг на маленьком датасете заучит датасет.
Почему добавка малого ранга вообще способна что-то изменить: изменения весов при дообучении на узкой задаче оказываются низкоранговыми по своей природе — модель не перестраивается целиком, она смещает небольшое число направлений. LoRA просто не тратит память на то, что всё равно останется нулём.
Что происходит, когда базу дополнительно сжимают до четырёх бит, и чем за это платят, — в статье про LoRA и QLoRA.
#Куда ставятся адаптеры
Не в каждый слой. Стандартная практика — механизм внимания и полносвязные блоки: проекции запроса, ключа, значения и выхода плюс блоки прямого прохода. В конфигурациях это выглядит как список имён вроде q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj — ровно такой список стоит в скрипте обучения нашей «второй копии».
Практическое правило: ставьте на все линейные слои, если не знаете точно, зачем сужать список. Экономия от сокращения набора невелика, а потеря в качестве бывает заметной. Инструменты обычно дают флаг «все линейные» — им и пользуйтесь.
#Что из этого следует на практике
Адаптер — отдельный артефакт. Файл на десятки мегабайт. Его можно положить в репозиторий, отправить письмом, версионировать. База при этом лежит одна.
Адаптеры переключаются. Библиотека умеет держать несколько загруженных адаптеров и активировать нужный. Отсюда сценарий, ради которого всё это интересно: одна база, три поведения.
Адаптер можно слить в базу. Тогда получается обычная модель без внешних зависимостей — это нужно, когда рантайм адаптеры не понимает. Что при этом теряется — разобрано в статье про слияние адаптера с моделью.
Адаптер привязан к своей базе. Он обучен под конкретные веса конкретной версии модели. Переносить его на другую базу того же семейства технически можно, а осмысленно — почти никогда.
#Сценарий: несколько адаптеров на одной базе
Разберём его конкретно, потому что это главный неочевидный выигрыш.
Допустим, вы хотите три режима: рабочий (сухой, структурированный), личный (разговорный) и предметный (терминология вашей области). Наивный путь — три дообученные модели по несколько гигабайт каждая. Путь с адаптерами:
| Что храните | Размер | Сколько раз |
|---|---|---|
| Базовая модель | гигабайты | один |
| Адаптер «рабочий» | десятки мегабайт | один |
| Адаптер «личный» | десятки мегабайт | один |
| Адаптер «предметный» | десятки мегабайт | один |
Экономия по диску очевидна. Менее очевидны два других следствия. Первое: переключение режима не требует перезагрузки базы, а значит, происходит быстро. Второе: каждый адаптер обучается и проверяется независимо — вы можете переобучить «рабочий», не трогая остальные.
Ограничение, о котором стоит знать: складывать несколько адаптеров одновременно можно, но результат непредсказуем. Два адаптера, обученные независимо, при одновременном применении дают поведение, которого не проверял никто. Если вам нужна комбинация — обучайте один адаптер на смешанном датасете, а не складывайте два готовых.
Что вообще означает «число параметров модели» и почему доля обучаемых так мала — в справке про параметры модели.
#Где интуиция подводит
«Больше ранг — лучше результат». Не лучше, а ёмче. На датасете в несколько сотен примеров высокий ранг даёт заучивание: модель начинает воспроизводить обучающие строки дословно. Начинайте с 8–16.
«Адаптер добавляет модели знания». Адаптер смещает поведение. Знания живут в базе и в подставленном контексте; попытка вшить факты в адаптер даёт уверенные ошибки вместо точных ответов.
«Раз адаптер маленький, обучение дешёвое по памяти целиком». Дешёвое по градиентам и состояниям оптимизатора, но замороженная база всё равно лежит в памяти полностью — именно она обычно и определяет, влезет ли обучение в вашу карту.
«Адаптер замедляет модель на запуске». При раздельной загрузке — немного, потому что добавляется второе слагаемое в каждом затронутом слое. После слияния в базу накладных расходов не остаётся вовсе.
Как эта механика настраивается в инструментах — в гайде по Unsloth и в разборе конфигурационного подхода Axolotl. Общая рамка маршрута — в пилларе про свою модель и дообучение под себя.
#Частые вопросы
#Сколько весит адаптер?
Обычно десятки мегабайт: размер определяется рангом, числом затронутых слоёв и размерностью модели, а не размером базы. Для сравнения, базовая модель на несколько миллиардов параметров занимает гигабайты. Именно эта разница делает хранение нескольких адаптеров практичным.
#Можно ли использовать чужой адаптер?
Только с той базой и той версией, под которую он обучен. Файл адаптера содержит поправку к конкретным весам; приложенный к другой модели, он даст в лучшем случае шум. Проверяйте, какая база указана в его конфигурации.
#Что выбрать, кроме LoRA?
В семействе есть и другие методы — обучение мягких подсказок, добавление отдельных блоков. Для задачи переноса стиля на языковую модель LoRA остаётся практическим стандартом: она хорошо изучена, поддержана всеми инструментами и умеет сливаться в базу.
#Обязательно ли сливать адаптер перед использованием?
Нет, если ваш рантайм умеет загружать базу и адаптер раздельно — так вы сохраняете возможность переключения. Слияние нужно, когда движок запуска ожидает единый файл модели. Это односторонняя операция: обратно разделить нельзя.
#Как понять, какой ранг взять?
Начните с 8 или 16 и меняйте, только если есть основание. Стиль не сдвинулся при достаточном датасете — повышайте. Модель повторяет обучающие примеры — понижайте либо уменьшайте число эпох. Ранг подбирается последним, после датасета и числа эпох.
#Вывод
Адаптер — это две маленькие матрицы рядом с большой замороженной и множитель перед ними. Всё остальное в теме дообучения — производные от этой конструкции: почему обучение помещается в домашнюю карту, почему результат весит мегабайты и почему его можно переключать.
Практический вывод один: не думайте о дообученной модели как о новой модели. Думайте о ней как о базе плюс сменном файле поведения — и вы сразу получите и экономию диска, и возможность держать несколько режимов, и понятный способ откатиться к исходному состоянию.