Transformers documentation
Параметрически эффективная дообучение
Параметрически эффективная дообучение
Методы Parameter-efficient fine-tuning (PEFT) обучают лишь небольшое количество дополнительных параметров модели (адаптеров) поверх предобученной модели. Поскольку обновляются только параметры адаптеров, оптимизатор отслеживает значительно меньше градиентов и состояний, что существенно снижает потребление памяти. Адаптеры легковесны, что делает их удобными для обмена, хранения и загрузки.
Transformers напрямую интегрируется с библиотекой PEFT через PeftAdapterMixin, который добавлен во все классы PreTrainedModel. Вы можете загружать, добавлять, обучать, переключать и удалять адаптеры, не оборачивая модель в отдельный PeftModel. Поддерживаются все методы PEFT, не основанные на обучении промптов (LoRA, IA3, AdaLoRA). Для методов, основанных на промптах, таких как prompt tuning и prefix tuning, необходимо использовать библиотеку PEFT напрямую.
Установите PEFT, чтобы начать работу. Для интеграции требуется peft >= 0.20.0.
pip install -U peft
Добавить адаптер
Создайте конфигурацию PEFT, например LoraConfig, и привяжите её к модели с помощью add_adapter().
from peft import LoraConfig, TaskType
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained("google/gemma-2-2b")
lora_config = LoraConfig(
task_type=TaskType.CAUSAL_LM,
inference_mode=False,
r=8,
lora_alpha=32,
lora_dropout=0.1,
)
model.add_adapter(lora_config, adapter_name="my_adapter")Полная дообучение конкретных слоёв
Чтобы обучать дополнительные модули вместе с адаптером (например, головную часть языковой модели), укажите их в modules_to_save. modules_to_save задаёт слои, которые полностью дообучаются вместе с адаптером, поэтому все их параметры обновляются. Это полезно, когда определённые слои требуют обновления, например головная часть языковой модели (lm_head), при адаптации каузальной LM для классификации последовательностей.
lora_config = LoraConfig(
modules_to_save=["lm_head"],
...
)
model.add_adapter(lora_config)Выбор слоев для адаптации
Для распространённых архитектур (Llama, Gemma, Qwen и др.) в PEFT заданы предопределённые целевые модули по умолчанию (например, q_proj и v_proj), поэтому указывать target_modules не требуется. Если нужно выбрать другие слои или у модели нет предопределённых целей, явно передайте target_modules в виде списка имён модулей или регулярного выражения.
lora_config = LoraConfig(
target_modules=["q_proj", "k_proj"],
...
)
model.add_adapter(lora_config)Обучение
Передайте модель с подключенным адаптером в Trainer и вызовите train(). Trainer обновляет только параметры адаптера (те, у которых requires_grad=True), так как базовая модель заморожена.
from transformers import Trainer, TrainingArguments
training_args = TrainingArguments(
output_dir="./output",
num_train_epochs=3,
per_device_train_batch_size=4,
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=dataset,
)
trainer.train()В процессе обучения контрольные точки Trainer содержат только веса адаптера (adapter_model.safetensors) и конфигурацию (adapter_config.json), что позволяет сохранять контрольные точки небольшого размера. Базовая модель в них не включается.
После обучения сохраните итоговый адаптер с помощью save_pretrained().
model.save_pretrained("./my_adapter")Возобновление из контрольной точки
Trainer автоматически обнаруживает контрольные точки адаптеров при возобновлении обучения. Trainer просматривает каталог контрольной точки в поисках подкаталогов, содержащих веса адаптеров, и перезагружает каждый адаптер с правильным обучаемым состоянием.
trainer.train(resume_from_checkpoint="./output/checkpoint-1000")Распределённое обучение
Адаптеры PEFT работают с распределённым обучением из коробки.
Для ZeRO-3 Trainer передаёт exclude_frozen_parameters=True при сохранении контрольных точек с моделью PEFT. Замороженные веса базовой модели пропускаются. Сохраняются только обучаемые параметры адаптера, что уменьшает размер контрольной точки и время сохранения.
Для FSDP Trainer обновляет политику автообёртки FSDP, чтобы корректно обрабатывать слои LoRA. Для QLoRA (квантованная базовая модель + LoRA) Trainer также настраивает политику смешанной точности в соответствии с типом данных хранения квантования.
Вы можете загрузить адаптеры PEFT на базовую модель с тензорным параллелизмом и позволить PEFT самостоятельно распределять веса адаптеров. Для загрузки адаптера требуется peft >= 0.21.1. О тренировке с TP см. Тензорный параллелизм.
Загрузка адаптера
Для загрузки адаптера репозиторий на Hub или локальный каталог должны содержать файл adapter_config.json и веса адаптера.
from_pretrained() автоматически обнаруживает адаптеры. При обнаружении adapter_config.json метод читает поле base_model_name_or_path, чтобы загрузить нужную базовую модель, а затем загружает адаптер поверх неё.
from transformers import AutoModelForCausalLM
# Automatically loads the base model and attaches the adapter
model = AutoModelForCausalLM.from_pretrained("klcsp/gemma7b-lora-alpaca-11-v1")Чтобы загрузить адаптер в существующую модель, используйте load_adapter().
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained("google/gemma-7b")
model.load_adapter("klcsp/gemma7b-lora-alpaca-11-v1")Для больших моделей загрузите квантованную версию с точностью 8 бит или 4 бита с помощью bitsandbytes, чтобы сэкономить память. Добавьте device_map="auto", чтобы распределить модель по доступному оборудованию.
from transformers import AutoModelForCausalLM, BitsAndBytesConfig
model = AutoModelForCausalLM.from_pretrained(
"klcsp/gemma7b-lora-alpaca-11-v1",
quantization_config=BitsAndBytesConfig(load_in_8bit=True),
device_map="auto",
)Управление несколькими адаптерами
Модель может одновременно содержать несколько адаптеров. Добавляйте адаптеры с уникальными именами и переключайтесь между ними по мере необходимости.
from peft import LoraConfig
model.add_adapter(LoraConfig(r=8, lora_alpha=32), adapter_name="adapter_1")
model.add_adapter(LoraConfig(r=16, lora_alpha=64), adapter_name="adapter_2")Используйте set_adapter() для активации конкретного адаптера. Остальные адаптеры отключаются, но остаются в памяти.
model.set_adapter("adapter_2")enable_adapters() включает все подключенные адаптеры, а disable_adapters() отключает их все.
# Disable all adapters for base model inference
model.disable_adapters()
# Re-enable all adapters
model.enable_adapters()Используйте active_adapters(), чтобы посмотреть, какие адаптеры активны в данный момент.
model.active_adapters()
# ["adapter_1"]Удалите ненужные адаптеры с помощью delete_adapter(), чтобы освободить память.
model.delete_adapter("adapter_1")Горячая замена адаптеров
Загрузка нового адаптера при каждой обработке запроса приводит к выделению новой памяти. Если модель скомпилирована с помощью torch.compile, каждый новый адаптер вызывает повторную компиляцию. Hotswapping заменяет веса адаптера на месте, что позволяет избежать обеих проблем. Поддерживаются только LoRA-адаптеры.
Передайте hotswap=True при загрузке адаптера LoRA, чтобы заменить его веса в существующем слоте адаптера. Установите adapter_name равным имени заменяемого адаптера (по умолчанию используется имя "default").
model = AutoModel.from_pretrained(...)
# Load the first adapter normally
model.load_adapter(adapter_path_1)
# Generate outputs with adapter 1
...
# Hotswap the second adapter in-place
model.load_adapter(adapter_path_2, hotswap=True, adapter_name="default")
# Generate outputs with adapter 2torch.compile
Для скомпилированных моделей вызовите enable_peft_hotswap() до загрузки первого адаптера и до компиляции.
model = AutoModel.from_pretrained(...)
max_rank = ... # highest rank among all LoRAs you'll load
model.enable_peft_hotswap(target_rank=max_rank)
model.load_adapter(adapter_path_1, adapter_name="default")
model = torch.compile(model, ...)
output_1 = model(...)
# Hotswap without recompilation
model.load_adapter(adapter_path_2, adapter_name="default")
output_2 = model(...)Аргумент target_rank задаёт максимальный ранг среди всех загружаемых адаптеров LoRA. Если у вас есть адаптеры с рангом 8 и рангом 16, передайте target_rank=16. По умолчанию значение равно 128.
После вызова enable_peft_hotswap все последующие вызовы load_adapter по умолчанию используют hotswap. Чтобы отключить hotswap, явно передайте hotswap=False.
Повторная компиляция всё ещё может произойти, если подменяемый адаптер охватывает больше слоёв, чем начальный адаптер. Загрузите сначала адаптер, охватывающий наибольшее количество слоёв, чтобы избежать повторной компиляции.
Оберните код в
with torch._dynamo.config.patch(error_on_recompile=True), чтобы обнаруживать неожиданную перекомпиляцию. Если перекомпиляция обнаруживается, несмотря на выполнение описанных выше шагов, создайте issue в PEFT с воспроизводимым примером.
Дальнейшие шаги
- Документация PEFT documentation охватывает полный спектр методов и опций PEFT.
- В справочном материале по hotswapping PEFT описаны ограничения и граничные случаи.
- В статье в блоге показано, как
torch.compileс hotswapping улучшает время выполнения.