LoRA и PEFT: Эффективная
Parameter-Efficient Fine-Tuning (PEFT) позволяет адаптировать крупные модели, обучая небольшое количество дополнительных параметров, сохраняя при этом базовую модель неизменной. Наиболее широко используемым методом PEFT является LoRA (Low-Rank Adaptation), которая внедряет обучаемые low-rank обновления в линейные слои. В результате количество обучаемых параметров часто сокращается примерно на 90%, сохраняя при этом производительность.
Когда использовать PEFT
- У вас ограниченный бюджет вычислительных ресурсов или памяти
- Вам нужно быстро адаптировать базовую модель к нескольким задачам/областям
- Вам нужны быстрые итерации и небольшие артефакты (веса адаптеров обычно составляют несколько МБ)
Понимание LoRA
LoRA стала самым распространенным методом PEFT. Она работает путем добавления небольших матриц разложения по рангу к весам внимания, что обычно сокращает количество обучаемых параметров примерно на 90%.
LoRA (Low-Rank Adaptation) - это метод эффективной по параметрам, который замораживает предобученные веса модели и внедряет обучаемые матрицы разложения по рангу в слои модели. Вместо обучения всех параметров модели во время, LoRA разлагает обновления весов на меньшие матрицы через low-rank decomposition, что значительно уменьшает количество обучаемых параметров, сохраняя при этом производительность модели. Например, при применении к GPT-3 175B, LoRA сократила количество обучаемых параметров в 10,000 раз и требования к оперативной памяти GPU в 3 раза по сравнению с полным fine-tuning. Подробнее о LoRA можно прочитать в документе LoRA.
LoRA работает путем добавления пар матриц разложения по рангу к слоям трансформера, обычно сосредотачиваясь на весах внимания. Во время инференса эти веса адаптера можно объединить с базовой моделью, что не приводит к дополнительной задержке. LoRA особенно полезна для адаптации больших языковых моделей к определенным задачам или областям, сохраняя при этом управляемые требования к ресурсам.
Загрузка адаптеров LoRA
Адаптеры можно загрузить в предобученную модель с помощью load_adapter(), что удобно для использования различных адаптеров, чьи веса не объединяются. Активные веса адаптера можно задать с помощью функции set_adapter(). Чтобы вернуться к базовой модели, можно использовать unload(), чтобы выгрузить все модули LoRA. Это упрощает переключение между различными специфическими для задачи весами.
from transformers import AutoModelForCausalLM
from peft import PeftModel
base_model = AutoModelForCausalLM.from_pretrained("<base_model_name>")
peft_model_id = "<peft_adapter_id>"
model = PeftModel.from_pretrained(base_model, peft_model_id)

Объединение адаптеров LoRA
После обучения с LoRA вы можете захотеть объединить веса адаптера обратно в базовую модель для более удобного развёртывания. Это создает единую модель с объединенными весами, устраняя необходимость отдельно загружать адаптеры во время инференса.
Процесс слияния требует внимательного управления памятью и точностью. Поскольку вам необходимо загрузить одновременно базовую модель и веса адаптера, убедитесь, что доступно достаточное количество оперативной памяти GPU/CPU. Использование device_map="auto" в transformers поможет с автоматическим управлением памятью. Сохраняйте непостоянную точность (например, float16) на протяжении всего процесса, соответствуя точности, используемой во время обучения, и сохраняйте объединенную модель в том же формате для развёртывания. Перед развёртыванием всегда проверяйте объединенную модель, сравнивая её выходные данные и показатели производительности с версией на основе адаптеров.
Адаптеры также удобны для переключения между различными задачами или доменами. Вы можете загрузить базовую модель и веса адаптера по-отдельности. Это позволяет быстро переключаться между различными специфичными для задачи весами.
При реализации методов PEFT начинайте с небольших значений ранга (4-8) для LoRA и отслеживайте потерю во время обучения. Используйте валидационные наборы для предотвращения переобучения и сравнивайте результаты с полным дообучением, если возможно. Эффективность различных методов может варьироваться в зависимости от задачи, поэтому эксперименты играют ключевую роль.
OLoRA
OLoRA использует декомпозицию QR для инициализации адаптеров LoRA. OLoRA транслирует базовые веса модели с помощью факторов их QR-разложений, т.е. мутирует веса перед выполнением любого обучения на них. Этот подход значительно улучшает стабильность, ускоряет скорость сходимости и в конечном итоге обеспечивает более высокое производительность.
Использование TRL с PEFT
Методы PEFT можно комбинировать с TRL (Transformers Reinforcement Learning) для эффективного дообучения. Это интеграция особенно полезна для RLHF (Reinforcement Learning from Human Feedback), так как она снижает требования к памяти.
from peft import LoraConfig
from trl import SFTTrainer
# Загрузка модели с конфигурацией PEFT
lora_config = LoraConfig(
r=16,
lora_alpha=32,
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM"
)
trainer = SFTTrainer(
model="your-model-name",
train_dataset=dataset["train"]
peft_config=lora_config
)
Базовая реализация слияния
После обучения адаптера LoRA вы можете объединить веса адаптера обратно в базовую модель. Вот как это сделать:
import torch
from transformers import AutoModelForCausalLM
from peft import PeftModel
# 1. Загрузка базовой модели
base_model = AutoModelForCausalLM.from_pretrained(
"base_model_name",
dtype=torch.bfloat16,
device_map="auto"
)
# 2. Загрузка модели PEFT с адаптером
peft_model = PeftModel.from_pretrained(
base_model,
"path/to/adapter",
dtype=torch.bfloat16
)
# 3. Слияние весов адаптера с базовой моделью
try:
merged_model = peft_model.merge_and_unload()
except RuntimeError as e:
print(f"Слияние не удалось: ")
# Реализуйте стратегию откат или оптимизацию памяти
# 4. Сохранение объединенной модели
merged_model.save_pretrained("path/to/save/merged_model")
При возникновении несоответствий размеров в сохраненной модели убедитесь, что также сохраняете токенайзер:
# Save both model and tokenizer
tokenizer = AutoTokenizer.from_pretrained("base_model_name")
merged_model.save_pretrained("path/to/save/merged_model")
tokenizer.save_pretrained("path/to/save/merged_model")
Быстрый запуск с TRL + LoRA
trl-тренер SFTTrainer работает штатно с PEFT. Подготовьте LoraConfig, передайте трениру, и обучайте только веса адаптера.
from peft import LoraConfig
from trl import SFTTrainer, SFTConfig
# 1) Configure LoRA
peft_config = LoraConfig(
r=8,
lora_alpha=16,
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM",
)
# 2) Create trainer (example)
trainer = SFTTrainer(
model=model,
args=SFTConfig(output_dir="lora-adapter", num_train_epochs=1, per_device_train_batch_size=2, packing=True),
train_dataset=dataset["train"],
peft_config=peft_config,
)
trainer.train()
После обучения у вас есть два варианта:
- Загружайте адаптеры во время инференса вместе с базовой моделью, или
- Объединяйте адаптеры с базовой моделью для упрощения развёртывания.