Module L25 · Partie K · Ingénierie de l’IA
Le Transformer en profondeur : de l’équation à l’implémentation, et aux articles qui l’ont fait.
Le module L15 a introduit l’attention. Ici, on démonte entièrement la machine : chaque matrice, chaque gradient, le cache KV, les variantes (encodeur, décodeur, encodeur-décodeur, MQA/GQA, RoPE, FlashAttention), les lois d’échelle, et ce que disent les articles fondateurs — lus avec la méthode de L24. Vue informatique (complexité, mémoire, implémentation) et vue mathématique (formes quadratiques, softmax, gradients, invariances).
Durée : 4 séances · Prérequis : L10, L14, L15, L24. Objectifs : implémenter un transformer décodeur complet en NumPy avec rétropropagation manuelle ; expliquer chaque choix architectural par une raison mathématique ou matérielle ; lire « Attention Is All You Need », les lois d’échelle et FlashAttention ; dimensionner un modèle.
Ce que vous saurez faire à la fin
- Écrire un bloc transformer (attention multi-têtes causale, MLP, LayerNorm, résiduel) et son gradient, et l’entraîner.
- Expliquer pourquoi √dk, pourquoi le pré-LN, pourquoi le cache KV, pourquoi GQA, pourquoi FlashAttention.
- Calculer paramètres, FLOPs, mémoire d’activation et de cache pour un modèle donné.
- Résumer dix articles clés avec leur contribution et leurs limites.
Fiche de cours · Définitions
Définitions à connaître
Fiche de cours · Formules
Formules : calcul, mémoire, gradients
Fiche de cours · Théorèmes et démonstrations
Démonstrations à savoir refaire
01 / Implémenter
Une tête d’attention et son gradient, vérifiés par différences finies
Une erreur relative < 10⁻⁶ valide la dérivation. C’est la première chose à faire avant tout entraînement : un gradient faux entraîne quand même « un peu », et l’on perd des jours.
01 / Implémenter
Le bloc complet : multi-têtes, LayerNorm, MLP, résiduel — et un mini-GPT qui apprend
Un modèle de 2 blocs apprend le motif périodique en quelques centaines de pas : c’est une tête d’induction minimale (L15). Chaque ligne de ce code correspond à une équation de la fiche ; rien n’est caché.
02 / Analyser
Vue informatique : où va le calcul, où va la mémoire
Retenir : à l’entraînement, le coût est ≈ 6ND et le calcul domine ; à l’inférence en petit lot, la bande passante mémoire domine (chaque token relit tous les poids) — d’où la quantification (4 bits : 4× moins d’octets), le traitement par lots, et la génération spéculative.
02 / Analyser
Vue mathématique : ce que l’attention peut et ne peut pas représenter
Ce qu’elle fait bien
- Moyennes conditionnelles : la sortie est toujours dans l’enveloppe convexe des valeurs — une agrégation, pas une transformation ; la non-linéarité vient du softmax (sélection) et du MLP.
- Recherche associative : une requête retrouve les clés proches ; avec des clés = positions, c’est un accès par adresse ; avec des clés = contenu, une mémoire associative (Hopfield moderne : Ramsauer 2020).
- Composition en profondeur : deux couches composent des relations (tête d’induction : « ce qui suivait la dernière occurrence du token courant »).
Ses limites démontrées
- Compter et parité : une couche d’attention à précision finie ne calcule pas la parité de n bits pour n arbitraire (Hahn 2020) ; en pratique, les modèles échouent sur des tâches de comptage long sans chaîne de raisonnement explicite.
- Profondeur bornée ⇒ classe de circuits bornée : un transformer de profondeur L à précision log n est dans TC⁰ (Merrill & Sabharwal 2023) : il ne peut pas, en un passage, simuler des calculs séquentiels longs (d’où l’intérêt de « penser étape par étape », qui rallonge la séquence = plus de calcul).
- Dilution : avec n tokens, chaque poids est ≈ 1/n en moyenne ; retrouver une aiguille exige un score très contrasté — les longs contextes dégradent le rappel (« lost in the middle », Liu 2023).
03 / Variantes
Encodeur, décodeur, encodeur-décodeur : quelle architecture pour quelle tâche
| Famille | Attention | Pré-entraînement | Tâches | Exemples |
|---|---|---|---|---|
| Encodeur | Bidirectionnelle | Tokens masqués (MLM) | Classification, extraction, plongements, recherche (RAG !) | BERT, RoBERTa, DeBERTa, E5, BGE |
| Décodeur | Causale | Token suivant | Génération, dialogue, code, raisonnement | GPT, LLaMA, Mistral, Qwen, Claude |
| Encodeur-décodeur | Bidirectionnelle + causale + croisée | Débruitage de spans | Traduction, résumé, ASR (Whisper), OCR (TrOCR) | T5, BART, Whisper, mT5 |
| Vision (ViT) | Bidirectionnelle sur patchs | Supervisé / auto-supervisé (MAE, DINO) | Classification, détection, segmentation | ViT, DINOv2, SAM |
| Multimodal | Croisée image→texte | Contrastif (CLIP) puis génératif | Description, VQA, OCR « libre », agents | CLIP, LLaVA, PaliGemma, GPT-4V |
| Alternatives linéaires | Récurrence à état (SSM) | Token suivant | Très longs contextes, embarqué | Mamba, RWKV, hybrides (Jamba) |
Attention croisée (décodeur d’un encodeur-décodeur) : Q vient du décodeur, K et V de l’encodeur — le décodeur « lit » la source à chaque pas. C’est aussi le mécanisme des modèles de diffusion conditionnés par du texte.
Choisir : comprendre/classer/plonger → encodeur (petit, rapide, bidirectionnel) ; produire du texte → décodeur ; transformer une séquence en une autre de nature différente → encodeur-décodeur ; contexte de 10⁶ tokens sous contrainte mémoire → hybride SSM.
04 / Entraîner à l’échelle
Lois d’échelle, données, et le calcul que vous n’avez pas
Leçon d’ingénierie : le pré-entraînement à l’état de l’art coûte 10⁷ à 10⁹ €. Votre levier n’est pas là : il est dans l’affinage (LoRA, quelques heures de GPU), le RAG (L26), les données (L36) et l’évaluation (L32). Entraîner « from scratch » est le dernier recours (L37).
05 / Articles
Les articles à lire, avec ce qu’il faut en retenir
| Article | Contribution | À retenir / limite |
|---|---|---|
| Vaswani et al., Attention Is All You Need, NeurIPS 2017 — arXiv:1706.03762 | Architecture sans récurrence ; attention multi-têtes ; encodage sinusoïdal ; SOTA en traduction | Post-LN, warmup indispensable ; le tableau 1 (complexité par couche) explique tout |
| Devlin et al., BERT, 2018 — 1810.04805 | Pré-entraînement bidirectionnel par tokens masqués ; affinage universel | Base des modèles de plongement et de recherche ; pas génératif |
| Radford et al., GPT-2 2019 ; Brown et al., GPT-3, 2020 — 2005.14165 | Décodeur causal à l’échelle ; apprentissage en contexte (few-shot) émerge | La taille change la nature des capacités ; évaluation par prompts |
| Kaplan et al., Scaling Laws, 2020 — 2001.08361 ; Hoffmann et al., Chinchilla, 2022 — 2203.15556 | Perte en loi de puissance de N, D, C ; optimum N ≈ D/20 | Kaplan sous-estimait les données ; les lois dépendent du corpus et de la tokenisation |
| Xiong et al., On Layer Normalization in the Transformer, ICML 2020 — 2002.04745 | Pré-LN : gradients bornés à l’initialisation, pas de warmup nécessaire | Explique le choix de tous les LLM modernes |
| Su et al., RoFormer (RoPE), 2021 — 2104.09864 | Position relative par rotation de q, k | Extension de contexte par interpolation (Chen 2023, YaRN) |
| Dao et al., FlashAttention, NeurIPS 2022 — 2205.14135 ; v2 2023 | Attention exacte par blocs, IO-aware ; mémoire O(n) | Le goulot est la mémoire, pas les FLOPs ; adopté partout |
| Shazeer, Fast Transformer Decoding (MQA), 2019 — 1911.02150 ; Ainslie et al., GQA, 2023 — 2305.13245 | Partager K, V entre têtes : cache KV réduit | Qualité quasi intacte ; standard dans LLaMA-2/3, Mistral |
| Touvron et al., LLaMA, 2023 — 2302.13971 | Modèles ouverts entraînés au-delà de Chinchilla ; RMSNorm, SwiGLU, RoPE | La recette « moderne » standard |
| Gu & Dao, Mamba, 2023 — 2312.00752 | Modèle à état sélectif, linéaire en n | Compétitif jusqu’à ~3B ; hybrides attention + SSM en production |
| Dosovitskiy et al., ViT, ICLR 2021 — 2010.11929 | Images en patchs = tokens ; transformer pur en vision | Exige beaucoup de données ou un pré-entraînement auto-supervisé |
| Olsson et al., In-context Learning and Induction Heads, 2022 — 2209.11895 | Mécanisme concret de l’apprentissage en contexte | Interprétabilité mécaniste : lire les circuits, pas seulement les scores |
Méthode de lecture : trois passes (L24). Pour chacun, remplir : contribution en 3 phrases, protocole, résultat clé (chiffre), limite avouée, limite non avouée.
TP guidé
TP — nanoGPT sur PC : entraîner, mesurer, modifier (6 h)
- Installer.
pip install torch numpy tiktoken; clonerkarpathy/nanoGPT; jeu de données Shakespeare au niveau caractère (data/shakespeare_char/prepare.py). - Entraîner la baseline.
python train.py config/train_shakespeare_char.py(CPU : 30 min pour des résultats lisibles ; GPU : 5 min). Noter la perte de validation finale et le temps ; échantillonner (sample.py). - Vérifier la théorie. Compter les paramètres (formule 12Ld² + Vd) et comparer au
model.get_num_params(). Mesurer le temps par itération pour n = 64, 128, 256 : la partie attention doit croître en n². - Ablations. (a) Retirer l’encodage de position : que se passe-t-il ? (b) Post-LN au lieu de pré-LN : la perte diverge-t-elle sans warmup ? (c) Une seule tête vs 6 ; (d) sans connexions résiduelles à 6 couches. Un tableau : configuration, perte val, temps.
- Cache KV. Implémenter la génération avec cache dans
model.generate(garder K, V par couche) ; vérifier l’identité des sorties avec la génération naïve ; mesurer l’accélération pour 500 tokens. - FlashAttention. Activer
torch.nn.functional.scaled_dot_product_attention(déjà dans nanoGPT si PyTorch ≥ 2) ; comparer mémoire (torch.cuda.max_memory_allocated) et vitesse avec l’attention manuelle, pour n = 1024. - Livrable. Dépôt avec le tableau d’ablations (5 graines pour la baseline et la meilleure variante, moyenne ± écart-type), les mesures de coût, la génération avec cache, et une page « ce que j’ai vérifié des articles ».
Exercices
Exercices auto-corrigés
Exercice 1 — RoPE
Implémentez rope(x, pos, theta_base=10000) qui applique la rotation par paires de dimensions (2i, 2i+1) d’angle pos·θi, θi = base−2i/d, à un vecteur x de dimension d paire. Vérifiez que le produit scalaire ne dépend que de la différence de positions.
Correction
def rope(x, pos, theta_base=10000):
d = len(x); i = np.arange(d // 2); theta = theta_base ** (-2 * i / d); a = pos * theta
x1, x2 = x[0::2], x[1::2]
y = np.empty_like(x); y[0::2] = x1 * np.cos(a) - x2 * np.sin(a); y[1::2] = x1 * np.sin(a) + x2 * np.cos(a)
return yExercice 2 — Softmax par blocs (FlashAttention)
Implémentez attention_par_blocs(q, K, V, taille_bloc) pour une seule requête q : parcourez K, V par blocs en maintenant (m, l, o) selon le Théorème 4, sans jamais former le vecteur complet des scores. Comparez à l’attention directe.
Correction
def attention_par_blocs(q, K, V, taille_bloc):
dk = len(q); m, l, o = -np.inf, 0.0, np.zeros(V.shape[1])
for deb in range(0, len(K), taille_bloc):
s = K[deb:deb + taille_bloc] @ q / np.sqrt(dk)
m2 = max(m, s.max()); f = np.exp(m - m2) if m > -np.inf else 0.0; e = np.exp(s - m2)
l = f * l + e.sum(); o = f * o + e @ V[deb:deb + taille_bloc]; m = m2
return o / lExercices
Exercices auto-corrigés (suite)
Exercice 3 — GQA
Implémentez attention_gqa(X, Wq, Wk, Wv, h, g) : h têtes de requêtes, g groupes de K/V (h divisible par g) ; les têtes q0..h/g−1 utilisent le groupe 0, etc. Vérifiez que g = h redonne l’attention multi-têtes classique, et que g = 1 est la MQA. Retournez aussi le nombre de valeurs du cache KV.
Correction
def attention_gqa(X, Wq, Wk, Wv, h, g, causal=True):
n = len(X); dk = Wq.shape[1] // h; Q, K, V = X @ Wq, X @ Wk, X @ Wv; outs = []
for i in range(h):
grp = i // (h // g); q = Q[:, i*dk:(i+1)*dk]; k = K[:, grp*dk:(grp+1)*dk]; v = V[:, grp*dk:(grp+1)*dk]
S = q @ k.T / np.sqrt(dk)
if causal: S = S + np.triu(np.full((n, n), -1e9), 1)
A = np.exp(S - S.max(1, keepdims=True)); A /= A.sum(1, keepdims=True); outs.append(A @ v)
return np.concatenate(outs, 1), 2 * n * g * dkExercice 4 — Dimensionner
Complétez dimensionner(C) qui, pour un budget de calcul C (FLOPs), renvoie (N, D) selon Chinchilla (D = 20N, C = 6ND), puis (d, L) pour un modèle « carré » avec d = 128·L (h = d/128, dff = 4d, V = 32 000) tel que 12Ld² + Vd ≈ N.
Correction
def dimensionner(C, V=32000):
N = np.sqrt(C / 120); D = 20 * N # C = 6·N·20N = 120 N²
Ls = np.linspace(1, 200, 100000); ds = 128 * Ls; params = 12 * Ls * ds**2 + V * ds
L = Ls[np.abs(params - N).argmin()]; return N, D, 128 * L, LFiche de cours · Exercices corrigés
Exercices corrigés (rédaction)
Vérification
Pourquoi la génération token par token d’un LLM en lot de 1 est-elle lente sur GPU alors que le calcul par token est faible ?
Deux questions supplémentaires
1. Pourquoi diviser par √dk ? Pour garder la variance des scores à 1 et éviter la saturation du softmax (gradient nul).
2. Que gagne FlashAttention ? La même attention exacte sans matérialiser la matrice n×n : mémoire O(n), et 2–4× plus rapide grâce à la SRAM.
Référence
Les mots à retenir
| Mot | Définition |
|---|---|
| Tête d’attention | softmax(QKᵀ/√dk)V : moyenne des valeurs pondérée par la similarité requête-clé. |
| Pré-LN | Normalisation à l’entrée des sous-couches ; flux résiduel non normalisé ; gradients stables. |
| Cache KV | Mémorisation des K, V passés en génération ; coût par token linéaire. |
| MQA / GQA | K, V partagés entre têtes (ou groupes) : cache réduit. |
| RoPE | Rotation de q, k selon la position ; score dépendant de la position relative. |
| FlashAttention | Softmax par blocs, sans matrice n×n, adapté à la hiérarchie mémoire. |
| Loi d’échelle | Perte en loi de puissance de N, D, C ; Chinchilla D ≈ 20N. |
| Encodeur / décodeur | Bidirectionnel (comprendre) / causal (générer) / les deux (transformer une séquence). |
Suite
Vous avez démonté le moteur. Maintenant, la voiture : comment on l’utilise en production.
Un transformer seul ne sait rien de vos documents, et se trompe avec assurance. Le chapitre suivant construit un RAG — recherche + génération — et montre comment le mesurer.