PEFT documentation
LoRA
LoRA
LoRA est l’une des méthodes PEFT les plus populaires et un excellent point de départ si vous débutez avec PEFT. Elle a été initialement développée pour les grands modèles de langage, mais elle est devenue une méthode d’entraînement extrêmement populaire pour les modèles de diffusion en raison de son efficacité et de son efficacité.
L'adaptation de rang faible (LoRA) est une méthode PEFT qui décompose une grande matrice en deux matrices de rang faible plus petites. Cela réduit considérablement le nombre de paramètres à affiner.
L'abstract de l'article est :
Un paradigme important du traitement du langage naturel consiste à effectuer un pré-entraînement à grande échelle sur des données de domaine général, puis à adapter le modèle à des tâches ou des domaines particuliers. À mesure que nous pré-entraînons des modèles plus grands, le fine-tuning complet, qui réentraîne tous les paramètres du modèle, devient de moins en moins réalisable. En prenant GPT-3 175B comme exemple, déployer des instances indépendantes de modèles fine-tunés, chacune avec 175B paramètres, est prohibitivement coûteux. Nous proposons la Low-Rank Adaptation, ou LoRA, qui fige les poids du modèle pré-entraîné et injecte des matrices de décomposition de rang entraînables dans chaque couche de l'architecture Transformer, réduisant considérablement le nombre de paramètres entraînables pour les tâches en aval. Par rapport à GPT-3 175B fine-tuné avec Adam, LoRA peut réduire le nombre de paramètres entraînables de 10 000 fois et la mémoire GPU requise de 3 fois. LoRA offre une qualité de modèle égale ou supérieure au fine-tuning sur RoBERTa, DeBERTa, GPT-2 et GPT-3, malgré un nombre de paramètres entraînables inférieur, un débit d'entraînement plus élevé et, contrairement aux adaptateurs, aucune latence d'inférence supplémentaire. Nous fournissons également une étude empirique sur la carence de rang dans l'adaptation des modèles de langage, qui met en lumière l'efficacité de LoRA. Nous publions un package qui facilite l'intégration de LoRA avec les modèles PyTorch et fournissons nos implémentations et points de contrôle de modèles pour RoBERTa, DeBERTa et GPT-2 à ce lien https.
LoRA représente les mises à jour des poids par deux matrices plus petites (appelées matrices de mise à jour) par décomposition de rang faible. Ces nouvelles matrices peuvent être entraînées pour s'adapter aux nouvelles données tout en maintenant un nombre total de paramètres faible. La matrice de poids d'origine reste figée et ne reçoit aucune mise à jour supplémentaire. Pour produire les résultats finaux, les poids d'origine et les poids adaptés supplémentaires sont combinés. Vous pouvez également fusionner les poids de l'adaptateur avec le modèle de base pour éliminer la latence d'inférence.

