Aller au contenu
Apprendre.
  • M1 · toutes les matières15 matières : Préparation de données, API & sécurité, CI/CD, Docker, Merise, Dataviz, KNIME, ML appliqué : projet LoL, SQL & UML, SQL avancé, Machine Learning, Git, IA frugale & responsable, Qualité des données, MathématiquesAnnée
  • M2 · toutes les matières4 matières : Deep Learning, AI Act, Comex & Fundraising, Gouvernance & DataMeshAnnée
  • Deep LearningM2 · 4 modules · Réseaux de neurones, CNN, texte, modèles génératifs.Matière
  • Préparation de donnéesM1 · 3 modules · Nettoyer, transformer et fusionner des données avec Python.Matière
  • API & sécuritéM1 · 2 modules · Consommer, concevoir et sécuriser des API.Matière
  • CI/CDM1 · 1 module · Intégration et livraison continues.Matière
  • DockerM1 · 3 modules · Conteneurs, images, volumes, réseaux, Compose.Matière
  • MeriseM1 · 2 modules · Modéliser une base : MCD, MLD, MPD.Matière
  • DatavizM1 · 2 modules · Visualiser pour comprendre et décider.Matière
  • KNIMEM1 · 1 module · Explorer et nettoyer des données en workflow.Matière
  • ML appliqué : projet LoLM1 · 2 modules · Du besoin métier au POC de vision par ordinateur.Matière
  • SQL & UMLM1 · 3 modules · Modèles UML et requêtes SQL.Matière
  • SQL avancéM1 · 3 modules · Jointures, fenêtres, CTE, optimisation.Matière
  • Machine LearningM1 · 3 modules · Régression, classification, segmentation, projet.Matière

↑↓ naviguer↵ ouvre le résultat en surbrillanceesc fermer

Partie 4

Adapter sans tout réentraîner (LoRA)

Après la pause : adapter DrBERT à MorFITT sans réentraîner ses 110 millions de poids. Rappel des façons déjà vues, avec le nombre de poids qui apprennent (4 octets par poids) :

Façon d'adapter DrBERTCe qui apprendPoids entraînésÀ stocker par tâche
Extraction (J3)la tête seule9 22837 ko
Dernier bloc (J3)le dernier des 12 blocs et la tête7 097 10028,4 Mo
Tout le modèleles 12 blocs, la table d'embeddings et la tête110 040 588440 Mo

L'extraction ne change rien dans DrBERT ; le dernier bloc n'en adapte que la fin ; tout réentraîner coûte une copie complète par tâche (et beaucoup trop de temps sur processeur). Peut-on adapter tous les blocs en n'entraînant que peu de poids ? C'est LoRA.

Prenons la couche dense des requêtes de l'attention : elle reçoit 768 nombres et rend y = x W₀, 768 nombres. Le fine-tuning classique modifie directement W₀ (à chaque pas, W devient W − η × pente) : on obtient une nouvelle matrice de 768 × 768 qui remplace W₀. LoRA gèle W₀ et apprend une correction ΔW ; la couche calcule avec les poids effectifs W₀ + ΔW, et W₀ reste intacte.

La correction s'écrit ΔW = A B

Une correction ΔW libre aurait 768 × 768 = 589 824 nombres, autant que W₀. LoRA lui impose une structure : ΔW = A B, où A a 768 lignes et r colonnes et B a r lignes et 768 colonnes. Le rang r est choisi petit, ici 8. Le produit A B a bien 768 × 768 cases, mais il est déterminé par 768 × 8 + 8 × 768 = 12 288 nombres : 48 fois moins. En pratique la bibliothèque multiplie la correction par α / r (ici 16 / 8 = 2) ; cela ne change pas le principe.

