Transformers documentation

Ajustement fin efficace en termes de paramètres

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v5.22.1).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Ajustement fin efficace en termes de paramètres

Les méthodes de fine-tuning à faible coût paramétrique (PEFT) n’ajustent qu’un petit nombre de paramètres supplémentaires du modèle (adaptateurs) par-dessus un modèle pré-entraîné. Comme seuls les paramètres des adaptateurs sont mis à jour, l’optimiseur suit beaucoup moins de gradients et d’états, ce qui réduit considérablement l’utilisation de la mémoire. Les adaptateurs sont légers, ce qui les rend pratiques à partager, à stocker et à charger.

Transformers s’intègre directement à la bibliothèque PEFT via PeftAdapterMixin, ajouté à toutes les classes PreTrainedModel. Vous pouvez charger, ajouter, entraîner, basculer et supprimer des adaptateurs sans avoir à encapsuler votre modèle dans un PeftModel distinct. Toutes les méthodes PEFT non basées sur l’apprentissage de prompts sont prises en charge (LoRA, IA3, AdaLoRA). Les méthodes basées sur les prompts, telles que le prompt tuning et le prefix tuning, nécessitent l’utilisation directe de la bibliothèque PEFT.

Installez PEFT pour commencer. L'intégration nécessite peft >= 0.20.0.

pip install -U peft

Ajouter un adaptateur

Créez une configuration PEFT, par exemple LoraConfig, et associez-la à un modèle à l’aide de 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")

Ajustement fin complet de couches spécifiques

Pour entraîner des modules supplémentaires en plus d'un adaptateur (par exemple, la tête du modèle de langage), spécifiez-les dans modules_to_save. modules_to_save définit les couches qui sont entièrement affinées en même temps que l'adaptateur, de sorte que tous leurs paramètres sont mis à jour. Cela est utile lorsque certaines couches nécessitent des mises à jour, par exemple la tête du modèle de langage (lm_head), lors de l'adaptation d'un LM causal pour la classification de séquences.

lora_config = LoraConfig(
    modules_to_save=["lm_head"],
    ...
)
model.add_adapter(lora_config)

Choisir les couches à adapter

Pour les architectures courantes (Llama, Gemma, Qwen, etc.), PEFT définit des cibles par défaut prédéfinies (comme q_proj et v_proj), il n’est donc pas nécessaire de spécifier target_modules. Si vous souhaitez cibler d’autres couches, ou si le modèle ne dispose pas de cibles prédéfinies, passez explicitement target_modules sous forme d’une liste de noms de modules ou d’un motif regex.

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

Entraînement

Passez le modèle avec un adaptateur attaché à Trainer et appelez train(). Trainer ne met à jour que les paramètres de l’adaptateur (ceux avec requires_grad=True) car le modèle de base est figé.

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

Lors de l’entraînement, les points de contrôle de Trainer ne contiennent que les poids de l’adaptateur (adapter_model.safetensors) et la configuration (adapter_config.json), ce qui permet de garder les points de contrôle de petite taille. Le modèle de base n’est pas inclus.

Après l'entraînement, enregistrez l'adaptateur final avec save_pretrained().

model.save_pretrained("./my_adapter")

Reprendre depuis un point de contrôle

Trainer détecte automatiquement les points de contrôle d’adaptateurs lors de la reprise. Trainer parcourt le répertoire du point de contrôle à la recherche de sous-répertoires contenant les poids des adaptateurs et recharge chaque adaptateur avec l’état entraînable approprié.

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

Entraînement distribué

Les adaptateurs PEFT fonctionnent avec l'entraînement distribué sans configuration supplémentaire.

Pour ZeRO-3, Trainer transmet exclude_frozen_parameters=True lors de l’enregistrement des points de contrôle avec un modèle PEFT. Les poids du modèle de base figés sont ignorés. Seuls les paramètres d’adaptateur entraînables sont enregistrés, ce qui réduit la taille du point de contrôle et le temps d’enregistrement.

