Своя модель

PEFT: как устроены адаптеры под капотом

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

Про 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 и меняйте, только если есть основание. Стиль не сдвинулся при достаточном датасете — повышайте. Модель повторяет обучающие примеры — понижайте либо уменьшайте число эпох. Ранг подбирается последним, после датасета и числа эпох.

#Вывод

Адаптер — это две маленькие матрицы рядом с большой замороженной и множитель перед ними. Всё остальное в теме дообучения — производные от этой конструкции: почему обучение помещается в домашнюю карту, почему результат весит мегабайты и почему его можно переключать.

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

Хотите ИИ, который помнит вас?

Persona держит контекст между чатами и работает на вашем железе — если развернуть её у себя и подключить локальную модель.

Создать аккаунт