Transformers documentation

Параметрически эффективная дообучение

Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Параметрически эффективная дообучение

Методы 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
load_adapter

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")

Для больших моделей загрузите квантованную версию с точностью 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 2

torch.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 с воспроизводимым примером.

Дальнейшие шаги

Update on GitHub