Module 5 — LoRA : adaptateurs de rang faible
Le module 4 a placé LoRA dans la famille PEFT. Ce module l'ouvre pour de bon : d'où vient l'astuce mathématique, ce que rang et alpha règlent vraiment, quels modules cibler dans un Transformer, combien de paramètres cela entraîne au juste, et comment un seul modèle de base porte plusieurs adaptateurs échangés à la volée.
L'observation fondatrice
Un modèle pré-entraîné contient des matrices de poids massives — pour Mistral 7B, chaque matrice de projection dans l'attention fait 4 096 par 4 096, soit près de 17 millions de valeurs. L'affinage complet met à jour ces matrices entièrement. Or, en 2021, Hu et coll. ont observé quelque chose de fort : la différence entre les poids avant et après affinage, notée , est de rang effectif très faible — souvent 8, 16 ou 32 pour un vecteur de dimension 4 096.
Traduit : sur 4 096 directions possibles dans l'espace des poids, l'affinage n'en pousse vraiment que quelques dizaines. Le reste est du bruit numérique. Si est de rang faible, on peut le représenter par un produit de deux petites matrices au lieu d'une grande.
où est de taille et de taille , avec le rang choisi, très petit devant . La matrice de poids modifiée devient :
Pendant l'entraînement, est gelé. Seules et apprennent, initialisées de sorte que vaille zéro au départ — ce qui garantit qu'on part exactement du modèle de base. À l'inférence, on peut soit calculer les deux termes séparément, soit fusionner dans pour effacer tout surcoût (module 9).
Le rang et le facteur
Deux réglages pilotent LoRA. Ils sont souvent confondus, à tort.
Le rang contrôle la capacité de l'adaptateur : combien de directions il peut pousser. Les valeurs typiques vont de 4 à 64, avec 16 ou 32 pour la plupart des tâches. Plus est grand, plus l'adaptateur peut apprendre, mais plus il compte de paramètres et plus il risque le surapprentissage sur un petit jeu. Pour nos deux mille paires transcription vers compte rendu, est un choix raisonnable ; sur un jeu de cinquante mille exemples, ou peut aider.
Le facteur alpha contrôle l'échelle de la contribution de l'adaptateur. Le rapport apparaît devant le produit : c'est un gain scalaire. La convention courante est , ce qui donne un rapport . Ce rapport de 2 fait que l'adaptateur pèse deux fois plus que le modèle de base sur les directions qu'il apprend, ce qui est empiriquement un bon point de départ.
| Rang | Facteur | Rapport | Contexte typique |
|---|---|---|---|
| 8 | 16 | 2 | tâches simples, petits jeux |
| 16 | 32 | 2 | valeur par défaut, nos comptes rendus |
| 32 | 64 | 2 | jeux plus grands, tâches plus difficiles |
| 32 | 16 | 0,5 | adaptateur volontairement discret |
| 8 | 32 | 4 | adaptateur agressif, à surveiller |
Une erreur classique consiste à multiplier par deux et par deux en pensant simplement doubler la capacité — cela laisse le rapport inchangé et n'apporte souvent presque rien. Modifier à fixé change le comportement plus qu'on ne le croit.
Où placer les adaptateurs
Toutes les matrices d'un Transformer ne se valent pas pour LoRA. Le mécanisme d'attention en compte plusieurs — projections q, k, v et o — et chaque couche MLP en ajoute deux ou trois (gate, up, down). Cibler tout casse le budget mémoire, ne rien cibler ne fait rien.
Trois configurations reviennent en pratique :
- Minimale —
q_projetv_projseulement. C'est la configuration originale du papier LoRA. Économe, souvent suffisante pour des tâches proches du pré-entraînement. - Étendue — les quatre projections d'attention (
q,k,v,o). Bon compromis pour la plupart des cas, dont notre fil rouge. C'est celle qu'on utilise dans le module 4. - Complète — attention plus les projections MLP (
gate,up,down). Coûteuse, réservée aux jeux plus difficiles où l'attention seule ne suffit pas à apprendre le format visé.
config_etendue = LoraConfig(
r=16, lora_alpha=32,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
lora_dropout=0.05, bias="none", task_type="CAUSAL_LM",
)
config_complete = LoraConfig(
r=16, lora_alpha=32,
target_modules=[
"q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj",
],
lora_dropout=0.05, bias="none", task_type="CAUSAL_LM",
)
Une bonne pratique consiste à commencer par la configuration étendue, mesurer la qualité, et ne passer à la configuration complète que si un plafond visible apparaît sur les courbes de validation.
Combien de paramètres au juste
Un calcul rapide clarifie l'intérêt. Pour Mistral 7B, avec et la configuration étendue sur 32 couches d'attention :
- chaque projection est ;
- son adaptateur LoRA fait valeurs ;
- quatre projections par couche, 32 couches : millions de valeurs.
Seize millions de paramètres à entraîner, contre 7,25 milliards pour l'affinage complet — le rapport est de 430. Les états d'optimiseur suivent la même échelle : au lieu de 56 gigaoctets pour Adam, on est à environ 130 mégaoctets. C'est cette bascule d'ordre de grandeur qui rend l'affinage praticable sur un GPU modeste, avant même de parler de QLoRA au module suivant.
Un modèle, plusieurs adaptateurs
Un adaptateur LoRA se sauvegarde dans un fichier de quelques dizaines à quelques centaines de mégaoctets. Cette légèreté permet un mode de déploiement radicalement différent du modèle affiné classique : on garde un seul modèle de base en mémoire et l'on attache l'adaptateur voulu à chaque requête.
from peft import PeftModel
# Modele de base charge une seule fois.
base = AutoModelForCausalLM.from_pretrained(
"mistralai/Mistral-7B-Instruct-v0.3", torch_dtype="bfloat16", device_map="auto",
)
# Adaptateur specifique a la tache.
modele = PeftModel.from_pretrained(base, "notre-org/mistral-comptes-rendus")
# Passage a un autre adaptateur sans recharger le modele de base.
modele.load_adapter("notre-org/mistral-mails-clients", adapter_name="mails")
modele.set_adapter("mails")
Trois adaptateurs de cent mégaoctets chacun tiennent dans l'espace d'un modèle de 14 gigaoctets. Pour une entreprise qui décline le même modèle sur plusieurs tâches, l'économie de mémoire GPU est massive.
Rang : combien de directions le modèle peut apprendre. Facteur : combien fort ces directions parlent. Un rang trop faible bride, un rang trop élevé bruite ; un trop faible passe inaperçu, un trop élevé écrase le modèle de base. Le rapport est le bon défaut.
En résumé
- La mise à jour induite par l'affinage est de rang effectif faible ; LoRA la représente par un produit de deux petites matrices.
- Le rang règle la capacité (typiquement 8 à 32), le facteur règle l'échelle (convention ).
- Cibler les projections d'attention couvre la plupart des cas ; étendre aux projections MLP ne se fait qu'en cas de plafond mesuré.
- Le nombre de paramètres entraînés est de l'ordre de quelques millions ; un même modèle de base peut porter plusieurs adaptateurs.
Module suivant : QLoRA, la variante quantifiée qui fait tenir tout cela sur un seul GPU de 24 Go.