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

Методы [Parameter-efficient fine-tuning (PEFT)](https://huggingface.co/docs/peft/index) обучают лишь небольшое количество дополнительных параметров модели (адаптеров) поверх предобученной модели. Поскольку обновляются только параметры адаптеров, оптимизатор отслеживает значительно меньше градиентов и состояний, что существенно снижает потребление памяти. Адаптеры легковесны, что делает их удобными для обмена, хранения и загрузки.

Transformers напрямую интегрируется с библиотекой PEFT через [PeftAdapterMixin](/docs/transformers/v5.23.0/ru/main_classes/peft#transformers.integrations.PeftAdapterMixin), добавленный во все классы [PreTrainedModel](/docs/transformers/v5.23.0/ru/main_classes/model#transformers.PreTrainedModel). Вы можете загружать, добавлять, обучать, переключать и удалять адаптеры, не оборачивая модель в отдельный `PeftModel`. Поддерживаются все методы PEFT, не основанные на обучении промптов (LoRA, IA3, AdaLoRA). Для промпт-ориентированных методов, таких как prompt tuning и prefix tuning, необходимо использовать [библиотеку PEFT](https://huggingface.co/docs/peft/index) напрямую.

Установите PEFT, чтобы начать работу. Для интеграции требуется `peft >= 0.20.0`.

```shell
pip install -U peft
```

## Добавить адаптер

Создайте конфигурацию PEFT, например `LoraConfig`, и подключите её к модели с помощью [add_adapter()](/docs/transformers/v5.23.0/ru/main_classes/peft#transformers.integrations.PeftAdapterMixin.add_adapter).

```py
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 для классификации последовательностей.

```py
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` в виде списка имён модулей или регулярного выражения.

```py
lora_config = LoraConfig(
    target_modules=["q_proj", "k_proj"],
    ...
)
model.add_adapter(lora_config)
```

## Обучение

Передайте модель с подключенным адаптером в [Trainer](/docs/transformers/v5.23.0/ru/main_classes/trainer#transformers.Trainer) и вызовите [train()](/docs/transformers/v5.23.0/ru/main_classes/trainer#transformers.Trainer.train). [Trainer](/docs/transformers/v5.23.0/ru/main_classes/trainer#transformers.Trainer) обновляет только параметры адаптера (те, у которых `requires_grad=True`), так как базовая модель заморожена.

```py
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](/docs/transformers/v5.23.0/ru/main_classes/trainer#transformers.Trainer) содержат только веса адаптера (`adapter_model.safetensors`) и конфигурацию (`adapter_config.json`), что позволяет сохранять контрольные точки небольшими. Базовая модель не включается.

После обучения сохраните итоговый адаптер с помощью [save_pretrained()](/docs/transformers/v5.23.0/ru/main_classes/model#transformers.PreTrainedModel.save_pretrained).

```py
model.save_pretrained("./my_adapter")
```

### Возобновление из контрольной точки

[Trainer](/docs/transformers/v5.23.0/ru/main_classes/trainer#transformers.Trainer) автоматически обнаруживает контрольные точки адаптеров при возобновлении обучения. [Trainer](/docs/transformers/v5.23.0/ru/main_classes/trainer#transformers.Trainer) просматривает каталог контрольной точки в поисках подкаталогов, содержащих веса адаптеров, и перезагружает каждый адаптер с правильным обучаемым состоянием.

```py
trainer.train(resume_from_checkpoint="./output/checkpoint-1000")
```

### Распределённое обучение

Адаптеры PEFT работают с распределённым обучением из коробки.

Для ZeRO-3 [Trainer](/docs/transformers/v5.23.0/ru/main_classes/trainer#transformers.Trainer) передаёт `exclude_frozen_parameters=True` при сохранении контрольных точек с моделью PEFT. Замороженные веса базовой модели пропускаются. Сохраняются только обучаемые параметры адаптера, что уменьшает размер контрольной точки и время сохранения.

Для FSDP [Trainer](/docs/transformers/v5.23.0/ru/main_classes/trainer#transformers.Trainer) обновляет политику автообёртки FSDP, чтобы корректно обрабатывать слои LoRA. Для QLoRA (квантованная базовая модель + LoRA) [Trainer](/docs/transformers/v5.23.0/ru/main_classes/trainer#transformers.Trainer) также настраивает политику смешанной точности в соответствии с типом данных хранения квантования.

Вы можете загрузить адаптеры PEFT на базовую модель с тензорным параллелизмом и позволить PEFT самостоятельно распределять веса адаптеров. Для загрузки адаптера требуется `peft >= 0.21.1`. О тренировке с TP см. [Тензорный параллелизм](./tensor_parallelism).

## Загрузка адаптера

Для загрузки адаптера репозиторий на Hub или локальный каталог должны содержать файл `adapter_config.json` и веса адаптера.

[from_pretrained()](/docs/transformers/v5.23.0/ru/main_classes/model#transformers.PreTrainedModel.from_pretrained) автоматически обнаруживает адаптеры. При обнаружении `adapter_config.json` метод читает поле `base_model_name_or_path`, чтобы загрузить нужную базовую модель, а затем загружает адаптер поверх неё.

```py
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()](/docs/transformers/v5.23.0/ru/main_classes/peft#transformers.integrations.PeftAdapterMixin.load_adapter).

```py
from transformers import AutoModelForCausalLM

model = AutoModelForCausalLM.from_pretrained("google/gemma-7b")
model.load_adapter("klcsp/gemma7b-lora-alpaca-11-v1")
```

Для больших моделей загрузите квантованную версию с точностью 8 бит или 4 бита с помощью [bitsandbytes](./quantization/bitsandbytes), чтобы сэкономить память. Добавьте `device_map="auto"`, чтобы распределить модель по доступному оборудованию.

```py
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",
)
```

## Управление несколькими адаптерами

Модель может одновременно содержать несколько адаптеров. Добавляйте адаптеры с уникальными именами и переключайтесь между ними по мере необходимости.

```py
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()](/docs/transformers/v5.23.0/ru/main_classes/peft#transformers.integrations.PeftAdapterMixin.set_adapter) для активации конкретного адаптера. Остальные адаптеры отключаются, но остаются в памяти.

```py
model.set_adapter("adapter_2")
```

[enable_adapters()](/docs/transformers/v5.23.0/ru/main_classes/peft#transformers.integrations.PeftAdapterMixin.enable_adapters) включает все подключенные адаптеры, а [disable_adapters()](/docs/transformers/v5.23.0/ru/main_classes/peft#transformers.integrations.PeftAdapterMixin.disable_adapters) отключает их все.

```py
# Disable all adapters for base model inference
model.disable_adapters()

# Re-enable all adapters
model.enable_adapters()
```

Используйте [active_adapters()](/docs/transformers/v5.23.0/ru/main_classes/peft#transformers.integrations.PeftAdapterMixin.active_adapters), чтобы посмотреть, какие адаптеры активны в данный момент.

```py
model.active_adapters()
# ["adapter_1"]
```

Удалите ненужные адаптеры с помощью [delete_adapter()](/docs/transformers/v5.23.0/ru/main_classes/peft#transformers.integrations.PeftAdapterMixin.delete_adapter), чтобы освободить память.

```py
model.delete_adapter("adapter_1")
```

## Горячая замена адаптеров

Загрузка нового адаптера при каждой обработке запроса приводит к выделению новой памяти. Если модель скомпилирована с помощью `torch.compile`, каждый новый адаптер вызывает повторную компиляцию. Hotswapping заменяет веса адаптера на месте, что позволяет избежать обеих проблем. Поддерживаются только LoRA-адаптеры.

Передайте `hotswap=True` при загрузке адаптера LoRA, чтобы заменить его веса в существующем слоте адаптера. Установите `adapter_name` равным имени заменяемого адаптера (по умолчанию используется имя `"default"`).

```py
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()](/docs/transformers/v5.23.0/ru/main_classes/peft#transformers.integrations.PeftAdapterMixin.enable_peft_hotswap) *до* загрузки первого адаптера и до компиляции.

```py
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`.

Повторная компиляция всё ещё может произойти, если подменяемый адаптер охватывает больше слоёв, чем начальный адаптер. Загрузите сначала адаптер, охватывающий наибольшее количество слоёв, чтобы избежать повторной компиляции.

> [!TIP]
> Оберните код в `with torch._dynamo.config.patch(error_on_recompile=True)`, чтобы обнаруживать неожиданную перекомпиляцию. Если перекомпиляция обнаруживается, несмотря на выполнение описанных выше шагов, создайте issue в [PEFT](https://github.com/huggingface/peft/issues) с воспроизводимым примером.

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

- Документация PEFT [documentation](https://huggingface.co/docs/peft/index) охватывает полный спектр методов и опций PEFT.
- В [справочном материале по hotswapping](https://huggingface.co/docs/peft/main/en/package_reference/hotswap#peft.utils.hotswap.hotswap_adapter) PEFT описаны ограничения и граничные случаи.
- В [статье в блоге](https://huggingface.co/blog/lora-fast) показано, как `torch.compile` с hotswapping улучшает время выполнения.