Pour FSDP, Trainer met à jour la politique d’auto-wrap de FSDP afin de traiter correctement les couches LoRA. Pour QLoRA (modèle de base quantifié + LoRA), Trainer ajuste également la politique de précision mixte pour qu’elle corresponde au type de stockage de la quantisation.

Vous pouvez charger des adaptateurs PEFT sur un modèle de base parallélisé par tenseurs et laisser PEFT gérer le fractionnement des poids des adaptateurs. Le chargement d'un adaptateur nécessite peft >= 0.21.1. Consultez Parallélisme par tenseurs pour l'entraînement avec TP.

Chargement d'un adaptateur

Pour charger un adaptateur, le dépôt du Hub ou le répertoire local doit contenir un fichier adapter_config.json et les poids de l’adaptateur.

from_pretrained
load_adapter

from_pretrained() détecte automatiquement les adaptateurs. Lorsqu’il trouve un adapter_config.json, il lit le champ base_model_name_or_path pour charger le modèle de base approprié, puis charge l’adaptateur par-dessus.

from transformers import AutoModelForCausalLM

# Automatically loads the base model and attaches the adapter
model = AutoModelForCausalLM.from_pretrained("klcsp/gemma7b-lora-alpaca-11-v1")

Pour les grands modèles, chargez une version quantifiée en précision 8 bits ou 4 bits avec bitsandbytes afin d’économiser de la mémoire. Ajoutez device_map="auto" pour répartir le modèle sur le matériel disponible.

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

Gérer plusieurs adaptateurs

Un modèle peut contenir plusieurs adaptateurs simultanément. Ajoutez des adaptateurs avec des noms uniques et basculez entre eux au besoin.

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

Utilisez set_adapter() pour activer un adaptateur spécifique. Les autres adaptateurs sont désactivés mais restent en mémoire.

model.set_adapter("adapter_2")

enable_adapters() active tous les adaptateurs attachés, et disable_adapters() les désactive tous.

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

# Re-enable all adapters
model.enable_adapters()

Utilisez active_adapters() pour voir les adaptateurs actuellement actifs.

model.active_adapters()
# ["adapter_1"]

Supprimez les adaptateurs dont vous n'avez plus besoin à l'aide de delete_adapter() pour libérer de la mémoire.

model.delete_adapter("adapter_1")

Changement à chaud des adaptateurs

Charger un nouvel adaptateur à chaque demande de service alloue de la nouvelle mémoire. Si le modèle est compilé avec torch.compile, chaque nouvel adaptateur déclenche une recompilation. Le hotswapping remplace les poids de l'adaptateur sur place, évitant ainsi les deux problèmes. Seuls les adaptateurs LoRA sont pris en charge.

Passez hotswap=True lors du chargement d'un adaptateur LoRA pour remplacer les poids d'un adaptateur existant. Définissez adapter_name sur le nom de l'adaptateur à remplacer ("default" est le nom par défaut de l'adaptateur).

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

Pour les modèles compilés, appelez enable_peft_hotswap() avant de charger le premier adaptateur et avant la compilation.

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(...)

L'argument target_rank définit le rang maximal parmi tous les adaptateurs LoRA que vous chargez. Si vous disposez d'adaptateurs de rang 8 et de rang 16, passez target_rank=16. La valeur par défaut est 128.

Après l’appel de enable_peft_hotswap, tous les appels suivants de load_adapter effectuent un hotswap par défaut. Passez explicitement hotswap=False pour désactiver le hotswap.

Une recompilation peut survenir si l'adaptateur rechargé cible plus de couches que l'adaptateur initial. Chargez d'abord l'adaptateur qui cible le plus grand nombre de couches afin d'éviter la recompilation.

Enveloppez votre code dans with torch._dynamo.config.patch(error_on_recompile=True) pour détecter toute recompilation inattendue. Si vous détectez une recompilation malgré le respect des étapes ci-dessus, ouvrez un ticket auprès de PEFT avec un exemple reproductible.

Étapes suivantes

Update on GitHub