Aller au contenu
The loss curve

Guide

Le fine-tuning LoRA, construit depuis zéro

Implémente Low-Rank Adaptation (LoRA) depuis zéro et fine-tune un modèle GPT-2. Les maths, le code et un exemple PyTorch qui fonctionne.

LoRA (Low-Rank Adaptation) est la méthode de fine-tuning économe en paramètres la plus utilisée en pratique. Elle est aussi assez petite pour être implémentée depuis zéro en une trentaine de lignes. Ce guide déroule les maths, le code, et les cas où s'en servir.

1. Le problème du fine-tuning complet

Le fine-tuning complet met à jour chaque poids du modèle. Pour GPT-2 small, ça fait 124 millions de paramètres entraînables ; pour un modèle de 70 milliards, c'est intenable sans infrastructure sérieuse. Pire : tu te retrouves avec un checkpoint complet distinct pour chaque variante fine-tunée.

Le pari de LoRA : le fine-tuning a rarement besoin de toute la capacité du modèle. La mise à jour — la différence entre les poids de base et les poids fine-tunés — est généralement de rang faible.

2. Les maths

Pour une matrice de poids gelée W ∈ ℝ^(d × k), LoRA introduit deux petites matrices :

A ∈ ℝ^(r × k)
B ∈ ℝ^(d × r)

r << min(d, k). La passe avant fine-tunée devient :

y = (W + B·A) · x

W est gelée : seules A et B sont entraînées. À l'inférence, tu peux soit les garder séparées (pratique pour permuter des adaptateurs), soit les fusionner : W' = W + B·A.

Le nombre de paramètres passe de d·k (fine-tuning complet) à r·(d + k) (LoRA). Pour un r = 8 typique, c'est une réduction de plus de 100 fois.

3. L'implémenter

En PyTorch, LoRA n'est qu'une enveloppe autour de nn.Linear :

class LoRALinear(nn.Module):
    def __init__(self, base_linear, r=8, alpha=16):
        super().__init__()
        self.base = base_linear            # gelée
        d, k = base_linear.out_features, base_linear.in_features
        self.A = nn.Parameter(torch.zeros(r, k))
        self.B = nn.Parameter(torch.zeros(d, r))
        nn.init.kaiming_uniform_(self.A)
        # B part de zéro pour que la sortie initiale soit celle de la base
        self.scale = alpha / r
 
        for p in self.base.parameters():
            p.requires_grad = False
 
    def forward(self, x):
        return self.base(x) + self.scale * (x @ self.A.t() @ self.B.t())

C'est tout. Le chapitre 18 — fine-tuning avec LoRA le construit progressivement, avec un vrai fine-tuning sur GPT-2 small.

4. Quelles couches adapter ?

En pratique, LoRA s'applique aux projections d'attention (W_Q, W_V, parfois W_K et W_O). Les couches feed-forward sont parfois incluses pour les tâches plus dures.

Empiriquement, LoRA sur l'attention suffit pour l'instruction tuning et la plupart des adaptations de domaine. Ajouter le FFN double le nombre de paramètres pour un gain marginal sur les tâches courantes.

5. Sauvegarder et charger des adaptateurs

Un LoRA entraîné produit un petit fichier : pour GPT-2 small en rang 8, l'adaptateur pèse quelques centaines de kilooctets. Tu peux livrer beaucoup d'adaptateurs à côté d'un seul modèle de base :

gpt2-small.bin              # 500 Mo de base, partagée
lora-finance.pt             # 200 Ko d'adaptateur
lora-medical.pt             # 200 Ko d'adaptateur
lora-support-client.pt      # 200 Ko d'adaptateur

À l'exécution, tu charges la base une fois et tu permutes les adaptateurs pour presque rien.

6. Où ça s'inscrit dans le cours

7. Aller plus loin

Après le LoRA de base, tu croiseras ses variantes :

  • QLoRA : LoRA par-dessus une base quantifiée en 4 bits. Permet de fine-tuner de gros modèles sur des GPU grand public.
  • DoRA : décompose la mise à jour LoRA en magnitude et direction.
  • Programmations de rang : faire varier r au cours de l'entraînement.

La recette LoRA de base est le socle de toutes.

Questions fréquentes

Qu'est-ce que LoRA ?

Low-Rank Adaptation. Au lieu de mettre à jour tous les poids pendant le fine-tuning, on ajoute deux petites matrices qui approximent la mise à jour — beaucoup moins de paramètres entraînables, pour presque le même résultat. Le modèle de base reste gelé.

Comment LoRA fonctionne-t-il concrètement ?

Pour chaque matrice de poids W que tu veux fine-tuner, tu ajoutes un delta de rang faible : deux petites matrices A et B telles que ΔW = A · B. Pendant la passe avant, la couche calcule (W + A·B) · x. Seules A et B sont entraînées ; W est gelée.

Combien de paramètres LoRA ajoute-t-il ?

Pour un LoRA de rang 8 sur GPT-2 small (124 millions de paramètres), environ 100 000 à 300 000 paramètres entraînables, contre 124 millions pour un fine-tuning complet — soit 0,1 à 0,2 %. L'économie devient encore plus spectaculaire sur les gros modèles.

Pourquoi le rang est-il important ?

Le rang r contrôle la capacité de l'adaptateur. Trop bas, le fine-tuning sous-apprend. Trop haut, tu perds l'économie de paramètres. Les valeurs courantes sont 4, 8 et 16. Les rangs élevés aident sur les tâches les plus exigeantes.

Quand préférer LoRA à un fine-tuning complet ?

Quand tu veux spécialiser un modèle de base sans payer un réentraînement complet : quand la base est grosse, quand tu auras beaucoup d'adaptateurs différents, ou quand la VRAM est le facteur limitant. Pour un petit modèle sur un petit jeu de données, le fine-tuning complet fait souvent aussi bien.