À chaque pas : (1) prédire avec W₀ + A B, (2) mesurer la perte comme en J3, (3) rétropropager : on calcule les pentes de A et B (W₀, gelée, n'en a pas), (4) l'optimiseur modifie A et B, et la correction A B suit. LoRA ne rajoute pas une nouvelle correction à chaque pas : il met à jour A et B. Au départ B = 0 et A est aléatoire : A B est nulle, le modèle se comporte exactement comme DrBERT.

Fais varier le rang r : le nombre de poids appris (r × 1 536 par couche) reste minuscule devant les 589 824 d'une correction libre. À r = 8 tu retrouves 12 288 par couche et 304 140 en tout (0,276 % des poids). Coche « B = 0 » : la correction disparaît.

À toi de coder

Compter les poids de LoRA

Pourquoi : c'est le chiffre qui justifie LoRA. Pour une matrice de d lignes et k colonnes, une correction libre a d × k poids ; A (d × r) et B (r × k) en ont r × (d + k). Complète la fonction lora_poids(d, k, r), puis le total de DrBERT : 24 couches (requêtes et valeurs des 12 blocs) plus la tête de 9 228 poids.

Ctrl/⌘+Entrée

À toi de coder

ΔW = A·B sur une maquette

Pourquoi : voir que les poids effectifs W₀ + A B changent la sortie alors que W₀ ne bouge pas, et que B = 0 redonne le modèle d'origine. Maquette en dimension 2 : x = [1 ; 1], W₀ = identité, A = une colonne (0 ; 1), B = une ligne (0,5 ; 0). Écris le produit de deux matrices (listes de listes), puis calcule dW = A B, la matrice W_eff = W₀ + dW et la sortie x W_eff. Ensuite refais le calcul avec B = 0 pour sortie_depart. On ignore ici le facteur α / r.

Ctrl/⌘+Entrée

Où placer LoRA dans DrBERT

Dans chaque bloc, l'attention calcule pour chaque token trois vecteurs de 768 nombres, chacun avec sa couche dense 768 → 768 : la requête q (son score avec un token j est q · k(j)), la clé k (à quoi les requêtes sont comparées) et la valeur v (ce que le token transmet : la sortie est la moyenne des valeurs pondérée par les poids d'attention). Le bloc contient aussi un MLP.

LoRA, comme dans son article (Hu et al., 2021), ajoute A et B aux couches des requêtes et des valeurs : corriger q change les poids d'attention, corriger v change ce que chaque token transmet. Cela fait 2 couches par bloc, 24 en tout : 12 × 2 × 12 288 = 294 912 poids, plus 9 228 pour la tête, soit 304 140. C'est 23 fois moins que le dernier bloc de J3, et les 12 blocs sont adaptés.

Coche d'autres couches (clé, MLP) ou change r : le total bouge. Le MLP à 768 → 3 072 → 768 est le BERT standard, hors cours : c'est l'exercice 4 du notebook (r = 1, ou q, k, v) qui te fait explorer ces variantes.

En PyTorch : la bibliothèque PEFT

from peft import LoraConfig, get_peft_model
config = LoraConfig(r=8, lora_alpha=16, target_modules=["query", "value"])
drbert = get_peft_model(drbert, config)   # gèle DrBERT, ajoute A et B aux 24 couches
drbert.print_trainable_parameters()       # 294 912 poids entraînables
tete = nn.Linear(768, 12)                 # la tête de J3 : 9 228 poids
entrainables = [p for p in drbert.parameters() if p.requires_grad] + list(tete.parameters())
opt = torch.optim.AdamW(entrainables, lr=5e-4)   # A, B et la tête seulement

Tu écris ces lignes, puis la boucle d'entraînement de J3 sans changement. lora_alpha (α) règle la taille de la correction : PEFT multiplie x A B par α / r (16 / 8 = 2). On fixe α une fois, ce qui permet de changer r sans changer le pas d'apprentissage.

Fine-tuning classique ou LoRA ? LoRA échange un peu de liberté contre beaucoup moins de poids à entraîner, de mémoire et de stockage. Après l'entraînement, on peut calculer une fois W₀ + 2 × A B et la mettre à la place de W₀ : le modèle adapté est alors aussi rapide que DrBERT.

Le tableau du cours, recalculé pour le r que tu choisis. À r = 8 : 304 140 poids, 1,2 Mo par tâche contre 440 Mo pour une copie complète.