Chapitre 1 : Comprendre LoRA
L'intuition derrière l'adaptation de faible rang
LoRA (Low-Rank Adaptation) est une technique élégante qui repose sur une observation simple mais puissante : nous n'avons pas besoin de mettre à jour tous les paramètres pour adapter un modèle à de nouvelles tâches.
L'idée principale
Observation clé
Lors de l'ajustement de modèles pré-entraînés, les mises à jour de poids ont souvent un faible "rang intrinsèque"—ce qui signifie que les changements peuvent être capturés par un ensemble de paramètres beaucoup plus petit.
Analogie
Pensez-y comme à la modification d'un document :
- Ajustement Complet : Réécrire tout le document
- LoRA : Ajouter des notes autocollantes avec des corrections
Le document original (modèle de base) reste intact, et les notes autocollantes (adaptateurs LoRA) capturent les changements.
Comment fonctionne LoRA
Ajustement Traditionnel
Dans une couche de réseau neuronal :
h = Wx
Où W est une matrice de poids de taille (d × d).
L'ajustement complet met à jour W directement :
W' = W + ΔW
Où ΔW est le changement appris pour tous les paramètres (d × d).
Approche de LoRA
LoRA représente ΔW comme un produit de deux matrices plus petites :
ΔW = BA
Où :
- B a une taille (d × r)
- A a une taille (r × d)
- r est beaucoup plus petit que d (r ≪ d)
Le passage avant devient :
h = Wx + BAx = (W + BA)x
Représentation Visuelle
Économies de Paramètres
Exemple : Une Matrice de Poids 4096 × 4096
# Matrice de poids originale
d = 4096
original_params = d * d # 16,777,216 paramètres
# LoRA avec rang 16
r = 16
lora_params = d * r + r * d # 131,072 paramètres
# Économies
reduction = 1 - (lora_params / original_params)
print(f"Réduction des paramètres : {reduction:.2%}") # 99.22%
Pour un Modèle Complet
| Modèle | Original | LoRA (r=16) | Réduction |
|---|---|---|---|
| 7B | 7,000,000,000 | ~4,000,000 | 99.94% |
| 13B | 13,000,000,000 | ~8,000,000 | 99.94% |
| 70B | 70,000,000,000 | ~40,000,000 | 99.94% |
Pourquoi le Faible Rang Fonctionne
Dimensionnalité Intrinsèque
La recherche montre que les mises à jour d'ajustement ont tendance à se situer dans un sous-espace de faible dimensionnalité :
- Les modèles pré-entraînés ont déjà des représentations riches
- L'adaptation des tâches nécessite un "pilotage" plutôt qu'une "reconstruction"
- De nombreuses dimensions de l'espace des poids sont redondantes pour des tâches spécifiques
Preuves Empiriques
D'après l'article original de LoRA :
- r=4 souvent suffisant pour de nombreuses tâches
- r=8-16 recommandé pour des adaptations complexes
- r=64+ rarement nécessaire, rendements décroissants
Initialisation
Matrice A : Gaussienne Aléatoire
# A est initialisé avec de petites valeurs aléatoires
A = torch.randn(r, d) * 0.01
Matrice B : Zéro
# B est initialisé à zéro
B = torch.zeros(d, r)
Pourquoi Cette Initialisation ?
Au début de l'entraînement : BA = 0 × A = 0
→ Sortie inchangée par rapport au modèle de base
→ Début d'entraînement stable
→ Adaptation progressive
Quelles Couches Cibler
Architecture Transformer
Bloc d'Attention :
├── Projection de requête (q_proj) ← Cible commune
├── Projection de clé (k_proj) ← Parfois cible
├── Projection de valeur (v_proj) ← Cible commune
└── Projection de sortie (o_proj) ← Parfois cible
Bloc Feed-Forward :
├── Projection de porte (gate_proj) ← Parfois cible
├── Projection ascendante (up_proj) ← Parfois cible
└── Projection descendante (down_proj)← Parfois cible
Configurations Communes
| Configuration | Modules Ciblés | Cas d'Utilisation |
|---|---|---|
| Minimal | q_proj, v_proj | Expériences rapides |
| Standard | q_proj, k_proj, v_proj, o_proj | Équilibré |
| Complet | Toute l'attention + FF | Capacité maximale |
Fusionner LoRA
Pendant l'Inférence (Avec Adaptateur)
# Deux chemins : base + LoRA
output = W @ x + B @ A @ x
Léger surcoût dû au calcul supplémentaire.
Fusionné (Sans Adaptateur)
# Fusionner une fois
W_merged = W + B @ A
# Chemin unique à l'inférence
output = W_merged @ x
Pas de surcoût—identique au modèle ajusté.
Exemple de Fusion
from peft import PeftModel
# Charger le modèle avec adaptateur
model = PeftModel.from_pretrained(base_model, "path/to/adapter")
# Fusionner l'adaptateur dans le modèle de base
model = model.merge_and_unload()
# Maintenant, le modèle est un modèle régulier sans surcoût d'adaptateur
model.save_pretrained("merged-model")
Variantes de LoRA
LoRA Standard
La formulation originale décrite ci-dessus.
LoRA+
Applique des taux d'apprentissage différents aux matrices A et B.
DoRA (Adaptation de Faible Rang Décomposée en Poids)
Sépare l'amplitude et la direction des poids.
GaLore (Projection de Gradient de Faible Rang)
Projette les gradients dans un espace de faible rang.
Points Clés à Retenir
✅ LoRA représente les mises à jour de poids comme des produits de matrices de faible rang
✅ Entraîne seulement ~0.01-0.1% des paramètres originaux
✅ B initialisé à zéro assure un début d'entraînement stable
✅ Cible courante : projections d'attention (q_proj, v_proj)
✅ Peut être fusionné pour un surcoût d'inférence nul
✅ Le faible rang fonctionne car l'ajustement a une faible dimensionnalité intrinsèque
Prochaines Étapes
Continuez vers Chapitre 2 : Mathématiques de LoRA pour les fondations mathématiques.
Vérification Rapide
Avant de continuer, assurez-vous de pouvoir répondre :
- Comment LoRA représente-t-il les mises à jour de poids ?
- Pourquoi B est-il initialisé à zéro ?
- Que signifie "faible rang" dans le contexte de LoRA ?