Что такое LoRA (Low-Rank Adaptation)? Объясните математическую идею: W' = W + BA. Почему это экономит память?
Краткий тезис
LoRA (Low-Rank Adaptation) — метод параметро-эффективного дообучения (PEFT), который фиксирует предобученные веса W размерности d×k и вводит две обучаемые матрицы низкого ранга: B размерности d×r и A размерности r×k. Результирующая апдейтованная матрица W' = W + BA содержит лишь r·(d+k) обучаемых параметров вместо d·k. При малом ранге r << min(d,k) это даёт радикальную экономию памяти (до 10 000×) при сопоставимом качестве дообучения.
2. B (d×r), A (r×k) обучаются
Вместо полного обновления W_0 вводится обновление ΔW — матрица такого же размера d×k, но она представляется как произведение двух маленьких матриц:
ΔW = B · A
где:
B— матрицаd×r(обычно инициализируется нулями или случайно с малым шумом);A— матрицаr×k(часто инициализируется случайными гауссовыми значениями, затем масштабируется коэффициентомα/r).
Тогда новая матрица весов:
W' = W_0 + ΔW = W_0 + B·A
Только B и A участвуют в обратном распространении ошибки и обновляются оптимизатором. W_0 остаётся константой.
Математическая идея: гипотеза о том, что изменение весов при дообучении на узкую задачу (сценарий, диалог, специфический стиль) лежит в пространстве низкого ранга. То есть векторное пространство, в котором происходят наиболее информативные смещения весов, имеет эффективную размерность много меньше min(d,k). Эксперименты показывают, что даже очень малый ранг (r=2, 4, 8…64) достаточно хорошо улавливает нужную адаптацию.
Форвард-проход теперь выглядит так:
h = (W_0 + B·A)·x = W_0·x + B·A·x
Первое слагаемое — стандартный проход через замороженный слой. Второе — низкоранговая поправка. Это можно вычислить с минимальными накладными расходами (два маленьких матричных умножения).
3. rank r << min(d,k)
Успех LoRA критически зависит от выбора ранга r. Он должен быть много меньше обоих размеров исходной матрицы:
r << min(d, k)
На практике для Transformer-моделей с размерностью скрытого состояния d_model = 4096 (как в LLaMA-13B) ранги r = 8 или r = 16 уже дают отличные результаты.
Почему это работает?
Интуитивно — при дообучении модель должна лишь слегка скорректировать своё поведение под конкретный датасет. Значительные изменения весов (полный апдейт) часто избыточны и могут привести к катастрофическому забыванию. Низкоранговая матрица B·A обучает только небольшое число независимых направлений, которые наиболее релевантны целевой задаче.
Практические замечания:
- Обычно LoRA применяют к слоям внимания (проекции Q, K, V, O). Иногда добавляют к dense_layer в FFN, но чаще всего улучшение от attention компонент оказывается самым значительным.
- Коэффициент масштабирования
αпозволяет контролировать силу адаптации:ΔW = (α/r)·B·A. При большомrон снижает шаг, чтобы не переобучаться.
4. Экономия памяти: вместо d·k параметров → r·(d+k)
Расчёт в цифрах
Пусть исходный линейный слой имеет d = 4096, k = 4096. Тогда:
- Полное количество параметров слоя:
d·k = 4096·4096 ≈ 16 777 216(≈16,8 млн). - Обучаемых параметров при LoRA с
r = 8:
B: d·r = 4096·8 = 32 768
A: r·k = 8·4096 = 32 768
Итого:65 536параметров — в 256 раз меньше, чем 16,8 млн.
Если модель содержит 96 таких слоёв (как LLaMA-13B), то:
- Полное обновление:
96 · 16,8M ≈ 1,6 млрдпараметров. - LoRA (r=8):
96 · 65 536 ≈ 6,3 млнпараметров — сокращение в ~250 раз.
Откуда берётся формула r·(d+k)?
- Параметры в
B:d · r - Параметры в
A:r · k - Сумма:
r·(d+k)
Исходная матрица имеет d·k параметров. Отношение (d·k) / (r·(d+k)) при больших d,k и малом r даёт гигантскую экономию.
Память под градиенты и оптимизатор:
При full fine-tuning каждый параметр хранит свой градиент (4 байта float32) и два момента Adam (8 байт). Итого 12 байт на параметр. Для 1,6 млрд параметров это ~19,2 ГБ только под градиенты и состояния.
При LoRA (6,3M параметров) это ~75 МБ. Плюс нужно хранить саму матрицу W_0 (она занимает те же 16,8M·4 байт = 67 МБ на слой, но она общая и не требует градиентов). Замороженные веса можно держать в FP16/BF16, уменьшив вдвое.
Итог: LoRA позволяет дообучать модели с десятками миллиардов параметров на одном GPU (например, A100 80GB) — без параллелизма по весам и без техник вроде ZeRO или FSDP.
5. Пет-проект для закрепления
Задача: Реализовать LoRA для дообучения небольшого языкового трансформера (например, DistilGPT2 или миниатюрной версии BERT) на задаче классификации тональности отзывов (SST-2). Сравнить количество обучаемых параметров, использование видеопамяти и точность с полным fine-tuning.
Инструменты:
- Python 3.10+, PyTorch 2.0+
- Transformers, PEFT (библиотека от Hugging Face), Datasets, Evaluate
- GPU с 8+ ГБ VRAM (например, GTX 1080 Ti или T4)
Шаги:
- Загрузить предобученную модель (например,
distilbert-base-uncased) и токенизатор. - Создать обычный классификационный head (линейный слой поверх пула) и заморозить все веса трансформера.
- Заменить линейные слои в attention (Q и V) на LoRA-версии с рангом
r=4(можно использовать LoraConfig из PEFT). - Обучить 3 эпохи на датасете
glue/sst2. Замерить occupied VRAM (черезtorch.cuda.max_memory_allocated()). - Для сравнения сделать полный fine-tuning (без LoRA) — разморозить все веса или использовать отдельную копию модели. Обучить на тех же данных и замерить память.
- Вывести таблицу:
Ожидаемый результат:
- Убедиться, что LoRA требует в 2–3 раза меньше видеопамяти при практически одинаковой точности.
- Понять, как задавать ранги
rи коэффициент масштабированияα(обычноα = 2r). - Научиться комбинировать LoRA с 8-bit quantization для ещё большей экономии.
Связь с другими вопросами
| Вопрос | Тема |
|---|---|
| 481 | Fine-tuning vs PEFT: сравнение методов и когда выбирать LoRA |
| 950 | Что такое PEFT? Обзор семейства методов |
| 953 | AdaLoRA – автоматический выбор ранга при адаптации |
| 480 | Как работает Low-Rank Adaptation в матричных разложениях |
Навигация
- Предыдущий: 951
- Следующий: 953
- Индекс: 00. Индекс разборов