Cette approche présente un certain nombre d'avantages :
- LoRA rend le fine-tuning plus efficace en réduisant considérablement le nombre de paramètres entraînables.
- Les poids pré-entraînés d’origine restent figés, ce qui permet de disposer de plusieurs modèles LoRA légers et portables pour diverses tâches en aval, construits sur leur base.
- LoRA est orthogonal aux autres méthodes efficaces en termes de paramètres et peut être combiné avec bon nombre d’entre elles.
- Les performances des modèles affinés à l'aide de LoRA sont comparables à celles des modèles entièrement affinés.
En principe, LoRA peut être appliqué à n'importe quel sous-ensemble de matrices de poids d'un réseau de neurones afin de réduire le nombre de paramètres entraînables. Toutefois, pour des raisons de simplicité et d'efficacité paramétrique accrue, LoRA est généralement appliqué uniquement aux blocs d'attention des modèles Transformer – il peut être judicieux de cibler d'autres couches également. Le nombre de paramètres entraînables résultant dans un modèle LoRA dépend de la taille des matrices de mise à jour, qui est déterminée principalement par le rang r et la forme de la matrice de poids d'origine.
Vous pouvez initialiser les matrices de faible rang en gardant à l'esprit différents cas d'usage - prise en compte de la tâche (CorDA, EVA, Astra), convergence plus rapide (PiSSA), atténuation des quantifications (LoftQ) - pour n'en citer que quelques-uns. Consultez les différentes initialisations ci-dessous. L'initialisation par défaut consiste à faire de LoRA une opération nulle, afin d'apprendre progressivement un nouveau comportement sans interférer de manière significative avec le modèle existant.
Utilisation
La taille des matrices de mise à jour de faible rang est déterminée par le rank ou r. Un rang plus élevé signifie que le modèle possède plus de paramètres à entraîner, mais cela lui confère également une plus grande capacité d’apprentissage. Dans l’exemple suivant, vous ciblez les matrices query et value des blocs d’attention. D’autres paramètres importants à définir sont lora_alpha (facteur d’échelle), bias (indique si none, all ou uniquement les paramètres de biais LoRA doivent être entraînés) et modules_to_save (les modules, autres que les couches LoRA, à entraîner et à enregistrer). Tous ces paramètres, et d’autres encore, se trouvent dans le LoraConfig.
from peft import LoraConfig, get_peft_model
config = LoraConfig(
r=16,
lora_alpha=16,
target_modules=["query", "value"],
lora_dropout=0.1,
bias="none",
modules_to_save=["classifier"],
)
model = get_peft_model(model, config)
model.print_healthcheck()Aperçu des benchmarks
Initialisation
L'initialisation des poids LoRA est contrôlée par le paramètre init_lora_weights dans LoraConfig. Par défaut, PEFT initialise les poids LoRA avec Kaiming-uniform pour le poids A et des zéros pour le poids B, ce qui aboutit à une transformation identité (identique à l'implémentation de référence).
Pour les couches transformers.Conv1D, telles que les projections de GPT-2, utilisez fan_in_fan_out=True car leurs poids sont stockés sous la forme (in_features, out_features) plutôt que selon la disposition de nn.Linear. Les initialiseurs basés sur les poids pris en charge utilisent la disposition Linear pour la décomposition et restaurent la disposition de stockage lors de la mise à jour des poids de base. Le mise à l'échelle "stable" de LoRA-GA utilise également la dimension de sortie, indépendamment de la disposition de stockage.
Il est également possible de passer init_lora_weights="gaussian". Comme son nom l’indique, cela initialise le poids A avec une distribution gaussienne et le poids B avec des zéros (c’est ainsi que Diffusers initialise les poids LoRA).
from peft import LoraConfig
config = LoraConfig(init_lora_weights="gaussian", ...)Il existe également une option pour définir init_lora_weights=False, utile pour le débogage et les tests. Ce devrait être le seul moment où vous utilisez cette option. Lorsque vous choisissez cette option, les poids LoRA sont initialisés de telle sorte qu'ils ne produisent pas une transformation identité.
from peft import LoraConfig
config = LoraConfig(init_lora_weights=False, ...)Normalized Low-Rank Adaptation (NoRA) normalise les matrices de projection descendante de LoRA. PEFT applique cette normalisation uniquement à l’initialisation via init_lora_weights="nora" dans LoraConfig : il normalise les colonnes du poids LoRA A initialisé aléatoirement et initialise le poids LoRA B à zéro. Les deux matrices restent entraînables, et les poids de base sont laissés inchangés.
Pour une couche linéaire, la mise à jour des poids de l'adaptateur est $\Delta W = sBA$, où a la forme (r, in_features), a la forme (out_features, r), et est le facteur d'échelle LoRA. NoRA part du poids aléatoire A créé par la couche linéaire LoRA et normalise chaque colonne le long de la dimension de rang :
Puisque B est nul, la mise à jour des poids de l'adaptateur est nulle avant l'entraînement. Avec bias="none" et lora_bias=False, l'adaptateur préserve initialement la sortie du modèle de base. NoRA ne nécessite ni jeu de données de calibration ni décomposition des poids.
Pour NoRA, le paramétrage recommandé est
lora_alphaégal àr(un ratio alpha/rang de 1:1), par exemple,r=8, lora_alpha=8. Avec la valeur par défautuse_rslora=False, le facteur d'échelle reste de 1 ; l'activation de LoRA stabilisé par le rang le modifie enlora_alpha / sqrt(r).
L’exemple suivant applique NoRA à deux couches linéaires dans un petit modèle PyTorch :
from torch import nn
from peft import LoraConfig, get_peft_model
model = nn.Sequential(
nn.Linear(32, 32),
nn.ReLU(),
nn.Linear(32, 16),
)
config = LoraConfig(
r=8,
lora_alpha=8,
target_modules=["0", "2"],
init_lora_weights="nora",
bias="none",
lora_bias=False,
use_rslora=False,
)
model = get_peft_model(model, config)
model.print_healthcheck()Pour un modèle Transformers, définissez target_modules avec les noms des couches linéaires que vous souhaitez adapter, par exemple ["q_proj", "v_proj"] si ces noms sont présents dans le modèle.
NoRA utilise les API existantes de configuration, d’entraînement, d’enregistrement et de chargement de LoRA, il n’existe donc pas de classes NoraConfig ou NoraModel distinctes. Pour les API de configuration et de modèle partagées, consultez LoraConfig et LoraModel.
NoRA prend en charge les modules cibles torch.nn.Linear ou les nn.Parameter passés directement via target_parameters. Il n'est pas pris en charge pour les embeddings, les couches de convolution, etc.
PiSSA initialise l'adaptateur LoRA à l'aide des valeurs singulières principales et des vecteurs singuliers. Cette modification simple permet à PiSSA de converger plus rapidement que LoRA et d'atteindre finalement de meilleures performances. De plus, PiSSA réduit l'erreur de quantisation par rapport à QLoRA, ce qui entraîne des améliorations supplémentaires.
Configurez la méthode d’initialisation sur « pissa », ce qui peut prendre plusieurs minutes pour exécuter la SVD sur le modèle pré-entraîné :
from peft import LoraConfig
config = LoraConfig(init_lora_weights="pissa", ...)Sinon, exécutez la SVD rapide, qui ne prend que quelques secondes. Le nombre d'itérations détermine le compromis entre l'erreur et le temps de calcul :
lora_config = LoraConfig(init_lora_weights="pissa_niter_[number of iters]", ...)Pour des instructions détaillées sur l'utilisation de PiSSA, veuillez suivre ces instructions.
MiCA (Minor Component Adaptation) est un complément de PiSSA : au lieu d’initialiser à partir des composantes singulières principales, MiCA utilise les composantes mineures. Concrètement, avec W = U Σ V^T, MiCA définit B = U[:, -r:] (les r vecteurs singuliers gauches associés aux plus petites valeurs singulières) et A = 0. Pendant l’entraînement, seul A est mis à jour ; B est figé. L’intuition est que les directions singulières mineures sont largement inutilisées par la tâche pré-entraînée et offrent donc un sous-espace plus « plastique » pour injecter de nouvelles connaissances tout en préservant les capacités pré-entraînées.
Puisque A == 0 à l'initialisation, la contribution de l'adaptateur B · A == 0 et la sortie du modèle est préservée exactement à l'étape 0 — aucune soustraction résiduelle sur le poids de base n'est nécessaire (contrairement à PiSSA). Comme seul A est entraînable, le nombre de paramètres entraînables pour un r donné est approximativement la moitié de celui de LoRA.
from peft import LoraConfig
config = LoraConfig(init_lora_weights="mica", r=16, target_modules=["q_proj", "v_proj"], ...)MiCA prend actuellement en charge les modules cibles nn.Linear et nn.Embedding. Le rang choisi doit satisfaire r <= min(in_features, out_features) pour les couches linéaires et r <= min(num_embeddings, embedding_dim) pour les couches d'embedding. Pour un guide d'utilisation détaillé, consultez ces instructions.
MiCA est principalement destiné au pré-entraînement continu / au pré-entraînement adaptatif au domaine. Dans ce contexte, utilisez le modèle de base, et non un point de contrôle ajusté pour les instructions ou le chat, pour l'initialisation SVD et l'entraînement MiCA. Après l'entraînement, fusionnez l'adaptateur dans les poids du modèle de base et utilisez le modèle de base adapté résultant comme point de départ pour l'ajustement ultérieur aux instructions/au chat.
CorDA construit des adaptateurs LoRA sensibles à la tâche à partir d'une décomposition des poids orientée par le contexte de la tâche en aval à apprendre (mode instruction-previewed, IPM) ou par les connaissances du monde à maintenir (mode knowledge-preserved, KPM). Le KPM non seulement obtient de meilleures performances que LoRA sur les tâches de fine-tuning, mais atténue également l'oubli catastrophique des connaissances du monde pré-entraînées. Lorsque la préservation des connaissances pré-entraînées n'est pas une préoccupation, le IPM est privilégié car il peut accélérer davantage la convergence et améliorer les performances du fine-tuning.
Vous devez configurer la méthode d’initialisation sur « corda » et spécifier le mode IPM ou KPM ainsi que le jeu de données à utiliser pour collecter les matrices de covariance.
@torch.no_grad()
def run_model():
# Assume `model` and `dataset` is in context...
model.eval()
for batch in dataset:
model(**batch)
corda_config = CordaConfig(
corda_method="kpm",
)
lora_config = LoraConfig(
init_lora_weights="corda",
corda_config=corda_config,
)
preprocess_corda(model, lora_config, run_model=run_model)
peft_model = get_peft_model(model, lora_config)Pour des instructions détaillées sur l'utilisation de CorDA, veuillez suivre ces instructions.
Astra construit des adaptateurs LoRA sensibles à la tâche à partir des vecteurs propres de la queue de la matrice de covariance des activations de sortie du module, estimés à partir d'un petit jeu de données de calibration de la tâche en aval. Le poids pré-entraîné est projeté sur le sous-espace engendré par les vecteurs propres de la queue pour initialiser l'adaptateur, ce qui contraint la mise à jour au sous-espace d'activation le plus pertinent pour la tâche en aval et accélère la convergence.
Vous devez configurer la méthode d’initialisation sur « astra », définir explicitement target_modules et fournir le jeu de données de calibration pour collecter les matrices de covariance.
@torch.no_grad()
def run_model():
# Assume `model` and `dataset` is in context...
model.eval()
for batch in dataset:
model(**batch)
from peft.tuners.lora import AstraConfig, preprocess_astra
astra_config = AstraConfig()
lora_config = LoraConfig(
init_lora_weights="astra",
target_modules=["q_proj", "v_proj"],
astra_config=astra_config,
)
preprocess_astra(model, lora_config, run_model=run_model)
peft_model = get_peft_model(model, lora_config)Pour des instructions détaillées sur l'utilisation d'Astra, veuillez suivre ces instructions.
OLoRA utilise la décomposition QR pour initialiser les adaptateurs LoRA. OLoRA traduit les poids de base du modèle par un facteur de leurs décompositions QR, c'est-à-dire qu'il modifie les poids avant d'effectuer tout entraînement sur eux. Cette approche améliore considérablement la stabilité, accélère la vitesse de convergence et atteint finalement des performances supérieures.
Il vous suffit de passer une option supplémentaire pour utiliser OLoRA :
from peft import LoraConfig
config = LoraConfig(init_lora_weights="olora", ...)Pour un usage plus avancé, veuillez consulter notre documentation.
EVA effectue une SVD sur les activations d’entrée de chaque couche et utilise les vecteurs singuliers droits pour initialiser les poids de LoRA. Il s’agit donc d’un schéma d’initialisation piloté par les données. De plus, EVA alloue de manière adaptative les rangs entre les couches en fonction de leur « ratio de variance expliquée », une métrique issue de l’analyse SVD.
Vous pouvez utiliser EVA en définissant init_lora_weights="eva" et en spécifiant EvaConfig dans LoraConfig :
from peft import LoraConfig, EvaConfig
peft_config = LoraConfig(
init_lora_weights = "eva",
eva_config = EvaConfig(rho = 2.0),
...
)Le paramètre rho (≥ 1.0) détermine la quantité de redistribution autorisée. Lorsque rho=1.0 et r=16, les adaptateurs LoRA sont limités à exactement 16 rangs, empêchant toute redistribution. Une valeur recommandée pour EVA avec redistribution est 2.0, ce qui signifie que le rang maximal autorisé pour une couche est 2r.
Il est recommandé d’effectuer l’initialisation EVA sur un accélérateur (par exemple, un GPU CUDA, un XPU Intel), car c’est beaucoup plus rapide. Pour optimiser la quantité de mémoire disponible pour EVA, vous pouvez utiliser le drapeau low_cpu_mem_usage dans get_peft_model() :
peft_model = get_peft_model(model, peft_config, low_cpu_mem_usage=True)Ensuite, appelez initialize_lora_eva_weights() pour initialiser les poids EVA (dans la plupart des cas, le dataloader utilisé pour l'initialisation EVA peut être le même que celui utilisé pour le finetuning) :
initialize_lora_eva_weights(peft_model, dataloader)
EVA fonctionne immédiatement avec bitsandbytes. Il suffit d’initialiser le modèle avec quantization_config et d’appeler initialize_lora_eva_weights() comme d’habitude.
Pour plus d'instructions sur l'utilisation d'EVA, veuillez consulter notre documentation.
Lors de la quantification du modèle de base pour l'entraînement QLoRA, envisagez d'utiliser l'initialisation LoftQ, qui a démontré une amélioration des performances lors de l'entraînement de modèles quantifiés. L'idée est d'initialiser les poids LoRA de manière à minimiser l'erreur de quantification. Pour utiliser LoftQ, suivez ces instructions.
En savoir plus sur le fonctionnement de PEFT avec la quantification et sur l’utilisation de LoftQ dans le guide Quantization.
Une autre façon d’initialiser LoraConfig est d’utiliser la méthode rank-stabilized LoRA (rsLoRA). L’architecture LoRA met à l’échelle chaque adaptateur lors de chaque passe avant par un scalaire fixe, défini à l’initialisation et dépendant du rang r. Ce scalaire est donné par lora_alpha/r dans l’implémentation originale, mais rsLoRA utilise lora_alpha/math.sqrt(r), ce qui stabilise les adaptateurs et augmente le potentiel de performance lié à l’utilisation d’un r plus élevé.
from peft import LoraConfig
config = LoraConfig(use_rslora=True, ...)LoRA-GA (Low-Rank Adaptation with Gradient Approximation) initialise les poids de l’adaptateur en effectuant une SVD sur des gradients estimés, de sorte que les poids s’alignent davantage sur le full-finetuning pour une convergence plus rapide.
Cette méthode nécessite une fonction d’initialisation pour estimer les gradients avant de commencer l’entraînement proprement dit :
from peft.tuners.lora import preprocess_loraga
def train_step():
"""Run forward and backward passes for gradient estimation."""
dataloader_iter = iter(grad_dataloader)
for _ in range(N):
batch = next(dataloader_iter)
batch = {k: v.to(device) for k, v in batch.items()}
outputs = model(**batch)
loss = outputs.loss
loss.backward()
preprocess_loraga(model, lora_config, train_step)Astuce d'utilisation
Estimation du gradient : LoRA-GA nécessite une phase d'estimation du gradient avant l'initialisation du modèle. Utilisez
preprocess_loraga()avec un rappeltrain_steppour calculer les gradients sur un petit nombre de lots d'entraînement (généralement 64 à 128 lots).Stratégies d’initialisation : LoRA-GA prend en charge quatre stratégies de direction (
direction) :"ArBr","A2rBr","ArB2r"(par défaut) et"random", ainsi que quatre stratégies de mise à l’échelle (scale) :"stable"(par défaut),"weight_svd","gd_scale"et"unit". La combinaison par défaut offre le meilleur équilibre entre vitesse de convergence et stabilité.Modification des poids de base : Contrairement à LoRA standard, LoRA-GA modifie les poids du modèle de base lors de l'initialisation en soustrayant une version mise à l'échelle de l'approximation de faible rang. Cela permet un meilleur alignement avec les gradients du fine-tuning complet. Étant donné que les poids de base sont modifiés, utilisez
save_pretrained()avec l'argumentsave_embedding_layersou passezpath_initial_model_for_weight_conversion.Surcoût de calcul : L'estimation du gradient ajoute un léger surcoût lors de l'initialisation (généralement 1 à 2 minutes pour 64 lots), mais celui-ci est rapidement amorti par une convergence plus rapide pendant l'entraînement.
Compatibilité : LoRA-GA nécessite des poids en pleine précision et ne prend pas en charge les modèles quantifiés. Il peut être combiné avec d'autres variantes de LoRA comme DoRA.
Entraînement
Cette section montre comment gérer des scénarios d’entraînement plus complexes, au lieu de simplement appliquer un adaptateur de faible rang au modèle et d’alimenter les données.
Entraînement de type QLoRA
Les paramètres par défaut de LoRA dans PEFT ajoutent des poids entraînables aux couches de requête et de valeur de chaque bloc d'attention. Mais QLoRA, qui ajoute des poids entraînables à toutes les couches linéaires d'un modèle transformer, peut offrir des performances égales à celles d'un modèle entièrement affiné. Pour appliquer LoRA à toutes les couches linéaires, comme dans QLoRA, définissez target_modules="all-linear" (plus simple que de spécifier individuellement les modules par nom, ce qui peut varier selon l'architecture).
config = LoraConfig(target_modules="all-linear", ...)Pour plus d'informations sur l'application de la quantification aux adaptateurs PEFT, consultez le guide de quantification.
Réplication efficace en mémoire des couches avec LoRA
Une approche utilisée pour améliorer les performances des modèles consiste à étendre un modèle en dupliquant des couches afin de construire un modèle plus grand à partir d'un modèle pré-entraîné d'une taille donnée. Par exemple, augmenter un modèle de 7B en un modèle de 10B, comme décrit dans l'article SOLAR. PEFT LoRA prend en charge ce type d'extension de manière efficace en mémoire, permettant un fine-tuning ultérieur à l'aide d'adaptateurs LoRA attachés aux couches après leur duplication. Les couches dupliquées n'occupent pas de mémoire supplémentaire car elles partagent les poids sous-jacents ; la seule mémoire additionnelle requise est celle des poids des adaptateurs. Pour utiliser cette fonctionnalité, vous devez créer une configuration avec l'argument layer_replication.
config = LoraConfig(layer_replication=[[0,4], [2,5]], ...)En supposant que le modèle d'origine avait 5 couches [0, 1, 2 ,3, 4], cela créerait un modèle avec 7 couches disposées comme suit [0, 1, 2, 3, 2, 3, 4]. Cela suit la convention de fusion « pass through » de mergekit, où les séquences de couches spécifiées sous forme de tuples début inclus et fin exclus sont empilées pour construire le modèle final. Chaque couche du modèle final reçoit son propre ensemble distinct d'adaptateurs LoRA.
Fewshot-Metamath-OrcaVicuna-Mistral-10B est un exemple de modèle entraîné à l'aide de cette méthode sur Mistral-7B étendu à 10B. Le fichier adapter_config.json présente un exemple de configuration d'adaptateur LoRA appliquant cette méthode pour le fine-tuning.
Contrôle fin des rangs et de l'alpha (mise à l'échelle)
Par défaut, toutes les cibles visées par LoRA auront le même rang r et la même valeur de lora_alpha (qui détermine l'échelle de LoRA), selon ce qui a été spécifié dans LoraConfig. Dans certains cas, toutefois, vous pouvez vouloir indiquer des valeurs différentes pour différentes cibles. Cela est possible en passant les arguments rank_pattern et alpha_pattern à LoraConfig. Ces arguments doivent être des dictionnaires dont la clé est le nom de la cible et la valeur est la valeur du rang/alpha. Les clés peuvent être des expressions régulières (regex). Toutes les cibles LoRA qui ne sont pas explicitement mentionnées dans rank_pattern et alpha_pattern prendront les valeurs par défaut de r et lora_alpha.
Pour donner un exemple, supposons que nous disposions d'un modèle avec la structure suivante :
>>> print(model)
Outer(
(foo): Linear(...)
(module): Middle(
(foo): Linear(...)
(foobar): Linear(...)
(module): Inner(
(foo): Linear(...)
(barfoo): Linear(...)
)
)
)rank_pattern={"foo": 42}correspondra aux 3 couchesfoo. Nifoobarnibarfoone sont correspondants.rank_pattern={"^foo": 42}ne correspondra qu’à la couchefoodu modèle, mais ni àmodule.fooni àmodule.module.foo. En effet,^signifie « début de chaîne » dans les expressions régulières, et seulfoocommence par"foo"; les autres noms de couches ont des préfixes.rank_pattern={"^module.foo": 42}ne correspond qu’àmodule.foo, mais pas àmodule.module.foo, pour la même raison.rank_pattern={"module.foo": 42}correspond à la fois àmodule.fooet àmodule.module.foo, mais pas àfoo.rank_pattern={"^foo": 42, "^module.module.foo": 55}correspond àfooet àmodule.module.foo, respectivement, mais pas àmodule.foo.- Il n'est pas nécessaire d'indiquer
$pour marquer la fin de la correspondance, car cela est ajouté automatiquement par PEFT.
La même logique s’applique à alpha_pattern. En cas de doute, évitez de compliquer les choses avec des expressions régulières — passez simplement le nom complet de chaque module ayant un rang/alpha différent, précédé du préfixe ^, et vous devriez être bon.
Détecter automatiquement les modules cibles viables
peft.helpers.KappaTuneSelector implémente la stratégie de sélection des cibles basée sur le nombre de conditionnement décrite dans l'article KappaTune. Elle parcourt chaque module nn.Linear et, pour les modèles dont les poids des experts MoE sont stockés sous forme de tenseurs nn.Parameter 3D fusionnés (par ex. Llama-4, Qwen3-MoE), également ces paramètres, calcule le nombre de conditionnement de la matrice κ = σ_max / σ_min pour chacun, et sélectionne les couches les plus isotropes (κ le plus faible). Ces couches isotropes constituent des candidats idéaux pour le fine-tuning, car leur nature à haute entropie leur permet d'absorber plus facilement de nouvelles informations, laissant les couches spécialisées et anisotropes intactes afin d'atténuer l'oubli catastrophique lors de l'apprentissage continu.
Utilisez peft.helpers.find_kappa_target_modules() en une seule ligne pour obtenir les target_modules optimaux pour LoraConfig :
from peft import LoraConfig, get_peft_model
from peft.helpers import find_kappa_target_modules
model = AutoModelForCausalLM.from_pretrained("mistralai/Mixtral-8x7B-Instruct-v0.1")
targets = find_kappa_target_modules(model, top_p=0.2)
config = LoraConfig(
target_modules=targets["target_modules"],
target_parameters=targets["target_parameters"] if stable_modules_dic["target_parameters"] else None,
r=64,
lora_alpha=32,
task_type="CAUSAL_LM",
)
peft_model = get_peft_model(model, config)Voir un exemple complet ici.
Cibler directement nn.Parameter
En général, vous devez utiliser target_modules pour cibler le module (par exemple nn.Linear). Cependant, dans certaines circonstances, cela n'est pas possible. Par exemple, dans de nombreuses couches mixture of expert (MoE) de HF Transformers, un nn.Parameter est utilisé au lieu de nn.Linear. PEFT remplace normalement la méthode forward pour LoRA, mais pour nn.Parameter, il n'en existe pas. Par conséquent, pour appliquer LoRA à ce paramètre, il doit être ciblé avec target_parameters. À titre d'exemple, pour Llama4, vous pouvez passer : target_parameters=['feed_forward.experts.gate_up_proj', 'feed_forward.experts.down_proj].
Notez que lorsque l'on cible les paramètres d'experts, PEFT peut ajouter un surcoût d'exécution considérable. La raison en est que PEFT matérialise toujours la contribution LoRA pour chaque expert, même si un petit nombre d'experts est requis. Pendant l'entraînement, cela est moins pertinent car, au fil de la séquence, une grande proportion d'experts est généralement activée au moins une fois. En revanche, lors de l'inférence, un cache KV est normalement utilisé et nous n'avons donc besoin de calculer que le dernier jeton, ce qui signifie qu'un petit nombre d'experts seulement est activé. Par conséquent, l'utilisation de LoRA sur les couches MoE peut entraîner un ralentissement important au moment de l'inférence. La recommandation est donc de fusionner les poids (model.merge_adapter() ou model = model.merge_and_unload()). Cela élimine le surcoût de PEFT.
Une investigation plus détaillée de ce problème peut être trouvée dans cette pull request sur l'optimisation MoE.
Limites
- Actuellement, cet argument permet de cibler des
nn.Parameterde dimension 2 ou 3. Il est supposé que, dans le cas d'un paramètre de dimension 3, la dimension 0 est la dimension des experts. - Plusieurs adaptateurs LoRA (ajoutés via
model.add_adapteroumodel.load_adapter) qui utilisenttarget_parameterssont pris en charge, mais ils doivent tous cibler le même ensemble de paramètres.
Paramètres des experts MoE et vLLM
Certains modèles MoE dans Transformers stockent les poids des experts sous forme de tenseurs nn.Parameter (souvent 3D), et non de modules nn.Linear.
Pour appliquer LoRA à ces experts, utilisez target_parameters et définissez un rang par couche avec rank_pattern :
num_experts = getattr(model.config, "num_local_experts", None) or model.config.num_experts
effective_r = max(1, r // num_experts)
config = LoraConfig(
r=r,
lora_alpha=32,
target_modules=["q_proj", "v_proj"],
target_parameters=[
# Mixtral / Qwen3-MoE / GPT-OSS
"mlp.experts.gate_up_proj",
"mlp.experts.down_proj",
# Llama4
# "feed_forward.experts.gate_up_proj",
# "feed_forward.experts.down_proj",
],
rank_pattern={
"experts.gate_up_proj": effective_r,
"experts.down_proj": effective_r,
},
)Cela maintient le budget total de paramètres LoRA similaire à celui des couches denses (voir LoRA Without Regret de Schulman et al.). Les modules non experts utilisent le rang par défaut r.
L’inférence accélérée avec le modèle affiné est possible, par exemple, avec vLLM, qui prend en charge les couches d’experts MoE fusionnées depuis la v0.11.2.
Entraîner efficacement des jetons en parallèle de LoRA
Les adaptateurs LoRA de PEFT prennent en charge l’ajout de nouveaux jetons via le paramètre trainable_token_indices. Cela permet d’ajuster d’autres jetons en parallèle de l’ajustement fin de couches spécifiques. Seuls les jetons spécifiés sont entraînés, tous les autres restent inchangés. Cette approche économise de la mémoire et préserve le contexte appris des embeddings de jetons existants, contrairement à l’entraînement de toute la matrice d’embeddings. En interne, cette méthode utilise la couche de TrainableTokensModel.
# for layer 'embed_tokens'
config = LoraConfig(trainable_token_indices=[idx_1, idx_2, ...], ...)
# specific embedding layer
config = LoraConfig(trainable_token_indices={'emb_tokens': [idx_1, idx_2, ...]}, ...)Dans l'extrait ci-dessous, nous montrons comment ajouter de nouveaux jetons au modèle et comment l'entraîner en même temps que les autres couches du modèle.
from transformers import AutoTokenizer, AutoModelForCausalLM
from peft import get_peft_model, LoraConfig
base_model = AutoModelForCausalLM.from_pretrained("mistralai/Mistral-7B-v0.1")
tokenizer = AutoTokenizer.from_pretrained("mistralai/Mistral-7B-v0.1")
# we define our new tokens and add them to the tokenizer as special tokens
special_tokens = ['<|start_think|>', '<|stop_think|>']
tokenizer.add_special_tokens({'additional_special_tokens': special_tokens})
# make room for new tokens in the embedding matrix if it isn't big enough already
base_model.resize_token_embeddings(max(len(tokenizer), base_model.model.embed_tokens.num_embeddings))
# typical LoRA config with `trainable_token_indices` targeting embedding layer `embed_tokens`
# and specifically our new tokens we just added
lora_config = LoraConfig(
target_modules='all-linear',
trainable_token_indices={'embed_tokens': tokenizer.convert_tokens_to_ids(special_tokens)},
)
peft_model = get_peft_model(base_model, lora_config)
# proceed to train the model like normal
[...]Les poids des jetons sont enregistrés en tant que partie du dictionnaire d'état de l'adaptateur, aux côtés des poids LoRA. Un fine-tuning complet et l'enregistrement de la matrice d'incorporation auraient généré un fichier beaucoup plus volumineux.
Pour donner une idée de la quantité de VRAM qui peut être économisée, une comparaison rudimentaire de l’exemple ci-dessus a été réalisée entre l’entraînement complet de la matrice d’embedding (modules_to_save=["embed_tokens"]), l’utilisation d’un LoRA pour la matrice d’embedding (target_modules=[..., "embed_tokens"], rang 32) et des tokens entraînables (trainable_token_indices=[...], 6 tokens) :
| Jetons entraînables | LoRA | Ajustement fin complet | |
|---|---|---|---|
| VRAM | 15,562 Mo | 15,581 Mo | ~16 500 Mo |
| Influence | 6 jetons | tous les jetons | tous les jetons |
Liaison des poids
De nombreux LM causaux utilisent le weight tying, où deux poids ou plus partagent les mêmes paramètres sous-jacents. Dans le cas le plus courant, les poids d'incorporation d'entrée (embed_tokens) et les poids de projection de sortie (lm_head) partagent le même tenseur. Cela permet de réduire le nombre de paramètres et préserve généralement la qualité du modèle.
Il n’est pas toujours évident de savoir comment PEFT gère ces poids liés lorsqu’ils sont visés par l’ajustement fin. Pour LoRA, le paramètre ensure_weight_tying de LoraConfig détermine si PEFT doit explicitement maintenir les mises à jour du côté adaptateur liées pour ces couches. En pratique, cela peut affecter modules_to_save, target_modules et trainable_token_indices. Notez que cette logique repose partiellement sur des conventions concernant le nommage des couches ("embed_tokens", "lm_head") et que son bon fonctionnement ne peut pas être garanti si ces conventions ne sont pas respectées.
Les tableaux ci-dessous récapitulent le comportement attendu.
modules_to_save
| Poids du modèle de base liés | ensure_weight_tying | Forme de LoraConfig | Comportement |
|---|---|---|---|
| Non | False | modules_to_save=["embed_tokens"] ou ["lm_head"] | Ajouter ModulesToSaveWrapper uniquement sur la couche sélectionnée |
| Non | True | modules_to_save=["embed_tokens"] ou ["lm_head"] | Émettre un avertissement, puis ajouter ModulesToSaveWrapper uniquement sur la couche sélectionnée |
| Oui | False | modules_to_save=["embed_tokens"] ou ["lm_head"] | Traiter comme séparé |
| Oui | True | modules_to_save=["embed_tokens"] ou ["lm_head"] | Envelopper les couches liées et maintenir les enveloppeurs liés |
| Non | False | modules_to_save=["embed_tokens", "lm_head"] | Traiter comme séparé |
| Non | True | modules_to_save=["embed_tokens", "lm_head"] | Émettre un avertissement, puis traiter comme séparé |
| Oui | False | modules_to_save=["embed_tokens", "lm_head"] | Émettre un avertissement, puis traiter comme séparé |
| Oui | True | modules_to_save=["embed_tokens", "lm_head"] | Garder les ModulesToSaveWrapper liés |
target_modules
| Poids du modèle de base liés | ensure_weight_tying | Forme de LoraConfig | Comportement |
|---|---|---|---|
| Non | False | target_modules=["embed_tokens"] ou ["lm_head"] | Ajouter LoRA uniquement sur la couche sélectionnée |
| Non | True | target_modules=["embed_tokens"] ou ["lm_head"] | Émettre un avertissement, puis ajouter LoRA uniquement sur la couche sélectionnée |
| Oui | False | target_modules=["embed_tokens"] ou ["lm_head"] | Traiter comme séparé |
| Oui | True | target_modules=["embed_tokens"] ou ["lm_head"] | Garder les adaptateurs LoRA liés |
| Non | False | target_modules=["embed_tokens", "lm_head"] | Traiter comme séparé |
| Non | True | target_modules=["embed_tokens", "lm_head"] | Émettre un avertissement, puis traiter comme séparé |
| Oui | False | target_modules=["embed_tokens", "lm_head"] | Émettre un avertissement, puis traiter comme séparé |
| Oui | True | target_modules=["embed_tokens", "lm_head"] | Garder les adaptateurs LoRA liés |
trainable_token_indices
Pour les jetons entraînables, nous avons la complication supplémentaire que, même si la tête du LM et les plongements sont liés, en tant qu’utilisateur, je peux vouloir affiner des jetons différents sur eux. Dans le tableau d’exemple ci-dessous, nous distinguons donc l’affinement des mêmes jetons de l’affinement de jetons différents.
| Poids du modèle de base liés | ensure_weight_tying | Forme de LoraConfig | Comportement |
|---|---|---|---|
| Non | False | trainable_token_indices=[1, 2, 3] | Jetons entraînables sur les embeddings uniquement |
| Non | True | trainable_token_indices=[1, 2, 3] | Avertir, puis jetons entraînables uniquement sur les embeddings |
| Oui | False | trainable_token_indices=[1, 2, 3] | Jetons d'entraînement liés |
| Oui | True | trainable_token_indices=[1, 2, 3] | Jetons d'entraînement liés |
| Non | False | trainable_token_indices={"lm_head": [1, 2], "embed_tokens": [1, 2]} | Traiter comme séparé |
| Non | True | trainable_token_indices={"lm_head": [1, 2], "embed_tokens": [1, 2]} | Émettre un avertissement, puis traiter comme séparé |
| Oui | False | trainable_token_indices={"lm_head": [1, 2], "embed_tokens": [1, 2]} | Jetons d'entraînement liés |
| Oui | True | trainable_token_indices={"lm_head": [1, 2], "embed_tokens": [1, 2]} | Jetons d'entraînement liés |
| Non | False | trainable_token_indices={"lm_head": [1, 2], "embed_tokens": [3, 4]} | Traiter comme séparé |
| Non | True | trainable_token_indices={"lm_head": [1, 2], "embed_tokens": [3, 4]} | Émettre un avertissement, puis traiter comme séparé |
| Oui | False | trainable_token_indices={"lm_head": [1, 2], "embed_tokens": [3, 4]} | Traiter comme séparé |
| Oui | True | trainable_token_indices={"lm_head": [1, 2], "embed_tokens": [3, 4]} | Erreur |
Pour les utilisateurs, cela signifie :
- En général, si vous souhaitez affiner des poids liés et les garder liés, passez
ensure_weight_tying=True. - Si les poids du modèle de base ne sont pas liés,
ensure_weight_tying=Truene peut pas forcer le lien et émet uniquement un avertissement. - Pour
trainable_token_indices, les couches liées doivent utiliser les mêmes indices de jetons lorsqueensure_weight_tying=True.
Optimiseurs
L’entraînement LoRA peut éventuellement inclure des optimiseurs spécialisés. Actuellement, PEFT prend en charge LoRA-FA, LoRA+ et Riemannian-preconditioned LoRA.
Optimiseur LoRA-FA
L’entraînement LoRA peut être plus efficace et performant en utilisant LoRA-FA, tel que décrit dans LoRA-FA. LoRA-FA réduit la consommation de mémoire des activations en fixant la matrice A et en n’ajustant que la matrice B. Pendant l’entraînement, le gradient de B est optimisé pour approximer le gradient de l’ajustement fin des paramètres complets. De plus, la consommation de mémoire de LoRA-FA n’est pas sensible au rang (puisque cela efface l’activation de $A$), il est donc possible d’améliorer les performances en augmentant le rang lora sans augmenter la consommation de mémoire.
from peft import LoraConfig, get_peft_model
from peft.optimizers import create_lorafa_optimizer
from transformers import Trainer, get_cosine_schedule_with_warmup
base_model = AutoModelForCausalLM.from_pretrained("meta-llama/Meta-Llama-3-8B-Instruct")
config = LoraConfig(...)
model = get_peft_model(base_model, config)
optimizer = create_lorafa_optimizer(
model=model,
r=128,
lora_alpha=32,
lr=7e-5,
)
scheduler = get_cosine_schedule_with_warmup(
optimizer,
num_warmup_steps=100,
num_training_steps=1000,
)
trainer = Trainer(
...,
optimizers=(optimizer, scheduler),
)LoRA+ optimisé
L’entraînement LoRA peut être optimisé à l’aide de LoRA+, qui utilise des taux d’apprentissage distincts pour les matrices d’adaptateurs A et B, permettant d’accélérer le fine-tuning jusqu’à 2 fois et d’améliorer les performances de 1 à 2 %.
from peft import LoraConfig, get_peft_model
from peft.optimizers import create_loraplus_optimizer
from transformers import Trainer
import bitsandbytes as bnb
base_model = ...
config = LoraConfig(...)
model = get_peft_model(base_model, config)
optimizer = create_loraplus_optimizer(
model=model,
optimizer_cls=bnb.optim.Adam8bit,
lr=5e-5,
loraplus_lr_ratio=16,
)
scheduler = None
...
trainer = Trainer(
...,
optimizers=(optimizer, scheduler),
)LoRA préconditionné par Riemann
L'entraînement LoRA peut être amélioré avec un préconditionneur riemannien, comme décrit dans Riemannian Preconditioned LoRA. À chaque étape de l'optimiseur, les gradients des matrices LoRA et sont multipliés par un préconditionneur qui rééchelonne le gradient euclidien vers la direction riemannienne (gradient mis à l'échelle) sur la variété des matrices de faible rang, ce qui améliore le conditionnement de la mise à jour et peut accélérer la convergence. Étant donné que le préconditionneur est de taille $r \times r$, son surcoût en mémoire et en temps d'exécution est faible par rapport au rang de LoRA. create_riemannian_optimizer encapsule n'importe quelle classe d'optimiseur de base (par ex. torch.optim.AdamW ou torch.optim.SGD): les paramètres non-LoRA sont mis à jour par l'optimiseur de base sans modification, et seuls nn.Linear-les couches LoRA de formelora_A / lora_B) sont préconditionnés.
from peft import LoraConfig, get_peft_model
from peft.optimizers import create_riemannian_optimizer
from transformers import Trainer, get_cosine_schedule_with_warmup
import torch
base_model = AutoModelForCausalLM.from_pretrained("meta-llama/Meta-Llama-3-8B-Instruct")
config = LoraConfig(...)
model = get_peft_model(base_model, config)
optimizer = create_riemannian_optimizer(
model=model,
optimizer_cls=torch.optim.AdamW,
lr=5e-5,
reg=1e-2,
)
scheduler = get_cosine_schedule_with_warmup(
optimizer,
num_warmup_steps=100,
num_training_steps=1000,
)
trainer = Trainer(
...,
optimizers=(optimizer, scheduler),
)reg est un terme d'amortissement ajouté à la diagonale de la matrice avant inversion ; il stabilise le préconditionneur lorsqu'un facteur LoRA est (quasi) de rang déficient.
Entraînement post
Cette section présente des méthodes de post-traitement potentielles pour les adaptateurs entraînés.
Fusionner les poids LoRA dans le modèle de base
Bien que LoRA soit nettement plus petit et plus rapide à entraîner, vous pouvez rencontrer des problèmes de latence lors de l’inférence en raison du chargement séparé du modèle de base et de l’adaptateur LoRA. Pour éliminer cette latence, utilisez la fonction merge_and_unload() pour fusionner les poids de l’adaptateur avec le modèle de base. Cela vous permet d’utiliser le modèle fusionné comme un modèle autonome. La fonction merge_and_unload() ne conserve pas les poids de l’adaptateur en mémoire.
Voici un schéma qui explique l'intuition derrière la fusion des adaptateurs LoRA :

Nous montrons dans les extraits ci-dessous comment l'exécuter à l'aide de PEFT.
from transformers import AutoModelForCausalLM
from peft import PeftModel
base_model = AutoModelForCausalLM.from_pretrained("mistralai/Mistral-7B-v0.1")
peft_model_id = "alignment-handbook/zephyr-7b-sft-lora"
model = PeftModel.from_pretrained(base_model, peft_model_id)
model = model.merge_and_unload()Il est important d'assigner le modèle retourné à une variable et de l'utiliser, merge_and_unload() n'est pas une opération en place. Si vous devez conserver une copie des poids afin de pouvoir désintégrer l'adaptateur plus tard ou supprimer et charger d'autres adaptateurs, vous devriez utiliser la fonction merge_adapter() à la place. Vous avez désormais la possibilité d'utiliser unmerge_adapter() pour revenir au modèle de base.
from transformers import AutoModelForCausalLM
from peft import PeftModel
base_model = AutoModelForCausalLM.from_pretrained("mistralai/Mistral-7B-v0.1")
peft_model_id = "alignment-handbook/zephyr-7b-sft-lora"
model = PeftModel.from_pretrained(base_model, peft_model_id)
model.merge_adapter()
# unmerge the LoRA layers from the base model
model.unmerge_adapter()Sur un modèle quantifié, la désintégration n’est pas exacte : la fusion re-quantise le poids de base, et des cycles répétés de fusion et de désintégration peuvent accumuler des erreurs d’arrondi. Voir Fusion des adaptateurs dans les poids quantifiés.
Si plusieurs adaptateurs sont actifs et que seuls certains d’entre eux sont fusionnés, les adaptateurs non fusionnés ne sont pas appliqués silencieusement. Ce comportement changera dans PEFT v1.0. Voir Fusionner uniquement certains des adaptateurs actifs.
La fonction add_weighted_adapter() est utile pour fusionner plusieurs LoRA dans un nouvel adaptateur en fonction d'un schéma de pondération fourni par l'utilisateur dans le paramètre weights. Voici un exemple de bout en bout.
Chargez d'abord le modèle de base :
from transformers import AutoModelForCausalLM
from peft import PeftModel
import torch
base_model = AutoModelForCausalLM.from_pretrained(
"mistralai/Mistral-7B-v0.1", dtype=torch.float16, device_map="auto"
)Nous chargeons ensuite le premier adaptateur :
peft_model_id = "alignment-handbook/zephyr-7b-sft-lora"
model = PeftModel.from_pretrained(base_model, peft_model_id, adapter_name="sft")Chargez ensuite un adaptateur différent et fusionnez-le avec le premier :
weighted_adapter_name = "sft-dpo"
model.load_adapter("alignment-handbook/zephyr-7b-dpo-lora", adapter_name="dpo")
model.add_weighted_adapter(
adapters=["sft", "dpo"],
weights=[0.7, 0.3],
adapter_name=weighted_adapter_name,
combination_type="linear"
)
model.set_adapter(weighted_adapter_name)Plusieurs méthodes sont prises en charge pour
combination_type. Consultez la documentation pour plus de détails. Notez que « svd » en tant quecombination_typen'est pas pris en charge lorsquetorch.float16outorch.bfloat16est utilisé comme type de données.
Effectuez maintenant l'inférence :
device = torch.accelerator.current_accelerator().type if hasattr(torch, "accelerator") else "cuda"
tokenizer = AutoTokenizer.from_pretrained("mistralai/Mistral-7B-v0.1")
prompt = "Hey, are you conscious? Can you talk to me?"
inputs = tokenizer(prompt, return_tensors="pt")
inputs = {k: v.to(device) for k, v in inputs.items()}
with torch.no_grad():
generate_ids = model.generate(**inputs, max_length=30)
outputs = tokenizer.batch_decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0]
print(outputs)Récupération des performances du modèle de base par réduction de la dimension des intrus
L’article LoRA vs Full Fine-tuning: An Illusion of Equivalence soutient que l’entraînement LoRA introduit des dimensions supplémentaires dans les poids, qui ont très peu en commun avec les poids déjà appris et entraînent l’oubli d’informations déjà acquises. PEFT implémente l’atténuation suggérée dans peft.tuners.lora.intruders.reduce_intruder_dimension().
La mitigation consiste à prendre un modèle PEFT doté d'un LoRA chargé et à créer un nouvel adaptateur modifié, qui est chargé aux côtés de l'adaptateur existant et devient l'adaptateur actif.
Exemple d'utilisation :
from peft.tuners.lora.intruders import reduce_intruder_dimension
peft_model = AutoPeftModelForCausalLM.from_pretrained('hubnemo/llama-3.2b-metamathqa-lora64')
reduce_intruder_dimension(
peft_model,
mitigation_lambda=0.75,
)
peft_model.generate(...)Il existe quelques hyperparamètres qui peuvent être utilisés pour ajuster l’efficacité de l’atténuation, mais, comme le montre la figure 8 de l’article, il y aura toujours un compromis entre la précision de la tâche apprise par l’adaptateur et l’oubli des connaissances du modèle de base. L’atténuation supprime des informations de l’adaptateur pour réduire l’impact sur l’oubli des connaissances antérieures, mais cela signifie également que certaines informations sur la tâche apprise par l’adaptateur sont perdues.
Bien que les valeurs par défaut soient configurées pour offrir un bon compromis entre les deux facteurs, il n’est pas garanti qu’elles conviennent à votre adaptateur, à votre modèle et à vos données. Il est donc judicieux de disposer d’un benchmark prêt à l’emploi pour mesurer l’effet.
Charger les adaptateurs
Les adaptateurs peuvent être chargés sur un modèle pré-entraîné à l’aide de load_adapter(), ce qui est utile pour tester différents adaptateurs dont les poids ne sont pas fusionnés. Définissez les poids de l’adaptateur actif avec la fonction set_adapter().
from transformers import AutoModelForCausalLM
from peft import PeftModel
base_model = AutoModelForCausalLM.from_pretrained("mistralai/Mistral-7B-v0.1")
peft_model_id = "alignment-handbook/zephyr-7b-sft-lora"
model = PeftModel.from_pretrained(base_model, peft_model_id)
# load different adapter
model.load_adapter("alignment-handbook/zephyr-7b-dpo-lora", adapter_name="dpo")
# set adapter as active
model.set_adapter("dpo")Pour récupérer le modèle de base, vous pouvez utiliser unload() pour décharger tous les modules LoRA ou delete_adapter() pour supprimer l'adaptateur entièrement. unload() n'est pas une opération en place, n'oubliez pas d'assigner le modèle retourné à une variable et de l'utiliser.
# unload adapter
model = model.unload()
# delete adapter
model.delete_adapter("dpo")Parallélisme de tenseurs
LoRA prend en charge le Tensor Parallelism (TP) fourni par Transformers. Lorsqu'un modèle de base est chargé avec un tp_plan, PEFT détecte automatiquement la configuration TP de chaque module cible et ajoute les hooks appropriés aux poids de l'adaptateur LoRA afin qu'ils participent correctement au calcul tensor-parallel.
La prise en charge du parallélisme de tenseurs pour LoRA nécessite
transformers >= 5.4.0. L'API de parallélisme de tenseurs basée sur DTensor nécessitepeft >= 0.21.1ettransformers >= 5.17.0.
L'utilisation est identique à celle du flux de travail standard LoRA — il suffit de charger le modèle de base avec un tp_plan avant de l'encapsuler avec PEFT :
from transformers import AutoModelForCausalLM
from peft import get_peft_model, LoraConfig
model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-3.1-8B", tp_plan="auto")
lora_config = LoraConfig(r=16, target_modules=["q_proj", "v_proj"])
model = get_peft_model(model, lora_config)La sauvegarde et le chargement fonctionnent comme d’habitude via save_pretrained / from_pretrained. PEFT regroupe les poids d’adaptateurs fragmentés en tenseurs complets avant la sauvegarde, de sorte que les points de contrôle sont portables et indépendants du nombre d’appareils utilisés pendant l’entraînement.
Si vous appliquez le parallélisme de tenseurs sans plan TP, par exemple avec parallelize_module de PyTorch, PEFT dimensionne les poids LoRA d’un nn.Linear fragmenté à la taille de son fragment local si le maillage d’appareils du poids possède une dimension nommée "tp", et à la forme complète de la couche dans le cas contraire (comme avec FSDP2). PEFT n’ajoute pas de hooks TP à ces poids LoRA ni ne les regroupe lors de l’enregistrement. Si la dimension de votre maillage TP porte un nom différent, ajoutez-la avant de créer le modèle PEFT :
from peft.utils.integrations import TpMeshDimRegistry
TpMeshDimRegistry.register("tp_dim")Inférence
Cette section présente ce que vous pouvez faire lors de l'inférence avec LoRA, comme le découplage de l'adaptateur.
LoRA activé (aLoRA)
Activated LoRA (aLoRA) est une architecture d’adaptateur de faible rang pour les LM causaux qui réutilise le cache KV du modèle de base existant pour une inférence plus efficace. Cette approche est particulièrement adaptée aux pipelines d’inférence qui s’appuient sur le modèle de base pour la plupart des tâches/générations, mais utilisent un ou plusieurs adaptateurs aLoRA pour effectuer des tâches spécialisées au sein de la chaîne. Par exemple, vérifier ou corriger les sorties générées par le modèle de base. Dans ces configurations, les temps d’inférence peuvent être réduits d’un ordre de grandeur ou plus. Pour plus d’informations sur aLoRA et de nombreux cas d’usage, consultez le paper aLoRA.
Cette technique recherche la dernière occurrence d’une séquence d’invocation (alora_invocation_tokens) dans chaque entrée (celle-ci peut être aussi courte qu’un seul jeton). Elle active les poids de l’adaptateur sur les jetons à partir du début de la séquence d’invocation. Les entrées situées après la séquence d’invocation sont également adaptées, et tous les jetons générés utiliseront les poids adaptés. Les poids des jetons précédents sont laissés non adaptés, ce qui rend le cache de ces jetons interchangeable avec le cache du modèle de base en raison du masque d’attention causale des LM causaux. L’utilisation est très similaire à celle de LoRA standard. La différence clé est que la séquence d’invocation doit être spécifiée lors de la création de l’adaptateur :
from peft import LoraConfig
config = LoraConfig(alora_invocation_tokens=alora_invocation_tokens, task_type="CAUSAL_LM", ...)alora_invocation_tokens` est une liste d'identifiants de jetons entiers. Étant donné une chaîne d'invocation souhaitée, celle-ci peut être obtenue comme suit :
invocation_string = "placeholder"
alora_invocation_tokens = tokenizer.encode(invocation_string, add_special_tokens=False).Le tokenizer est celui du modèle de base. Utilisez add_special_tokens=False pour éviter d’ajouter les jetons SOS/EOS dans notre chaîne de recherche (ce qui entraînera très probablement l’échec de la recherche).
Remarques
- aLoRA n'est pris en charge que pour les tâches
task_type=CAUSAL_LMen raison de son accent sur la réutilisation du cache. - Comme les poids sont adaptés sur un nombre réduit de jetons, aLoRA nécessite souvent (pas toujours) un rang (
r) plus élevé que LoRA.r=32peut être un bon point de départ. - Les poids aLoRA ne peuvent pas être fusionnés dans le modèle de base par définition, car les poids de l'adaptateur sont appliqués sélectivement à un sous-ensemble de jetons. Toute tentative de fusion déclenchera des erreurs.
- La recherche par faisceau n'est pas encore prise en charge.
- Il est généralement déconseillé d’ajouter de nouveaux jetons au tokenizer qui ne sont pas présents dans le modèle de base. Cela peut compliquer le cas d’usage cible où le modèle de base et le modèle adaptateur fonctionnent sur un contexte commun. Vous pouvez contourner ce problème en ajoutant des jetons entraînables au modèle de base avant l’entraînement de l’adaptateur.
Choix de la séquence d'appel et conception du SFT
Vous devez ajouter la séquence alora_invocation_tokens car elle n’est pas ajoutée automatiquement. Nous recommandons d’activer les poids de l’adaptateur tôt (au début de tout promptage spécifique à l’adaptateur), mais après les entrées longues, afin de maximiser les performances du modèle sans compromettre la réutilisation du cache. Comme pour tout modèle, le formatage doit être cohérent entre l’entraînement et le test.
Considérons l’exemple suivant, où le modèle de base possède un gabarit de discussion (chat template) et où l’objectif est d’entraîner l’adaptateur à générer une sortie souhaitée.
- Option 1 : s'il n'y a pas de prompt spécifique à la tâche, c'est-à-dire si l'entrée est un historique de chat avec le prompt
assistant, alors le promptassistantdu modèle de chat (par exemple<|start_of_role|>assistant<|end_of_role|>) est un choix naturel pour la chaîne d'appel. Consultez le modèle de chat du modèle pour trouver le prompt correspondant. - Option 2 : s’il existe un prompt spécifique à la tâche pour l’adaptateur qui décrit la tâche que l’adaptateur apprend, et que ce prompt est placé comme un tour
userimmédiatement avant la génération, alors le promptuserdu modèle de dialogue (par exemple<|start_of_role|>user<|end_of_role|>) est un choix naturel pour la chaîne d’invocation.
Après avoir défini la chaîne d'appel, récupérez le tokenizer du modèle et obtenez alora_invocation_tokens comme
alora_invocation_tokens = tokenizer.encode(invocation_string, add_special_tokens=False).Un exemple de configuration d'inférence se trouve dans alora finetuning.
Lors de l’utilisation de chaînes personnalisées pour la chaîne d’invocation, assurez-vous que le début et la fin de la chaîne sont des jetons spéciaux afin d’éviter les problèmes de tokenisation aux frontières.
Pour comprendre pourquoi, imaginez que ‘a’, ‘b’, ‘c’ et ‘ab’ sont des jetons dans votre tokenizer (numéros 1, 2, 3 et 4 respectivement). Supposons que votre alora_invocation_tokens = [2, 3]. Imaginez maintenant que votre chaîne d’entrée est “abc”. Comme “ab” est un jeton, cela sera tokenisé en [4,3]. Ainsi, alora_invocation_tokens ne sera pas trouvé, bien que la chaîne “bc” soit présente. Cependant, si le début et la fin de la chaîne d’invocation sont des jetons spéciaux, ce cas d’échec ne se produira jamais, car les jetons spéciaux ne sont jamais tokenisés dans le même jeton que d’autres caractères.
Utilisation (et réutilisation) du cache pour la génération
L'objectif principal de aLoRA est de rendre le cache KV interchangeable entre le modèle de base et les modèles adaptateurs aLoRA avant la séquence d'appel, car les valeurs KV de base et adaptées ne sont pas compatibles. Plus précisément, les clés et les valeurs stockées lors d'une génération de modèle peuvent être réutilisées lors des générations suivantes afin d'éviter les opérations de préremplissage coûteuses pour les jetons de contexte. Lors du partage du cache entre le modèle de base et les adaptateurs aLoRA, il existe deux principaux schémas :
- Le modèle de base a généré quelque chose, puis un adaptateur aLoRA est appelé pour effectuer une génération de suivi. Par exemple, le modèle de base répond à une question, et un aLoRA entraîné à détecter les hallucinations vérifie la réponse du modèle de base.
- Un adaptateur aLoRA a généré un contenu, et le modèle de base ou un autre adaptateur aLoRA est appelé pour effectuer une génération de suivi où il y a un chevauchement partiel du contexte avec l’adaptateur aLoRA d’origine. Par exemple, l’utilisateur fournit une requête, et un aLoRA réécrit la requête pour qu’elle soit plus autonome et améliore la récupération dans un système RAG. Ensuite, des documents sont récupérés et chargés dans le contexte, aLoRA vérifie si ces documents sont pertinents par rapport à la question, puis le modèle de base génère une réponse.
Pour illustrer les comportements décrits ci-dessus lors de l'utilisation du cache, nous utilisons DynamicCache de transformers. Veillez à ne pas mélanger les valeurs de cache adaptées aux valeurs de cache de base. En particulier, une étape supplémentaire est requise pour partager le cache lorsqu'il y a un chevauchement partiel du contexte (motif 2).
Modèle 1 : modèle de base suivi de aLoRA Ici, l'entrée et la génération complètes du modèle de base sont transmises à l'adaptateur aLoRA, ainsi que la séquence d'appel :
from transformers import DynamicCache
...
cache = DynamicCache()
inputs_base = tokenizer(prompt_base, return_tensors="pt")
# Generate from base model and save cache
with model_alora.disable_adapter():
output = model_alora.generate(inputs_base["input_ids"].to(device),attention_mask=inputs_base["attention_mask"].to(device),past_key_values = cache,return_dict_in_generate=True)
output_text_base = tokenizer.decode(output.sequences[0])
cache = output.past_key_values
# Generate with aLoRA adapter from cache
prompt_alora = output_text + INVOCATION_STRING
inputs_alora = tokenizer(prompt_alora, return_tensors="pt").to(device)
output = model_alora.generate(**inputs_alora, past_key_values=cache)
output_text_alora = tokenizer.decode(output[0])
# Note: cache is now tainted with adapter values and cannot be used in base model from here on!Modèle 2 : génération aLoRA suivie du modèle de base (ou d'un autre aLoRA) avec chevauchement partiel du contexte Ici, nous préremplissons le contexte partagé à l'aide du modèle de base, puis nous générons.
from transformers import DynamicCache
import copy
...
cache = DynamicCache()
inputs_shared = tokenizer(prompt_shared, return_tensors="pt").to(device)
# Prefill from base model and save cache
with model_alora.disable_adapter():
with torch.no_grad():
model_alora(**inputs_shared, past_key_values=cache)
cache_copy = copy.deepcopy(cache)
# Generate from aLoRA using prefilled cache
prompt_alora = prompt_shared + INVOCATION_STRING
inputs_alora = tokenizer(prompt_alora, return_tensors="pt").to(device)
output = model_alora.generate(**inputs_alora, past_key_values=cache)
output_text_alora = tokenizer.decode(output[0])
# Generate from base model using saved cache not tainted by aLoRA KV values
prompt_base = prompt_shared
inputs_base = tokenizer(prompt_base, return_tensors="pt").to(device)
with model_alora.disable_adapter():
output = model_alora.generate(**inputs_base, past_key_values=cache_copy)
output_text_base = tokenizer.decode(output[0])Inférence avec différents adaptateurs LoRA dans le même lot
Normalement, chaque lot d’inférence doit utiliser le même adaptateur (ou les mêmes adaptateurs) dans PEFT. Cela peut parfois être gênant, car nous pouvons avoir des lots contenant des échantillons destinés à être utilisés avec différents adaptateurs LoRA. Par exemple, nous pourrions avoir un modèle de base qui fonctionne bien en anglais et deux adaptateurs LoRA supplémentaires, l’un pour le français et l’autre pour l’allemand. Habituellement, nous devrions diviser nos lots de sorte que chaque lot ne contienne que des échantillons d’une seule des langues ; nous ne pouvons pas combiner différentes langues dans le même lot.
Heureusement, il est possible de mélanger différents adaptateurs LoRA dans le même lot à l'aide de l'argument adapter_name. Ci-dessous, nous montrons un exemple de fonctionnement en pratique. D'abord, chargez le modèle de base, l'anglais, ainsi que les deux adaptateurs, le français et l'allemand, comme suit :
from transformers import AutoTokenizer, AutoModelForCausalLM
from peft import PeftModel
model_id = ...
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(model_id)
# load the LoRA adapter for French
peft_model = PeftModel.from_pretrained(model, <path>, adapter_name="adapter_fr")
# next, load the LoRA adapter for German
peft_model.load_adapter(<path>, adapter_name="adapter_de")Nous voulons maintenant générer du texte sur un échantillon contenant les trois langues : les trois premiers échantillons sont en anglais, les trois suivants en français et les trois derniers en allemand. Nous pouvons utiliser l'argument adapter_names pour spécifier l'adaptateur à utiliser pour chaque échantillon. Comme notre modèle de base est utilisé pour l'anglais, nous utilisons la chaîne spéciale "__base__" pour ces échantillons. Pour les trois échantillons suivants, nous indiquons le nom de l'adaptateur du fine-tuning LoRA français, en l'occurrence "adapter_fr". Pour les trois derniers échantillons, nous indiquons le nom de l'adaptateur du fine-tuning LoRA allemand, en l'occurrence "adapter_de". De cette manière, nous pouvons utiliser le modèle de base et les deux adaptateurs dans un seul lot.
inputs = tokenizer(
[
"Hello, my dog is cute",
"Hello, my cat is awesome",
"Hello, my fish is great",
"Salut, mon chien est mignon",
"Salut, mon chat est génial",
"Salut, mon poisson est super",
"Hallo, mein Hund ist süß",
"Hallo, meine Katze ist toll",
"Hallo, mein Fisch ist großartig",
],
return_tensors="pt",
padding=True,
)
adapter_names = [
"__base__", "__base__", "__base__",
"adapter_fr", "adapter_fr", "adapter_fr",
"adapter_de", "adapter_de", "adapter_de",
]
output = peft_model.generate(**inputs, adapter_names=adapter_names, max_new_tokens=20)Notez que l’ordre n’a pas d’importance ici, c’est-à-dire que les échantillons du lot n’ont pas besoin d’être regroupés par adaptateur comme dans l’exemple ci-dessus. Il suffit de s’assurer que l’argument adapter_names est correctement aligné avec les échantillons.
De plus, la même approche fonctionne également avec la fonctionnalité modules_to_save, qui permet d'enregistrer et de réutiliser des couches spécifiques du réseau de neurones, telles que des têtes personnalisées pour des tâches de classification, entre différents adaptateurs LoRA.
Limites
L'utilisation de cette fonctionnalité présente certains inconvénients, à savoir :
- Cela ne fonctionne que pour l'inférence, pas pour l'entraînement.
- La désactivation des adaptateurs à l’aide du contexte
with model.disable_adapter()prime suradapter_names. - Vous ne pouvez pas passer
adapter_namessi certains poids d'adaptateurs ont été fusionnés avec les poids de base à l'aide de la méthodemerge_adapter. Veuillez d'abord dés-fusionner tous les adaptateurs en appelantmodel.unmerge_adapter(). Notez que la fusion de seulement certains des adaptateurs actifs présente une restriction similaire en dehors deadapter_names. - Pour des raisons évidentes, cela ne peut pas être utilisé après avoir appelé
merge_and_unload(), car dans ce cas, tous les adaptateurs LoRA seront fusionnés dans les poids de base. - Cette fonctionnalité ne fonctionne actuellement pas avec DoRA, il est donc nécessaire de définir
use_dora=Falsedans votreLoraConfigsi vous souhaitez l'utiliser. - La fonctionnalité
modules_to_saveest actuellement prise en charge uniquement pour les couches de typesLinear,Embedding,Conv2detConv1d. - Il existe un surcoût attendu pour l'inférence avec
adapter_names, en particulier si le nombre d'adaptateurs différents dans le lot est élevé. Cela est dû au fait que la taille du lot est effectivement réduite au nombre d'échantillons par adaptateur. Si les performances d'exécution sont votre priorité absolue, essayez ce qui suit :- Augmenter la taille du lot.
- Essayez d'éviter d'avoir un grand nombre d'adapteurs différents dans le même lot (batch) ; préférez des lots homogènes. Cela peut être obtenu en tamponnant les échantillons avec le même adaptateur et en n'effectuant l'inférence qu'avec un petit nombre d'adapteurs différents.
- Consultez des implémentations alternatives telles que LoRAX, punica ou S-LoRA, qui sont spécialisées pour fonctionner avec un grand nombre d'adaptateurs différents.
Composer et réutiliser des adaptateurs LoRA
Arrow
Arrow est un algorithme de routage modulaire conçu pour combiner plusieurs adaptateurs LoRA pré-entraînés et spécifiques à une tâche afin de résoudre une tâche donnée, de manière similaire à Polytropon mais sans nécessiter de fine-tuning. Plutôt que de fusionner naïvement tous les adaptateurs, Arrow introduit un mécanisme de routage sans gradient, par jeton, de type mixture-of-experts (MoE). Lors de l'inférence, il calcule d'abord un prototype pour chaque LoRA en extrayant le premier vecteur singulier droit de sa décomposition SVD. Chaque représentation de jeton est ensuite comparée à ces prototypes via la similarité cosinus pour obtenir des coefficients de routage. Les jetons sont affectés aux k adaptateurs LoRA les plus pertinents, les coefficients sont normalisés par softmax et leurs sorties sont combinées linéairement. Cela permet une réutilisation efficace des modules LoRA existants pour de nouvelles tâches et conduit à une généralisation zero-shot plus robuste.
Dans PEFT, Arrow est activé via [`ArrowConfig]andcreate_arrow_model. You can also configure parameters such astop_k(the number of LoRA adapters combined per token),router_temperature(the softmax temperature applied to the routing coefficients), andrng_seed` (for reproducibility).
from peft import create_arrow_model, ArrowConfig
from transformers import AutoModelForCausalLM
# Loading the model
base_model = AutoModelForCausalLM.from_pretrained("microsoft/Phi-3-mini-4k-instruct")
# Creating the Arrow config
arrow_config = ArrowConfig(
top_k=3,
router_temperature=1.0,
rng_seed=42,
)
# The LoRA adapters below were trained on a clustered FLAN dataset.
# Task clustering was performed using the Model-Based Clustering (MBC) method,
# as described in the Arrow paper.
# While one could train a separate LoRA for each task and let Arrow route tokens among them,
# training LoRAs on clusters of tasks instead provides an indirect optimization for
# transfer across the multi-task dataset.
task_specific_adapter_paths = [
f"TahaBa/phi3-mini-clustered-flan/ts_expert_{i}" for i in range(10)
]
# Creating the Arrow model
model = create_arrow_model(
base_model=base_model,
task_specific_adapter_paths=task_specific_adapter_paths,
arrow_config=arrow_config,
)
# Now the forward path could be called on this model, like a normal PeftModel.De plus, vous pouvez ajouter ou supprimer des adaptateurs après avoir appelé create_arrow_model—par exemple, pour affiner un nouvel adaptateur ou en supprimer un inutile. Une fois les adaptateurs en place, vous pouvez activer le "arrow_router" pour l'inférence afin d'utiliser Arrow. Notez que si vous ajoutez un nouvel adaptateur LoRA après create_arrow_model et souhaitez l'affiner, vous devez explicitement définir le nouvel adaptateur comme actif, car "arrow_router" est activé par défaut dans create_arrow_model.
from trl import SFTTrainer, SFTConfig
# Adding a new adapter and activating it
model.add_adapter(adapter_name='new_adapter')
model.set_adapter('new_adapter')
# Now the model could be trained along the `new_adapter`.
trainer = SFTTrainer(
model=model,
args=SFTConfig(...),
...
)
# Once the training is done, you can activate `arrow_router` and use it in inference
model.set_adapter('arrow_router') # Model is ready to be used at inference time nowGenKnowSub
GenKnowSub améliore Arrow en purifiant les adaptateurs LoRA spécifiques à une tâche avant le routage. L’idée clé consiste à soustraire les connaissances générales encodées dans l’espace LoRA — sur la base du principe d’oubli par négation — afin que les adaptateurs de tâche deviennent plus isolés et centrés sur les signaux pertinents pour la tâche. Concrètement, GenKnowSub estime un sous-espace « général » de faible dimension à partir d’un ensemble d’adaptateurs LoRA généraux (non spécifiques à une tâche) et retire ce composant de la mise à jour LoRA de chaque adaptateur de tâche avant le routage par jeton d’Arrow. Cela améliore généralement la compositionnalité et réduit les interférences lors de la combinaison de nombreux adaptateurs de tâche.
Dans PEFT, activez GenKnowSub en définissant use_gks=True dans ArrowConfig et en fournissant general_adapter_paths dans create_arrow_model :
from peft import create_arrow_model, ArrowConfig
from transformers import AutoModelForCausalLM
# Loading the model
base_model = AutoModelForCausalLM.from_pretrained("microsoft/Phi-3-mini-4k-instruct")
# Creating the Arrow config
arrow_config = ArrowConfig(
top_k=3,
router_temperature=1.0,
use_gks=True,
rng_seed=42,
)
# Path to task-specific, trained on flan clustered dataset (as we explained before.)
task_specific_adapter_paths = [
f"TahaBa/phi3-mini-clustered-flan/ts_expert_{i}" for i in range(10)
]
# These general adapters are trained on English, German, and French Wikipedia dataset,
# with causal language modelling objective, each pair like: (507 token tsentence, 5 token completion), and the loss computed on the completion
general_adapter_paths = [
"TahaBa/phi3-mini-general-adapters/cluster0_batch16_prop1.0_langen/checkpoint-17",
"TahaBa/phi3-mini-general-adapters/cluster0_batch16_prop1.0_langfr/checkpoint-35",
"TahaBa/phi3-mini-general-adapters/cluster0_batch16_prop1.0_langger/checkpoint-17"
]
# Creating the Arrow model
model = create_arrow_model(
base_model=base_model,
task_specific_adapter_paths=task_specific_adapter_paths,
general_adapter_paths=general_adapter_paths,
arrow_config=arrow_config,
)
# Now the forward path could be called on this model, like a normal PeftModel.Pour encoder les connaissances générales, GenKnowSub soustrait la moyenne des adaptateurs généraux fournis de chaque adaptateur spécifique à la tâche, une seule fois, avant le début du routage. De plus, la possibilité d'ajouter ou de supprimer des adaptateurs après l'appel de create_arrow_model (comme décrit dans la section Arrow) est toujours prise en charge dans ce cas.
Points à garder en tête lors de l'utilisation d'Arrow + GenKnowSub :
Tous les adaptateurs LoRA (spécifiques à la tâche et généraux) doivent partager le même
ranket les mêmestarget_modules.Toute incohérence dans ces paramètres déclenchera une erreur dans
create_arrow_model.L'utilisation de facteurs d'échelle différents (
lora_alpha) entre les adaptateurs de tâche est prise en charge — Arrow les gère automatiquement.La fusion du
"arrow_router"n'est pas prise en charge, en raison de son comportement de routage dynamique.Dans
create_arrow_model, les adaptateurs de tâche sont chargés sous le nomtask_iet les adaptateurs généraux sous le nomgks_j(oùietjsont des indices). La fonction garantit la cohérence detarget_modules, deranket du fait que les adaptateurs soient appliqués aux couchesLinearouLinear4bit. Elle ajoute ensuite le module"arrow_router"et l’active. Toute personnalisation de ce processus nécessite de redéfinircreate_arrow_model.Cette implémentation est compatible avec la quantification 4 bits (via bitsandbytes) :
from transformers import AutoModelForCausalLM, BitsAndBytesConfig import torch # Quantisation config bnb_config = BitsAndBytesConfig( load_in_4bit=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.bfloat16, bnb_4bit_use_double_quant=False, ) # Loading the model base_model = AutoModelForCausalLM.from_pretrained( "microsoft/Phi-3-mini-4k-instruct", dtype=torch.bfloat16, device_map="auto", quantization_config=bnb_config, ) # Now call create_arrow_model() as we explained before.
API
LoraConfig
classe peft.LoraConfig
< source >( task_type: Optional[Union[str, TaskType]] = Nonepeft_type: Optional[Union[str, PeftType]] = Noneauto_mapping: Optional[dict] = Nonepeft_version: Optional[str] = Nonebase_model_name_or_path: Optional[str] = Nonerevision: Optional[str] = Noneinference_mode: bool = Falser: int = 8target_modules: Optional[Union[list[str], str]] = Noneexclude_modules: Optional[Union[list[str], str]] = Nonelora_alpha: int = 8lora_dropout: float = 0.0fan_in_fan_out: bool = Falsebias: Literal['none', 'all', 'lora_only'] = 'none'use_rslora: bool = Falsemodules_to_save: Optional[list[str]] = Noneinit_lora_weights: bool | Literal['gaussian', 'eva', 'olora', 'pissa', 'pissa_niter_[number of iters]', 'corda', 'astra', 'loftq', 'orthogonal', 'mica', 'nora'] = Truelayers_to_transform: Optional[Union[list[int], int]] = Nonelayers_pattern: Optional[Union[list[str], str]] = Nonerank_pattern: Optional[dict] = <factory>alpha_pattern: Optional[dict] = <factory>megatron_config: Optional[dict] = Nonemegatron_core: Optional[str] = 'megatron.core'trainable_token_indices: Optional[Union[list[int], dict[str, list[int]]]] = Noneloftq_config: Union[LoftQConfig, dict] = <factory>eva_config: Optional[EvaConfig] = Nonecorda_config: Optional[CordaConfig] = Noneastra_config: Optional[AstraConfig] = Nonelora_ga_config: Optional[LoraGAConfig] = Noneuse_dora: bool = Falsevelora_config: Optional[Union[VeloraConfig, dict]] = Nonealora_invocation_tokens: Optional[list[int]] = Noneuse_qalora: bool = Falseqalora_group_size: int = 16monteclora_config: Optional[MontecloraConfig] = Nonelayer_replication: Optional[list[tuple[int, int]]] = Noneruntime_config: LoraRuntimeConfig = <factory>lora_bias: bool = Falsetarget_parameters: Optional[list[str]] = Noneuse_bdlora: Optional[BdLoraConfig] = Nonearrow_config: Optional[ArrowConfig] = Nonekasa_config: Optional[KasaConfig] = Noneensure_weight_tying: bool = False )
Paramètres
- r (
int) — Dimension de l'attention Lora (le « rang »). - target_modules (
Optional[Union[List[str], str]]) — Les noms des modules auxquels appliquer l’adaptateur. Si ce paramètre est spécifié, seuls les modules portant les noms indiqués seront remplacés. En cas de chaîne de caractères, une correspondance par expression régulière est effectuée. En cas de liste de chaînes de caractères, une correspondance exacte est effectuée ou il est vérifié si le nom du module se termine par l’une des chaînes transmises. Si ce paramètre est spécifié comme ‘all-linear’, tous les modules linéaires/Conv1D sont sélectionnés (si le modèle est un PreTrainedModel, la couche de sortie est exclue). Si ce paramètre n’est pas spécifié, les modules sont choisis en fonction de l’architecture du modèle. Si l’architecture est inconnue, une erreur est levée — dans ce cas, vous devez spécifier manuellement les modules cibles. Pour éviter de cibler des modules (parce que vous souhaitez appliquertarget_parameters), définisseztarget_modules=[]. - exclude_modules (
Optional[Union[List[str], str]]) — Les noms des modules auxquels l’adaptateur ne doit pas être appliqué. Si une chaîne de caractères est transmise, une correspondance par expression régulière est effectuée. Si une liste de chaînes de caractères est transmise, une correspondance exacte est effectuée ou il est vérifié si le nom du module se termine par l’une des chaînes transmises. - lora_alpha (
int) — Le paramètre alpha pour le mise à l'échelle de Lora. - lora_dropout (
float) — Probabilité de dropout pour les couches Lora. - fan_in_fan_out (
bool) — Définissez cette valeur sur True si la couche à remplacer stocke les poids sous la forme (fan_in, fan_out). Par exemple, gpt-2 utiliseConv1Dqui stocke les poids sous la forme (fan_in, fan_out) et il convient donc de définir cette valeur surTrue. - bias (
str) — Type de biais pour LoRA. Peut être ‘none’, ‘all’ ou ‘lora_only’. Si ‘all’ ou ‘lora_only’, les biais correspondants seront mis à jour pendant l’entraînement. Notez que cela signifie que, même en désactivant les adaptateurs, le modèle ne produira pas la même sortie que le modèle de base sans adaptation. - use_rslora (
bool) — Lorsqu’il est défini sur True, utilise Rank-Stabilized LoRA qui définit le facteur d’échelle de l’adaptateur surlora_alpha/math.sqrt(r), car cela a été prouvé plus efficace. Sinon, il utilisera la valeur par défaut originale delora_alpha/r. - modules_to_save (
List[str]) — Liste des modules, en dehors des couches d’adaptateur, à définir comme entraînables et à enregistrer dans le point de contrôle final. - init_lora_weights (
bool|Literal["gaussian", "eva", "olora", "pissa", "pissa_niter_[number of iters]", "corda", "astra", "loftq", "orthogonal", "mica", "nora"]) — Méthode d'initialisation des poids des couches d'adaptateur. Passer True (par défaut) entraîne l'initialisation par défaut de l'implémentation de référence de Microsoft, avec le poids LoRA B défini à 0. Cela signifie qu'sans entraînement supplémentaire, l'adaptateur LoRA sera une opération nulle. Définir l'initialisation sur False entraîne une initialisation aléatoire de LoRA A et B, ce qui signifie que LoRA n'est pas une opération nulle avant l'entraînement ; ce paramètre est destiné aux fins de débogage. Passer 'gaussian' entraîne une initialisation gaussienne mise à l'échelle par le rang LoRA pour les couches linéaires et d'incorporation. Passer'loftq'pour utiliser l'initialisation LoftQ. Passer'eva'entraîne une initialisation basée sur les données de Adaptation de la variance expliquée. EVA initialise LoRA sur la base de la SVD des activations d'entrée des couches et atteint des performances SOTA grâce à sa capacité à s'adapter aux données de fine-tuning. Passer'olora'pour utiliser l'initialisation OLoRA. Passer'pissa'entraîne l'initialisation de https://huggingface.co/papers/2404.02948’ Adaptation des valeurs singulières principales et des vecteurs singuliers (PiSSA), qui converge plus rapidement que LoRA et atteint finalement des performances supérieures. De plus, PiSSA réduit l'erreur de quantification par rapport à QLoRA, entraînant des améliorations supplémentaires. Passer 'pissa_niter_[number of iters]' initie l'initialisation PiSSA basée sur Fast-SVD, où [number of iters] indique le nombre d'itérations de sous-espace à effectuer pour FSVD, et doit être un entier non négatif. Lorsque [number of iters] est défini sur 16, il peut terminer l'initialisation d'un modèle 7B en quelques secondes, et l'effet d'entraînement est approximativement équivalent à l'utilisation de SVD. Passer 'corda' entraîne l'initialisation de l'Adaptation par décomposition orientée contexte, qui converge encore plus rapidement que PiSSA en mode Instruction-Previewed, et préserve mieux les connaissances du monde en mode Knowledge-Preserved. Passer 'astra' entraîne l'initialisation de l'Adaptation de rang faible des vecteurs propres de la queue dans l'espace d'activation, qui projette le poids de base sur l'espace propre des activations de sortie estimé à partir d'un petit jeu de données de calibration. Passer "orthogonal" entraîne l'initialisation orthogonale de LoRA A et B ; à cet égard, cela ressemble à "olora", mais les poids de base sont laissés intacts (exige que r soit pair, pris en charge uniquement pour les couches linéaires pour le moment). Passer "mica" entraîne l'initialisation de l'Adaptation des composantes mineures (MiCA), qui initialise B à partir des r vecteurs singuliers gauches du poids de base associés aux plus petites valeurs singulières, définit A à zéro et fige B pendant l'entraînement ; seul A est mis à jour. Actuellement pris en charge pour les couches linéaires et d'incorporation. Passer "nora" entraîne l'initialisation de l'Adaptation de rang faible normalisée (NoRA), qui normalise chaque colonne du poids LoRA A initialisé aléatoirement à une norme L2 unitaire et définit le poids LoRA B à zéro. La normalisation est appliquée uniquement à l'initialisation ; les poids de base ne sont pas modifiés et A et B restent entraînables. Le paramètre recommandé est lora_alpha égal à r avec use_rslora=False, ce qui maintient un ratio de 1:1 et un facteur d'échelle de 1. Actuellement pris en charge pour les couches linéaires et pour les nn.Parameters ciblés avec target_parameters.Valeurs singulières principales et vecteurs singuliers Adaptation (PiSSA), qui converge plus rapidement que LoRA et atteint finalement de meilleures performances. De plus, PiSSA réduit l'erreur de quantification par rapport à QLoRA, entraînant des améliorations supplémentaires. Passer
'pissa_niter_[number of iters]'initie l'initialisation PiSSA basée sur Fast-SVD, où[number of iters]indique le nombre d'itérations de sous-espace à effectuer pour FSVD, et doit être un entier non négatif. Lorsque[number of iters]est défini à 16, il peut compléter l'initialisation d'un modèle 7B en quelques secondes, et l'effet d'entraînement est approximativement équivalent à l'utilisation de SVD. Passer'corda'entraîne l'initialisation de Context-Oriented Decomposition Adaptation, qui converge encore plus rapidement que PiSSA en mode Instruction-Previewed, et préserve mieux les connaissances du monde en mode Knowledge-Preserved. Passer'astra'entraîne l'initialisation de Activation-Space Tail-Eigenvector Low-Rank Adaptation, qui projette le poids de base sur l'espace propre des activations de sortie estimées à partir d'un petit jeu de données de calibration. Passer"orthogonal"entraîne l'initialisation orthogonale de LoRA A et B ; dans ce cas, cela ressemble à"olora", mais les poids de base sont laissés intacts (nécessite quersoit pair, pris en charge uniquement pour les couches linéaires pour le moment). Passer"mica"entraîne l'initialisation de Minor Component Adaptation (MiCA), qui initialise B à partir des r vecteurs singuliers gauches du poids de base associés aux plus petites valeurs singulières, définit A à zéro et fige B pendant l'entraînement ; seul A est mis à jour. Actuellement pris en charge pour les couches linéaires et d'incorporation. Passer"nora"entraîne l'initialisation de Normalized Low-Rank Adaptation (NoRA), qui normalise chaque colonne du poids LoRA A initialisé aléatoirement à une norme L2 unitaire et définit le poids LoRA B à zéro. La normalisation est appliquée uniquement à l'initialisation ; les poids de base ne sont pas modifiés et A et B restent entraînables. Le paramétrage recommandé estlora_alphaégal àravecuse_rslora=False, ce qui maintient un ratio de 1:1 et un facteur d'échelle de 1. Actuellement pris en charge pour les couches linéaires et pour lesnn.Parameters ciblés avectarget_parameters. - layers_to_transform (
Union[List[int], int]) — Les indices de couches à transformer. Si une liste d'entiers est transmise, l'adaptateur sera appliqué aux indices de couches spécifiés dans cette liste. Si un seul entier est transmis, les transformations seront appliquées à la couche située à cet index. - layers_pattern (
Optional[Union[List[str], str]]) — Le nom du motif de couche, utilisé uniquement silayers_to_transformest différent deNone. Il doit cibler lenn.ModuleListdu modèle, souvent appelé'layers'ou'h'. - rank_pattern (
dict) — La correspondance entre les noms de couches ou les expressions régulières et les rangs qui diffèrent du rang par défaut spécifié parr. Par exemple,{'^model.decoder.layers.0.encoder_attn.k_proj': 16}. - alpha_pattern (
dict) — La correspondance entre les noms de couches ou les expressions régulières et les alphas qui diffèrent de l'alpha par défaut spécifié parlora_alpha. Par exemple,{'^model.decoder.layers.0.encoder_attn.k_proj': 16}. - megatron_config (
Optional[dict]) — Les arguments de TransformerConfig pour Megatron. Ils sont utilisés pour créer la couche linéaire parallèle de LoRA. Vous pouvez les obtenir ainsi,core_transformer_config_from_args(get_args()), ces deux fonctions provenant de Megatron. Les arguments seront utilisés pour initialiser le TransformerConfig de Megatron. Vous devez spécifier ce paramètre lorsque vous souhaitez appliquer LoRA aux couches ColumnParallelLinear et RowParallelLinear de megatron. - megatron_core (
Optional[str]) — Le module principal de Megatron à utiliser, par défaut"megatron.core". - trainable_token_indices (
Optional[Union[List[int], dict[str, List[int]]]]) — Permet de spécifier quels indices de jetons affiner sélectivement, sans avoir à réentraîner toute la matrice d’incorporation en utilisant la méthodepeft.TrainableTokensModel. Vous pouvez spécifier les indices de jetons de deux manières. Soit vous fournissez une liste d’indices qui ciblera alors la couche d’incorporation d’entrée du modèle (ou, si elle n’est pas trouvée,embed_tokens). Sinon, vous pouvez fournir un dictionnaire où la clé est le nom du module d’incorporation et les valeurs sont la liste des indices de jetons, par exemple{'embed_tokens': [0, 1, ...]}. Notez que l’entraînement avec FSDP nécessiteuse_orig_params=Truepour éviter les problèmes liés à desrequires_gradnon uniformes. - loftq_config (
Optional[LoftQConfig]) — La configuration de LoftQ. Si cette valeur n’est pas None, LoftQ sera utilisé pour quantiser les poids du backbone et initialiser les couches Lora. Passez égalementinit_lora_weights='loftq'. Notez qu’il ne faut pas passer un modèle quantifié dans ce cas, car LoftQ quantisera lui-même le modèle. LoftQ n’est appliqué qu’aux couches linéaires, y compris celles ciblées viatarget_parameters. Les couches d’embedding et convolutionnelles ne sont pas prises en charge et utilisent à la place l’initialisation LoRA par défaut. Les couchesnn.MultiheadAttentionne sont pas prises en charge et déclenchent une erreur. - eva_config (
Optional[EvaConfig]) — La configuration d’EVA. Au minimum, l’argument dataset doit être défini (utilisez le même jeu de données que pour le finetuning). - corda_config (
Optional[CordaConfig]) — La configuration de CorDA. Si cette valeur n'est pas None, CorDA sera utilisé pour construire les couches d'adaptateur. Pensez également à passerinit_lora_weights='corda'. - astra_config (
Optional[AstraConfig]) — La configuration d’Astra. Si cette valeur n’est pas None, Astra sera utilisé pour construire les couches d’adaptateur. Dans ce cas, passez égalementinit_lora_weights='astra'et appelezpreprocess_astraavantget_peft_model. - lora_ga_config (
Optional[LoraGAConfig]) — La configuration de LoRA-GA. Si ce paramètre est transmis, LoRA-GA sera utilisé pour initialiser les couches d’adaptateur. Dans ce cas, définissez égalementinit_lora_weights='lora_ga'. - use_dora (
bool) — Active la « Weight-Decomposed Low-Rank Adaptation » (DoRA). Cette technique décompose les mises à jour des poids en deux parties, l’amplitude et la direction. La direction est gérée par LoRA standard, tandis que l’amplitude est gérée par un paramètre apprenable distinct. Cela peut améliorer les performances de LoRA, en particulier pour de faibles rangs. Actuellement, DoRA ne prend en charge que les couches linéaires et Conv2D. DoRA introduit un surcoût plus important que LoRA pur, il est donc recommandé de fusionner les poids pour l’inférence. Pour plus d’informations, voir https://huggingface.co/papers/2402.09353. - velora_config (
Optional[VeloraConfig]) — Active VeLoRA en fournissant un VeloraConfig. VeLoRA remplace le passage en arrière (backward pass) personnalisé de la projection A de LoRA par un qui stocke des activations compressées au lieu des activations d’entrée complètes. - kasa_config (
Optional[KasaConfig]) — Activez KaSA (Knowledge-aware Singular-value Adaptation) en fournissant un KasaConfig. KaSA tronque lesrcomposantes singulières les plus petites des poids de base figés via une SVD unique et insère une diagonale apprenable de valeurs singulières entre les facteurs A et B de LoRA. Actuellement, seules les couches linéaires sont prises en charge. - alora_invocation_tokens (
List[int]) — Si la valeur n’est pas None, active ‘Activated LoRA’ (aLoRA), où alora_invocation_tokens correspond à la chaîne d’invocation tokenisée pour l’adaptateur (elle doit être présente dans toutes les chaînes d’entrée du modèle). Cette technique active sélectivement les poids de l’adaptateur uniquement sur les jetons pendant et après alora_invocation_tokens. Lorsqu’elle est utilisée dans un CausalLM, cela signifie que le cache KV antérieur à l’invocation est interchangeable avec celui du modèle de base (et avec celui d’autres adaptateurs aLoRA fonctionnant de la même manière). En conséquence, dans les pipelines d’inférence impliquant des basculements entre l’inférence du modèle de base et l’inférence de l’adaptateur (par exemple, les pipelines agentiques, voir l’article pour des exemples), des économies significatives sont réalisées (par rapport à LoRA) en sauvegardant les opérations de préremplissage. Des accélérations globales de l’inférence de l’adaptateur d’un ordre de grandeur ou plus peuvent se produire sur vLLM, selon la longueur du contexte partagé. Notez que la fusion n’est pas possible en raison de l’application sélective des poids. - use_qalora (
bool) — Actuellement, il n’est implémenté que pour GPTQ. Active Quantization-Aware Low-Rank Adaptation (QALoRA). Cette technique combine l’entraînement conscient de la quantification avec LoRA pour améliorer les performances des modèles quantifiés. Cela peut améliorer les performances de LoRA, en particulier à faible rang. Pour l’instant, QALoRA ne prend en charge que les couches linéaires. - qalora_group_size (
int) — Paramètre de taille de groupe pour le pooling QALoRA, contrôlant le facteur de réduction de dimension. Les dimensions d’entrée sont regroupées en groupes de cette taille, réduisant ainsi le coût de calcul. Des valeurs plus élevées offrent une compression plus importante mais peuvent réduire la qualité du modèle. Ce paramètre détermine le nombre de caractéristiques d’origine qui sont moyennées ensemble pour créer une caractéristique regroupée. Utilisé uniquement lorsqueuse_qalora=True. - monteclora_config (
Optional[MontecloraConfig]) — La configuration de Monteclora (Monte Carlo Low-Rank Adaptation). Si transmise, Monteclora sera utilisée pour ajouter un échantillonnage variationnel de Monte Carlo par-dessus les adaptateurs LoRA. ConsultezMontecloraConfigpour les détails sur les hyperparamètres individuels. - layer_replication (
List[Tuple[int, int]]) — Construit une nouvelle pile de couches en empilant les couches du modèle d'origine selon les plages spécifiées. Cela permet d'élargir (ou de réduire) le modèle sans dupliquer les poids du modèle de base. Les nouvelles couches auront chacune des adaptateurs LoRA distincts qui leur sont attachés. - runtime_config (
LoraRuntimeConfig) — Configurations d'exécution (qui ne sont ni enregistrées ni restaurées). - lora_bias (
bool) — Valeur par défaut :False. Indique s'il faut activer le terme de biais pour le paramètre B de LoRA. En général, il est recommandé de le désactiver. Le principal cas d'usage est lorsque les poids LoRA ont été extraits de paramètres entièrement affinés, afin de prendre en compte le biais de ces paramètres. - target_parameters (
List[str], facultatif) — Liste des noms de paramètres à remplacer par LoRA. Cet argument se comporte de manière similaire àtarget_modules, à l'exception près qu'il faut y passer le nom du paramètre. En général, vous devriez utilisertarget_modulespour cibler le module (par ex.nn.Linear). Cependant, dans certaines circonstances, cela n'est pas possible. Par exemple, dans de nombreuses couches mixture of expert (MoE) de HF Transformers, au lieu d'utilisernn.Linear, unnn.Parameterest utilisé. PEFT remplace normalement la méthodeforwardpour LoRA, mais pournn.Parameter, il n'y en a pas. Par conséquent, pour appliquer LoRA à ce paramètre, il faut le cibler avectarget_parameters. À titre d'exemple, pour Llama4, vous pouvez passer :target_parameters=['feed_forward.experts.gate_up_proj', 'feed_forward.experts.down_proj]. Le passage d'une chaîne de caractères pour une correspondance par expression régulière n'est pas encore implémenté. Notez que lorsque le modèle est compilé et utilisetarget_parameters, une recompilation et/ou des ruptures de graphe sont à prévoir. Il est recommandé de ne pas utiliser les deux en même temps. - use_bdlora (
Optional[BdLoraConfig]) — Activez BD-LoRA (Block-Diagonal LoRA) en fournissant un BdLoraConfig. Cette technique utilise des matrices block-diagonales pour les facteurs LoRA-A ou LoRA-B afin de permettre un service multi-LoRA plus rapide en éliminant les surcoûts de communication dans les environnements distribués. - arrow_config (
Optional[ArrowConfig]) — La configuration nécessaire pour appliquer le routage arrow sur le modèle. - ensure_weight_tying (
bool, facultatif) — Indique s'il faut lier les poids ou non après l'initialisation de peft. Cela garantit que les adaptateurs ajoutés aux couches liées sont également liés. Cela ne s'applique qu'aux couches passées viamodules_to_saveettarget_modules.
Ceci est la classe de configuration destinée à stocker la configuration d'un LoraModel.
Retourne la configuration de votre modèle d'adaptateur sous forme de dictionnaire. Supprime les configurations d'exécution.
LoraModel
classe peft.LoraModel
< source >( modelpeft_config: Union[PeftConfig, dict[str, PeftConfig]]adapter_name: strlow_cpu_mem_usage: bool = Falsestate_dict: Optional[dict[str, torch.Tensor]] = None ) → torch.nn.Module
Paramètres
- model (
torch.nn.Module) — Le modèle à adapter. - config (LoraConfig) — La configuration du modèle Lora.
- adapter_name (
str) — Le nom de l'adaptateur, par défaut"default". - low_cpu_mem_usage (
bool,optional, par défautFalse) — Crée des poids d'adaptateurs vides sur l'appareil méta. Utile pour accélérer le processus de chargement.
Retourne
torch.nn.Module
Le modèle Lora.
Crée un modèle Low Rank Adapter (LoRA) à partir d'un modèle transformers pré-entraîné.
La méthode est décrite en détail dans https://huggingface.co/papers/2106.09685.
Exemple :
>>> from transformers import AutoModelForSeq2SeqLM
>>> from peft import LoraConfig, get_peft_model
>>> config = LoraConfig(
... task_type="SEQ_2_SEQ_LM",
... r=8,
... lora_alpha=32,
... target_modules=["q", "v"],
... lora_dropout=0.01,
... )
>>> model = AutoModelForSeq2SeqLM.from_pretrained("t5-base")
>>> lora_model = get_peft_model(model, config)>>> import torch
>>> import transformers
>>> from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
>>> rank = ...
>>> target_modules = ["q_proj", "k_proj", "v_proj", "out_proj", "fc_in", "fc_out", "wte"]
>>> config = LoraConfig(
... r=4, lora_alpha=16, target_modules=target_modules, lora_dropout=0.1, bias="none", task_type="CAUSAL_LM"
... )
>>> quantization_config = transformers.BitsAndBytesConfig(load_in_8bit=True)
>>> tokenizer = transformers.AutoTokenizer.from_pretrained(
... "kakaobrain/kogpt",
... revision="KoGPT6B-ryan1.5b-float16", # or float32 version: revision=KoGPT6B-ryan1.5b
... bos_token="[BOS]",
... eos_token="[EOS]",
... unk_token="[UNK]",
... pad_token="[PAD]",
... mask_token="[MASK]",
... )
>>> model = transformers.GPTJForCausalLM.from_pretrained(
... "kakaobrain/kogpt",
... revision="KoGPT6B-ryan1.5b-float16", # or float32 version: revision=KoGPT6B-ryan1.5b
... pad_token_id=tokenizer.eos_token_id,
... use_cache=False,
... device_map={"": rank},
... torch_dtype=torch.float16,
... quantization_config=quantization_config,
... )
>>> model = prepare_model_for_kbit_training(model)
>>> lora_model = get_peft_model(model, config)Attributs :
- model (PreTrainedModel) — Le modèle à adapter.
- peft_config (LoraConfig) : La configuration du modèle Lora.
add_weighted_adapter
< source >( adapters: list[str]weights: list[float]adapter_name: strcombination_type: Literal['svd', 'linear', 'cat', 'ties', 'ties_svd', 'dare_ties', 'dare_linear', 'dare_ties_svd', 'dare_linear_svd', 'magnitude_prune', 'magnitude_prune_svd'] = 'svd'svd_rank: int | None = Nonesvd_clamp: int | None = Nonesvd_full_matrices: bool = Truesvd_driver: str | None = Nonedensity: float | None = Nonemajority_sign_method: Literal['total', 'frequency'] = 'total' )
Paramètres
- adapters (
list) — Liste des noms d'adaptateurs à fusionner. - weights (
list) — Liste des poids pour chaque adaptateur. Les poids peuvent être positifs ou négatifs, permettant ainsi l’addition et la soustraction des effets des adaptateurs. - adapter_name (
str) — Nom du nouvel adaptateur. - combination_type (
str) — Le type de fusion peut être l’un des éléments suivants : [svd,linear,cat,ties,ties_svd,dare_ties,dare_linear,dare_ties_svd,dare_linear_svd,magnitude_prune,magnitude_prune_svd]. Lors de l’utilisation ducatcombination_type, le rang de l’adaptateur résultant est égal à la somme des rangs de tous les adaptateurs (l’adaptateur mixte peut être trop volumineux et entraîner des erreurs OOM). Notez quecatetsvdsont des méthodes précises et vous donneront une bonne précision, tandis quelinearest efficace mais constitue une approximation très grossière et doit être évitée si vous en avez la possibilité. - svd_rank (
int, facultatif) — Rang de l’adaptateur de sortie pour svd. Si aucune valeur n’est fournie, le rang maximal des adaptateurs fusionnés est utilisé. - svd_clamp (
float, facultatif) — Seuil de quantile pour l’application d’un clamp à la sortie de la décomposition SVD. Si None est fourni, aucun clamp n’est effectué. Valeur par défaut : None. - svd_full_matrices (
bool, facultatif) — Contrôle le calcul de la SVD complète ou réduite, et par conséquent, la forme des tenseurs U et Vh renvoyés. Valeur par défaut : True. - svd_driver (
str, facultatif) — Nom de la méthode cuSOLVER à utiliser. Cet argument par mot-clé ne fonctionne que lors de la fusion sur CUDA. Peut être l'un des éléments suivants : [None,gesvd,gesvdj,gesvda]. Pour plus d'informations, veuillez consulter la documentation detorch.linalg.svd. La valeur par défaut est None. - density (
float, facultatif) — Valeur comprise entre 0 et 1. 0 signifie que toutes les valeurs sont élaguées et 1 signifie qu’aucune valeur n’est élaguée. Doit être utilisé avec [ties,ties_svd,dare_ties,dare_linear,dare_ties_svd,dare_linear_svd,magnintude_prune,magnitude_prune_svd] - majority_sign_method (
str) — La méthode, qui doit être l’une des valeurs [“total”, “frequency”], à utiliser pour obtenir l’amplitude des valeurs de signe. Doit être utilisée avec [ties,ties_svd,dare_ties,dare_ties_svd]
Cette méthode ajoute un nouvel adaptateur en fusionnant les adaptateurs donnés avec les poids donnés.
Lors de l’utilisation de la combinaison cat, il convient de savoir que le rang de l’adaptateur résultant sera égal à la somme des rangs de tous les adaptateurs. Il est donc possible que l’adaptateur mixte devienne trop volumineux et provoque des erreurs OOM.
subtract_mutated_init
< source >( output_state_dict: dict[str, torch.Tensor]adapter_name: strkwargs = None )
Cette fonction peut calculer les mises à jour de PiSSA/CorDA/OLoRA en comparant les paramètres de l'adaptateur PiSSA/CorDA/OLoRA dans output_state_dict avec les valeurs initiales de PiSSA/CorDA/OLoRA dans adapter_name, convertissant ainsi PiSSA/CorDA/OLoRA en LoRA.
Des méthodes comme PiSSA suppriment une partie des poids de base du modèle pour l’entraînement et ne peuvent donc pas être facilement remplacées. Mais si vous connaissez à la fois les poids de pré-entraînement initiaux et les poids post-entraînement, vous pouvez calculer le \Delta W et l’utiliser comme adaptateur LoRA.
Étapes de calcul :
- (init PiSSA)
- (PiSSA après l'entraînement)
- (Dérivation de dW pour LoRA)
Utilitaire
ArrowConfig
classe peft.ArrowConfig
< source >( top_k: int = 3router_temperature: float = 1.0use_gks: bool = Falserng_seed: Optional[int] = None )
Ceci est la classe de sous-configuration destinée à stocker la configuration pour les algorithmes Arrow et GenKnowSub. Arrow est un algorithme de routage permettant de combiner les modules LoRA entraînés afin de résoudre de nouvelles tâches, proposé dans ’https://huggingface.co/papers/2405.11157’. GenKnowSub est une amélioration des modules entraînés avant leur combinaison via Arrow, introduite dans ’https://aclanthology.org/2025.acl-short.54/’
LoftQ
replace_lora_weights_loftq
peft.replace_lora_weights_loftq
< source >( peft_modelmodel_path: Optional[str] = Noneadapter_name: str = 'default'callback: Optional[Callable[[torch.nn.Module, str], bool]] = None )
Paramètres
- peft_model (
PeftModel) — Le modèle dont les poids doivent être remplacés. Doit être un modèle PEFT quantifié avec des couches LoRA. - model_path (
Optional[str]) — Le chemin vers le fichier safetensors du modèle. Si le modèle est un modèle Hugging Face, ce chemin est déduit de la configuration du modèle. Sinon, il doit être fourni. - adapter_name (
str) — Le nom de l'adaptateur dont les poids doivent être remplacés. Le nom d'adaptateur par défaut est « default ». - callback (
Optional[Callable[[PeftModel, str], bool]]) — Fonction de rappel appelée après le remplacement de chaque module. La fonction de rappel doit prendre en entrée le modèle et le nom du module actuel, et renvoyer un booléen indiquant si le remplacement doit être conservé. Si le rappel renvoie False, le remplacement est annulé. Cela peut être très utile pour confirmer que l’initialisation LoftQ réduit effectivement l’erreur de quantification du modèle. À titre d’exemple, ce rappel pourrait générer des logits pour une entrée donnée et les comparer aux logits du modèle original, non quantisé, avec la même entrée, et ne renvoyerTrueque s’il y a une amélioration. Comme il s’agit d’une optimisation gloutonne, il est possible que l’appel de cette fonction à plusieurs reprises produise des améliorations incrémentales.
Remplacez les poids LoRA d'un modèle quantisé avec bitsandbytes, en utilisant la technique LoftQ.
Le remplacement est effectué à la volée en chargeant les poids non quantifiés depuis un fichier de modèle safetensors stocké localement et en initialisant les poids LoRA de manière à minimiser l'erreur de quantisation entre les poids d'origine et les poids quantifiés.
Le chargement paresseux n’étant pas possible avec pickle, les fichiers de points de contrôle PyTorch standards ne peuvent pas être pris en charge.
Selon la taille du modèle, l'appel de cette fonction peut prendre un certain temps à s'achever.
LoftQConfig
classe peft.LoftQConfig
< source >( loftq_bits: int = 4loftq_iter: int = 1 )
Ceci est la classe de sous-configuration destinée à stocker la configuration d'un LoraModel.
Eva
EvaConfig
classe peft.EvaConfig
< source >( rho: float = 2.0tau: float = 0.99use_label_mask: bool = Truelabel_mask_value: int = -100whiten: bool = Falseadjust_scaling_factors: bool = True )
Paramètres
- rho (
float) — Valeur de Rho pour la redistribution EVA (>= 1.0). Le rang maximal pour une couche est lora_r * rho. La valeur par défaut est 2.0, ce qui signifie que le rang maximal autorisé pour une couche est 2r. Augmenter rho permet un degré plus élevé de redistribution des rangs entre les couches. Certains modèles pré-entraînés peuvent être plus sensibles à une redistribution des rangs. Il peut donc être bénéfique d’essayer rho=1.0 (aucune redistribution) si les performances sont inférieures aux attentes. - tau (
float) — Seuil de similarité cosinus pour l'arrêt anticipé. Compare la similarité cosinus des vecteurs singuliers droits entre deux étapes SVD consécutives. Si la similarité cosinus est supérieure à ce seuil, l'itération SVD est arrêtée. La valeur par défaut est 0.99. - use_label_mask (
bool) — Utilise un masque de labels pour l’initialisation EVA. Cela signifie que les positions où labels=label_mask_value sont ignorées pour le calcul de la SVD. Il est préférable de définir use_label_mask=True dans la plupart des cas, ce qui peut être particulièrement bénéfique pour les conversations multi-tours. La valeur par défaut est True. Filtrer les éléments en fonction du masque de labels peut parfois entraîner une taille de lot réduite et, par conséquent, des instabilités dans le calcul de la SVD. Dans les cas où une grande proportion des éléments du lot serait filtrée, définissez use_label_mask=False. - label_mask_value (
int) — Si use_label_mask=True, la valeur à rechercher pour masquer les jetons ignorés. La valeur par défaut est -100. - whiten (
bool) — Applique un blanchiment aux vecteurs singuliers. La valeur par défaut est False. Le blanchiment s'est avéré bénéfique pour EVA dans le domaine de la vision. - adjust_scaling_factors (
bool) — Ajuste les facteurs d’échelle de LoRA après la redistribution des rangs. Définir cette valeur sur True signifie que les facteurs d’échelle sont ajustés de sorte que tous les gradients LoRA aient la même échelle, quel que soit leur rang. La valeur par défaut est True.
Ceci est la classe de sous-configuration destinée à stocker la configuration d'une initialisation basée sur les données via EVA. EVA a été introduit dans Explained Variance Adaptation.
initialize_lora_eva_weights
peft.initialize_lora_eva_weights
< source >( model: Moduledataloader: collections.abc.Iterable | None = Noneeva_state_dict: dict | None = Noneforward_fn: collections.abc.Callable | None = <function forward_fn_dict at 0x7f240ea44e00>prepare_model_inputs_fn: collections.abc.Callable | None = <function prepare_model_inputs_fn_language_modeling at 0x7f240ea44bf0>prepare_layer_inputs_fn: collections.abc.Callable | dict[str, collections.abc.Callable] | None = <function prepare_layer_inputs_fn_language_modeling at 0x7f240ea44d50>adapter_name: str = 'default'gather_distributed_inputs: bool = Trueshow_progress_bar: bool = True ) → model (torch.nn.Module)
Paramètres
- model (PeftModel) — Le modèle peft pour lequel calculer la SVD.
- dataloader (Optional[Iterable]) — Le dataloader à utiliser pour le passage avant. Si None, il est nécessaire de fournir eva_state_dict.
- eva_state_dict (Optional[dict]) —
Le state_dict à charger dans le modèle. Si None, un dataloader doit être fourni et le state_dict sera
calculé à l'aide de
get_eva_state_dict. - forward_fn (Callable) —
La fonction de propagation avant à utiliser pour la passe avant. Prend deux arguments :
modeletinputs. Le comportement par défaut estreturn model(**inputs) - prepare_model_inputs_fn (Optional[Callable]) —
Cette fonction reçoit les entrées du modèle et le peft_config et transmet la sortie à
prepare_layer_inputs_fn. Elle peut être utilisée pour modifier l’entrée du calcul SVD en fonction des entrées d’origine du modèle. Par exemple, pour le modélisation de langage, le masque d’attention est utilisé pour déterminer quels indices correspondent à des jetons de bourrage et ne doivent pas être utilisés pour le SVD. Toute fonction définie ici s’attend à deux arguments :model_inputetpeft_config.peft.tuners.lora.eva.prepare_model_inputs_fn_language_modelingest utilisé par défaut. - prepare_layer_inputs_fn (Union[Callable, Dict[str, Callable], None]) —
Cette fonction reçoit les entrées de la couche, les entrées du modèle (éventuellement modifiées par
prepare_model_inputs_fn) et le nom de la couche, et renvoie les entrées qui doivent être utilisées pour la SVD pour cette couche en particulier. Toute fonction personnalisée définie ici s’attend à trois arguments :layer_input,model_inputetlayer_name, et doit renvoyer un tenseur 2D. La logique par défaut se trouve dans peft.tuners.lora.eva.prepare_layer_inputs_fn_language_modeling et fonctionne pour le language modeling. Dans ce cas, model_inputs est le masque utilisé pour déterminer quels indices doivent être utilisés pour la SVD (créé parprepare_model_inputs_fn_language_modeling). - adapter_name (str) — Le nom de l'adaptateur pour lequel initialiser les poids.
- gather_distributed_inputs (bool) — Indique s'il faut collecter les entrées de la couche à partir de tous les rangs. La valeur par défaut est True, ce qui signifie qu'en environnement distribué, les entrées de la couche sont collectées à partir de tous les rangs pour le calcul de la SVD. Dans un environnement non distribué, cet argument est ignoré. Définissez-le sur False si vous utilisez un chargeur de données non distribué dans un environnement distribué.
- show_progress_bar (bool) — Indique s'il faut afficher une barre de progression. La valeur par défaut est True.
Retourne
model (torch.nn.Module)
Le modèle avec les poids LoRA initialisés.
Initialiser les poids des couches LoRA à l'aide de la méthode EVA.
Cette fonction initialise les poids des couches LoRA à l'aide de la méthode EVA. Elle calcule la SVD pour chaque couche d'adaptateur et met à jour les poids en conséquence.
get_eva_state_dict
peft.get_eva_state_dict
< source >( model: Moduledataloader: Iterablepeft_config: peft.tuners.lora.config.LoraConfig | None = Noneforward_fn: collections.abc.Callable | None = <function forward_fn_dict at 0x7f240ea44e00>prepare_model_inputs_fn: collections.abc.Callable | None = <function prepare_model_inputs_fn_language_modeling at 0x7f240ea44bf0>prepare_layer_inputs_fn: collections.abc.Callable | dict[str, collections.abc.Callable] | None = <function prepare_layer_inputs_fn_language_modeling at 0x7f240ea44d50>adapter_name: str = 'default'gather_distributed_inputs: bool = Trueshow_progress_bar: bool = True ) → eva_state_dict (dict)
Paramètres
- model (torch.nn.Module) — Le modèle pour lequel calculer la SVD. Il n'a pas besoin d'être un PeftModel.
- dataloader (Iterable) — Le dataloader à utiliser pour le passage avant.
- peft_config (Optional[LoraConfig]) —
La configuration des couches LoRA. Uniquement requise si
modeln’est pas un PeftModel. - forward_fn (Callable) —
La fonction de propagation avant à utiliser pour la passe avant. Prend deux arguments :
modeletinputs. Le comportement par défaut estreturn model(**inputs) - prepare_model_inputs_fn (Optional[Callable]) —
Cette fonction reçoit les entrées du modèle et le peft_config et transmet la sortie à
prepare_layer_inputs_fn. Elle peut être utilisée pour modifier l’entrée du calcul SVD en fonction des entrées d’origine du modèle. Par exemple, pour le modélisation de langage, le masque d’attention est utilisé pour déterminer quels indices correspondent à des jetons de bourrage et ne doivent pas être utilisés pour le SVD. Toute fonction définie ici s’attend à deux arguments :model_inputetpeft_config.peft.tuners.lora.eva.prepare_model_inputs_fn_language_modelingest utilisé par défaut. - prepare_layer_inputs_fn (Union[Callable, Dict[str, Callable], None]) —
Cette fonction reçoit les entrées de la couche, les entrées du modèle (éventuellement modifiées par
prepare_model_inputs_fn) et le nom de la couche, et renvoie les entrées qui doivent être utilisées pour la SVD pour cette couche en particulier. Toute fonction personnalisée définie ici s’attend à trois arguments :layer_input,model_inputetlayer_name, et doit renvoyer un tenseur 2D. La logique par défaut se trouve dans peft.tuners.lora.eva.prepare_layer_inputs_fn_language_modeling et fonctionne pour le language modeling. Dans ce cas, model_inputs est le masque utilisé pour déterminer quels indices doivent être utilisés pour la SVD (créé parprepare_model_inputs_fn_language_modeling). - adapter_name (str) — Le nom de l'adaptateur pour lequel calculer la SVD.
- gather_distributed_inputs (bool) — Indique s'il faut collecter les entrées de la couche à partir de tous les rangs. La valeur par défaut est True, ce qui signifie qu'en environnement distribué, les entrées de la couche sont collectées à partir de tous les rangs pour le calcul de la SVD. Dans un environnement non distribué, cet argument est ignoré. Définissez-le sur False si vous utilisez un chargeur de données non distribué dans un environnement distribué.
- show_progress_bar (bool) — Indique s'il faut afficher une barre de progression. La valeur par défaut est True.
Retourne
eva_state_dict (dict)
Le dictionnaire d'état contenant les composantes SVD pour chaque couche.
Calculer la SVD pour chaque couche du modèle.
Cette fonction calcule la décomposition en valeurs singulières (SVD) pour chaque couche du modèle. Elle utilise la méthode de PCA incrémentale pour calculer les composantes de la SVD. La fonction vérifie également la convergence des composantes calculées à l'aide de la similarité cosinus. La distribution des rangs pour chaque couche est déterminée en fonction du ratio de variance expliquée.
LoraGAConfig
classe peft.LoraGAConfig
< source >( direction: Literal['ArBr', 'A2rBr', 'ArB2r', 'random'] = 'ArB2r'scale: Literal['stable', 'weight_svd', 'gd_scale', 'unit'] = 'stable'stable_gamma: int = 16 )
Paramètres
- direction (
Literal["ArBr", "A2rBr", "ArB2r", "random"]) — Stratégie de distribution des composantes SVD des gradients aux matrices lora_A et lora_B.- « ArBr » : indices alternés (A prend les impairs, B prend les pairs)
- “A2rBr” : A prend les indices [r:2r], B prend les indices [:r]
- « ArB2r » : A prend les indices [:r], B prend les indices [r:2r] (recommandé)
- “random” : sélection aléatoire des indices Valeur par défaut : “ArB2r”
- scale (
Literal["stable", "weight_svd", "gd_scale", "unit"]) — Stratégie de mise à l'échelle pour l'initialisation de l'adaptateur.- “stable” : mise à l'échelle stable avec le paramètre gamma
- “weight_svd” : Échelle basée sur les valeurs singulières de la matrice de poids
- “gd_scale” : mise à l'échelle basée sur la descente de gradient
- “unit” : aucune mise à l'échelle supplémentaire Valeur par défaut : “stable”
- stable_gamma (
int) — Paramètre gamma pour la méthode de mise à l'échelle stable. Valeur par défaut : 16
Ceci est la classe de sous-configuration destinée à stocker la configuration pour l'initialisation LoRA-GA.
LoRA-GA (Low-Rank Adaptation with Gradient Approximation) utilise des informations de gradient lors de l'initialisation pour atteindre une convergence plus rapide (accélération de 2 à 4 fois) en alignant les poids initiaux de l'adaptateur avec la direction des gradients de l'ajustement fin complet.
Référence : https://arxiv.org/abs/2407.05000
Utilitaires
peft.tuners.lora.loraga.estimate_gradients
< source >( model: Modulelora_config: LoraConfigtrain_step: Callable )
Estimer les gradients pour l’initialisation de LoRA-GA.
Cette fonction permet le calcul des gradients UNIQUEMENT sur les poids du module cible et exécute le rappel train_step. C'est plus efficace en mémoire que d'activer les gradients globalement.
peft.preprocess_loraga
< source >( model: Modulelora_config: LoraConfigtrain_step: Callablecache_file: str | None = None )
Paramètres
- model (
nn.Module) — Modèle à prétraiter. - lora_config (
LoraConfig) — Configuration Lora du modèle.lora_config.lora_ga_configdoit être défini. - train_step (
Callable[[], None]) — Callback pour exécuter l'estimation des gradients. Vous devez généralement exécuter les passes avant et arrière du modèle dans ce callback. Les gradients seront accumulés au fil de tous les appels effectués dans ce callback. - cache_file (
Optional[str]) — Chemin facultatif vers un fichier de cache pour enregistrer/charger les gradients. S'il est fourni et que le fichier existe, les gradients seront chargés depuis le cache. Sinon, les gradients seront estimés et enregistrés à cet emplacement.
Construire les champs LoRA-GA nécessaires pour un modèle en estimant les gradients.
Pour chaque couche linéaire, les gradients seront estimés en exécutant le rappel train_step fourni. Ces gradients sont ensuite attachés aux modules et utilisés lors de l’initialisation.
Une fois terminé, les champs suivants sont définis pour chaque module cible :
_peft_loraga_grad (torch.Tensor) :
Gradient accumulé pour la matrice de poids.
Réduction de dimensionnalité de l'intrus
peft.tuners.lora.intruders.reduce_intruder_dimension
< source >( peft_modelold_adapter_name = 'default'new_adapter_name = 'intruder_reduced'top_k = 10threshold_epsilon = 0.5mitigation_lambda = 0.75logging_sink = <built-in function print> )
Paramètres
- peft_model —
Le modèle PEFT avec un adaptateur LoRA chargé portant le nom fourni dans
old_adapter_name. Les modèles mixtes ne sont pas pris en charge pour le moment. - top_k (par défaut — 10) Prend en compte les k premières dimensions pour la détection d’intrus. Plus la valeur est élevée, plus le nombre de dimensions considérées pour l’analyse de détection d’intrus est important (et plus il peut y avoir de faux positifs). Opère sur la similarité cosinus entre les poids de base et les poids de l’adaptateur, triés approximativement par influence de la dimension (déterminée par la décomposition en valeurs singulières), de sorte qu’un top-k de 10 examinera les 10 dimensions les plus « importantes ».
- threshold_epsilon (par défaut — 0.5) Seuil à partir duquel une similarité cosinus entre le poids de base et le poids de l’adaptateur est considérée comme un intrus. Selon l’article, les dimensions intruses présentent une similarité cosinus absolue proche de zéro avec les vecteurs singuliers pré-entraînés. Plus cette valeur est basse, moins de dimensions potentiellement intruses sont identifiées. Plus elle est élevée, plus de faux positifs potentiels sont considérés comme des intrus.
- mitigation_lambda (par défaut — 0.75) La portion relative des dimensions de l’intrus qui est soustraite du delta de poids de l’adaptateur. Plus la valeur est élevée, plus une grande partie de la dimension de l’intrus est soustraite, mais plus d’informations sont perdues. Reportez-vous à la figure 8 de l’article pour une analyse des compromis.
- logging_sink (par défaut — print) Fonction qui affiche des informations sur le processus d’atténuation. Définissez-la sur None si vous ne souhaitez aucune sortie.
Atténuation de la dimension de l’intrus basée sur https://huggingface.co/papers/2410.21228 (« LoRA vs Full Fine-tuning: An Illusion of Equivalence »).
Cette méthode peut récupérer des connaissances antérieures (c’est-à-dire atténuer l’oubli) en post-traitant des adaptateurs de faible rang déjà entraînés. Cela se fait au détriment de la précision de la tâche – la valeur de migration_lambda peut être ajustée pour arbitrer entre ces deux facteurs.
Une fois l’atténuation effectuée, un nouvel adaptateur portant le nom défini dans new_adapter_name sera créé et deviendra l’adaptateur actif. L’inférence sur le modèle atténué utilisera donc l’adaptateur modifié. Pour revenir à l’adaptateur d’origine, vous pouvez utiliser peft_model.set_adapter(<old_adapter_name>).
Actuellement, seul LoRA est pris en charge, car il n'est pas clair si cette méthode se généralise à d'autres méthodes de poids delta.