LYCÉE → PRÉPA · L15

Module L15 · Partie H · Intelligence artificielle

Comment fonctionne un grand modèle de langage.

ChatGPT, Claude, Gemini : des transformeurs, entraînés à prédire le mot suivant. Ce module part du modèle de langue le plus simple (compter les paires de lettres), passe par les plongements et les réseaux récurrents, puis construit le mécanisme d’attention et un mini-transformeur en NumPy. À la fin, vous aurez entraîné dans la page un modèle qui génère des noms de robots plausibles — et vous saurez ce qui se passe derrière chaque réponse d’un assistant IA.

Durée : 4 séances · Prérequis : L14. Objectifs : modèle de langue et perplexité, tokenisation, plongements (embeddings), RNN et leurs limites, attention (Q, K, V), attention multi-têtes et masquage causal, blocs transformeurs, pré-entraînement / ajustement / RLHF (vue d’ensemble), lois d’échelle, limites et questions ouvertes.

Ce que vous saurez faire à la fin
  • Expliquer et coder le calcul d’attention et sa complexité en O(n²).
  • Entraîner un modèle de langue au niveau caractère et en générer des textes.
  • Lire l’architecture d’un transformeur (GPT) et situer chaque composant.
  • Discuter les limites (hallucinations, contexte, coût) avec des arguments techniques.

Références : « Attention Is All You Need » (Vaswani et al., 2017), « The Illustrated Transformer » (Alammar), « Let’s build GPT » et « makemore » (Karpathy), cours CS224n (Stanford), Dive into Deep Learning chapitres 9-11.

Fiche de cours · Définitions

Deep learning et transformers : définitions

Définition (token, plongement, vocabulaire). Un texte est découpé en tokens (sous-mots, BPE) d’un vocabulaire de taille V. Chaque token est représenté par un vecteur appris de dimension d (plongement, embedding) : matrice E de taille V×d. Une séquence de n tokens devient une matrice X de taille n×d.
Définition (attention). À partir de X, on calcule requêtes Q = XWQ, clés K = XWK, valeurs V = XWV (dimensions dk, dk, dv). Attention(Q, K, V) = softmax(QKᵀ/√dk)·V : chaque position produit une moyenne pondérée des valeurs, les poids mesurant la similarité requête-clé. Multi-têtes : h attentions en parallèle sur des projections différentes, concaténées.
Définition (bloc transformer). x ← x + MultiHead(LN(x)) ; x ← x + MLP(LN(x)), avec LN la normalisation de couche et deux connexions résiduelles. Un modèle empile L blocs. Encodeur : attention bidirectionnelle (BERT). Décodeur : attention causale (masque triangulaire : une position ne voit que les précédentes) pour la génération (GPT).
Définition (encodage de position). L’attention est invariante par permutation des positions : on ajoute à chaque token un vecteur dépendant de sa position (sinusoïdal, appris, ou rotatif RoPE).
Définition (modèle de langue, pré-entraînement, affinage). Un modèle de langue estime P(tokent | tokens<t). Pré-entraînement auto-supervisé sur un grand corpus (prédire le token suivant), puis affinage (fine-tuning) sur une tâche, ou instruction/RLHF pour suivre des consignes. La perplexité exp(perte moyenne) mesure la qualité du modèle de langue.
Définition (génération). Décodage glouton (argmax), échantillonnage avec température T (softmax(z/T)), top-k, nucleus (top-p), recherche en faisceau (beam search, largeur B).

Fiche de cours · Formules

Formules à connaître

Attention(Q, K, V) = softmax(QKᵀ/√dk) V — coût O(n²·d) en temps, O(n²) en mémoire pour les poids d’attention
LayerNorm(x) = γ ⊙ (x − μ)/√(σ² + ε) + β, μ et σ² calculées sur les d composantes du vecteur
Encodage sinusoïdal : PE(pos, 2i) = sin(pos/100002i/d), PE(pos, 2i+1) = cos(pos/100002i/d)
Perte du modèle de langue : L = −(1/n)Σt log P(xt | x<t) ; perplexité = eL
Paramètres d’un bloc ≈ 12d² (4d² attention + 8d² MLP à facteur 4) ; modèle ≈ 12Ld² + Vd
FLOPs d’entraînement ≈ 6 × Nparams × Ntokens ; inférence ≈ 2 × Nparams par token généré
Lois d’échelle (Chinchilla) : à budget fixe, Ntokens ≈ 20 × Nparams
ArchitectureDonnéesInductive biasCoût par couche
MLPVecteursAucunO(d²)
CNNImages, signauxLocalité, translationO(n·k²·C²)
RNN / LSTMSéquencesOrdre, récurrenceO(n·d²), séquentiel
TransformerSéquences, images (patchs), graphesAucun sur la position (ajouté), tout-à-toutO(n²d + nd²), parallèle

Fiche de cours · Théorèmes et démonstrations

Démonstrations à savoir refaire (1/2)

Théorème 1 (pourquoi diviser par √dk). Si les composantes de q et k sont i.i.d. centrées de variance 1, alors qᵀk a une variance dk ; qᵀk/√dk a une variance 1.
qᵀk = Σi=1dk qiki. Chaque terme est centré (E[qiki] = E[qi]E[ki] = 0) de variance E[qi²]E[ki²] = 1, et les termes sont indépendants : Var = dk. Diviser par √dk ramène la variance à 1. Sans cela, pour dk = 64, les logits ont un écart-type 8 : le softmax devient quasi one-hot (saturation), et son gradient pk(1 − pk) tend vers 0 — l’attention n’apprend plus.
Théorème 2 (l’attention est invariante par permutation). Si P est une matrice de permutation des positions, Attention(PQ, PK, PV) = P·Attention(Q, K, V).
(PQ)(PK)ᵀ = PQKᵀPᵀ ; le softmax ligne par ligne commute avec la permutation des lignes et des colonnes : softmax(PMPᵀ) = P·softmax(M)·Pᵀ. Puis P·softmax(M)·Pᵀ·PV = P·softmax(M)·V car PᵀP = I. La sortie à la position permutée est la sortie originale : le modèle ne « sait » pas où sont les tokens. D’où la nécessité d’un encodage de position, et l’aptitude naturelle des transformers aux ensembles (nuages de points, graphes).
Théorème 3 (l’encodage sinusoïdal code les décalages linéairement). Pour tout décalage k, il existe une matrice Mk (indépendante de pos) telle que PE(pos + k) = Mk·PE(pos).
Pour chaque fréquence ωi = 10000−2i/d, la paire (sin(ωipos), cos(ωipos)) devient (sin(ωi(pos + k)), cos(ωi(pos + k))) = R(ωik)·(sin, cos) par les formules d’addition — une rotation d’angle ωik. Mk est diagonale par blocs de rotations 2×2. Une projection linéaire (WQ, WK) peut donc exprimer « le token à distance k », quelle que soit la position absolue. RoPE pousse l’idée jusqu’au bout en appliquant ces rotations directement à q et k, de sorte que qᵀk ne dépende que de la distance relative.

Fiche de cours · Théorèmes et démonstrations

Démonstrations à savoir refaire (2/2)

Théorème 4 (masque causal et factorisation). Avec un masque triangulaire (−∞ sur les positions futures avant le softmax), la sortie à la position t ne dépend que de x≤t ; le modèle définit alors une loi jointe valide P(x₁…xn) = Πt P(xt | x<t), et l’entraînement sur toutes les positions d’une séquence se fait en un seul passage avant.
Le softmax d’une ligne ayant −∞ aux colonnes j > t donne un poids e−∞ = 0 à ces colonnes : la sortie t est une combinaison des valeurs V≤t, elles-mêmes fonctions de x≤t ; par récurrence sur les couches, idem à toutes les profondeurs. La règle de la chaîne des probabilités P(x₁…xn) = Π P(xt | x<t) est une identité toujours vraie ; le modèle fournit chaque facteur. Comme les n prédictions sont calculées simultanément (une matrice n×n d’attention), une séquence de n tokens fournit n exemples d’entraînement pour le prix d’un passage — c’est ce qui rend le pré-entraînement à grande échelle possible, contrairement aux RNN séquentiels.
Théorème 5 (la température contrôle l’entropie de l’échantillonnage). Pour pT = softmax(z/T) : T → 0 donne l’argmax (entropie 0), T = 1 la loi du modèle, T → ∞ la loi uniforme (entropie log V) ; l’entropie est croissante en T.
pT,k = ezk/T/Σezj/T. Quand T → 0, le terme de plus grand zk domine exponentiellement : p → one-hot. Quand T → ∞, tous les exposants tendent vers 0 : p → 1/V. Monotonie : dH/dT = VarpT(z)/T³ ≥ 0 (calcul direct à partir de H = −Σp log p et de la dérivée de la log-partition, dont la dérivée seconde en 1/T est la variance de z). En pratique : T ≈ 0,7 pour du code (précision), T ≈ 1 pour de la créativité ; top-p coupe la queue de faibles probabilités qui produit les tokens absurdes.
Proposition 6 (coût quadratique et fenêtre de contexte). Doubler la longueur de contexte n quadruple la mémoire des matrices d’attention (n² par tête et par couche) et le temps de la partie attention.
QKᵀ est n×n ; L couches × h têtes → L·h·n² valeurs. Pour n = 8 192, h = 32, L = 32 : 6,9·10¹⁰ valeurs par séquence (en float16, 137 Go) — d’où FlashAttention (calcul par blocs sans matérialiser la matrice), l’attention locale/sparse, et les modèles à état (Mamba) linéaires en n.

Fiche de cours · Méthodes

Méthodes et pièges

Méthode — implémenter un transformer minimal (ordre de vérification). (1) Embedding + position, vérifier les formes (n×d). (2) Une tête d’attention sans masque : sortie = moyenne des V si Q = 0 (test). (3) Masque causal : vérifier que modifier xt+1 ne change pas la sortie t. (4) Multi-têtes, résiduel, LayerNorm. (5) Sur-apprendre une séquence de 100 tokens : perte → 0. (6) Comparer la perplexité à un bigramme (L15 module) : le transformer doit faire nettement mieux.
Méthode — utiliser un modèle pré-entraîné. Ne jamais entraîner from scratch si un modèle pré-entraîné existe : affiner (fine-tuning complet, ou LoRA : matrices de rang faible ajoutées aux poids, 100× moins de paramètres à apprendre), ou simplement extraire des plongements et entraîner un petit classifieur dessus. Toujours comparer à « zéro-shot » avec un bon prompt.
Méthode — évaluer un modèle génératif. Perplexité sur un corpus tenu à l’écart ; métriques de tâche (exactitude sur un jeu de questions, BLEU/ROUGE pour traduction/résumé, avec leurs limites) ; évaluation humaine ou par modèle juge sur un échantillon ; tests de contamination (les données de test sont-elles dans le corpus d’entraînement ?).

Pièges : oublier le masque causal en génération (le modèle « triche » à l’entraînement puis échoue) ; tokenisation différente entre entraînement et inférence ; fenêtre de contexte dépassée silencieusement ; comparer des modèles à des nombres de tokens vus différents ; confondre « le modèle produit une phrase plausible » et « vraie ».

Fiche de cours · Exercices corrigés

Exercices corrigés

Exercice 1. Calculer à la main une attention pour 3 tokens en dimension 2 : Q = K = [[1, 0], [0, 1], [1, 1]], V = [[1, 0], [0, 1], [1, 1]], dk = 2. Donner la sortie de la position 3 sans masque, puis avec masque causal pour la position 2.
Correction. Position 3 : q₃ = (1, 1) ; scores q₃ᵀkj/√2 = (1, 1, 2)/1,414 = (0,707 ; 0,707 ; 1,414). Softmax : e0,707 = 2,03, e1,414 = 4,11 ; somme 8,17 ; poids (0,248 ; 0,248 ; 0,503). Sortie = 0,248·(1, 0) + 0,248·(0, 1) + 0,503·(1, 1) = (0,751 ; 0,751). Position 2 avec masque (voit 1 et 2) : q₂ = (0, 1), scores (0, 1)/1,414 = (0 ; 0,707), le troisième est −∞. Softmax : (1, 2,03)/3,03 = (0,330 ; 0,670). Sortie = 0,330·(1, 0) + 0,670·(0, 1) = (0,330 ; 0,670). La position 2 s’attend surtout à elle-même.
Exercice 2. Un modèle a d = 1 024, L = 24, h = 16, V = 50 000. Estimer le nombre de paramètres, le coût d’entraînement sur 100 milliards de tokens en FLOPs, et le temps sur un GPU à 10¹⁴ FLOP/s utile.
Correction. Blocs : 12·L·d² = 12·24·1,05·10⁶ ≈ 3,0·10⁸. Embeddings : V·d = 5,1·10⁷ (souvent partagés avec la sortie). Total ≈ 3,5·10⁸ paramètres (« 350 M »). FLOPs ≈ 6·N·D = 6·3,5·10⁸·10¹¹ = 2,1·10²⁰. Temps = 2,1·10²⁰/10¹⁴ = 2,1·10⁶ s ≈ 24 jours sur un GPU ; 8 GPU → 3 jours (si le parallélisme est efficace). Chinchilla suggère D ≈ 20N = 7·10⁹ tokens pour ce modèle : 100 G tokens est « sur-entraîné », ce qui est un choix courant pour l’inférence (petit modèle, beaucoup de données).
Exercice 3. Montrer qu’une tête d’attention peut implémenter exactement « copier le token précédent » avec un encodage de position approprié, et expliquer le lien avec les têtes d’induction.
Correction. Supposons que le plongement de la position t contienne un vecteur pt avec ptᵀps = 1 si s = t − 1 et 0 sinon (approximativement réalisable avec des sinusoïdes de fréquences variées, ou exactement avec des positions one-hot décalées). Prendre WQ et WK qui extraient ces vecteurs, avec un grand gain β : score(t, s) = β·ptᵀps vaut β en s = t − 1, 0 ailleurs ; le softmax concentre le poids sur t − 1 ; WV = identité recopie le token précédent. Une tête d’induction enchaîne deux têtes : la première écrit « le token qui me précède » dans chaque position, la seconde cherche la position passée dont le token précédent est égal au token courant et copie ce qui la suivait — c’est le mécanisme « [A][B] … [A] → [B] » qui explique une part de l’apprentissage en contexte des LLM (Olsson et al., 2022).

01 / Modèles de langue

Le modèle le plus simple : compter les bigrammes

Ce qu’est un modèle de langue

Une distribution de probabilité sur le prochain symbole sachant les précédents : P(x_t | x_1 … x_{t−1}). Générer = tirer au sort selon cette distribution, symbole après symbole. Évaluer = mesurer la probabilité que le modèle attribue à des textes réels (perplexité : « entre combien de choix le modèle hésite »). Le bigramme ne regarde que la lettre précédente ; GPT regarde des milliers de mots. Mais l’objectif, la génération et l’évaluation sont identiques. Le fameux « prédire le mot suivant » est exactement ceci, à grande échelle.

01 / Modèles de langue

Plongements : des symboles aux vecteurs

Dans un LLM, la table E a ~100 000 lignes (les tokens : morceaux de mots) et 4 000 à 16 000 colonnes. Les fameuses analogies « roi − homme + femme ≈ reine » (word2vec, 2013) viennent de ce que les plongements appris par prédiction du contexte capturent des régularités sémantiques — sans qu’on les ait programmées.

02 / Séquences

Réseau récurrent : un état qui résume le passé, et pourquoi il oublie

Le gradient qui disparaît, encore

Pour relier la sortie au temps t à l’entrée au temps t − k, le gradient traverse k fois Wh et tanh′ (≤ 1) : il s’évanouit (ou explose) exponentiellement en k. Un RNN simple ne retient guère plus de 10-20 pas. Les LSTM (1997) et GRU ajoutent des « portes » qui laissent passer l’information sans la multiplier — ils ont dominé la traduction et la parole jusqu’en 2017. Mais ils restent séquentiels : impossible de paralléliser sur la longueur, donc lents à entraîner sur des milliards de mots. L’attention résout les deux problèmes.

03 / Attention

L’attention : chaque position interroge toutes les autres

Lire l’attention comme une base de données floue

Chaque position émet une requête (« que cherche-je ? »), une clé (« que contiens-je ? ») et une valeur (« que transmets-je ? »). Le score requête·clé mesure la pertinence ; le softmax en fait des poids ; la sortie est la somme pondérée des valeurs. Contrairement au RNN, la position 100 accède à la position 1 en un pas, sans dégradation ; et tout se calcule par des produits matriciels parallèles. Le prix : n² scores. D’où les recherches sur l’attention linéaire, les fenêtres glissantes, FlashAttention (même calcul, mémoire optimisée) et les « états » façon RNN qui reviennent (Mamba) — un front de recherche actif.

03 / Attention

Multi-têtes, position, bloc transformeur

Anatomie de GPT
  1. Tokenisation (BPE) : le texte devient des entiers (morceaux de mots).
  2. Plongement de token + plongement de position (sans lui, l’attention ne sait pas l’ordre : c’est un ensemble, pas une séquence).
  3. N blocs : attention causale multi-têtes → MLP, chacun avec normalisation et résiduel.
  4. Tête de sortie : projection vers le vocabulaire, softmax, entropie croisée sur le token suivant.

Les têtes multiples permettent d’attendre à plusieurs choses à la fois (syntaxe, coréférence, position). Le MLP « par position » stocke l’essentiel des connaissances factuelles. Les résiduels et la normalisation (module L14) rendent 96 blocs entraînables.

03 / Attention

Entraîner un mini-transformeur avec autodiff : générer des noms de robots

Un transformeur complet — plongements, position, attention causale, MLP, normalisations, résiduels, Adam — en 60 lignes, avec ses gradients dérivés à la main (l’attention et la normalisation sont les deux dérivations délicates : comparez-les aux formules des modules L14 et L12). La perte descend, les noms générés ressemblent aux noms d’entraînement sans les copier. GPT-2 est ce code avec d = 768, 12 blocs et 40 Go de texte.

04 / Les grands modèles

Du transformeur à l’assistant : pré-entraînement, ajustement, RLHF

ÉtapeDonnéesObjectifRésultat
1. Pré-entraînementDes milliers de milliards de tokens (web, livres, code)Prédire le token suivantUn modèle « de base » qui complète du texte, sait beaucoup, mais n’obéit pas
2. Ajustement supervisé (SFT)Dizaines de milliers de dialogues écrits par des humainsImiter les réponsesUn modèle qui répond aux instructions
3. Apprentissage par préférences (RLHF / DPO)Paires de réponses classées par des humainsMaximiser la préférence humaine (module L16)Un assistant utile, honnête, inoffensif — en principe
Lois d’échelle et émergence

Kaplan (2020) et Hoffmann (« Chinchilla », 2022) ont montré que la perte suit des lois de puissance en fonction des paramètres, des données et du calcul, et qu’il faut ≈ 20 tokens par paramètre pour un entraînement efficace. Des capacités (arithmétique à plusieurs chiffres, raisonnement en chaîne) apparaissent à certaines tailles sans avoir été programmées — « émergence », dont la réalité statistique est débattue. Coût : GPT-4 ≈ 1025 opérations, des dizaines de millions de dollars et des mégawatts. Sur votre PC : un modèle de 7 milliards de paramètres quantifié en 4 bits tourne sur une carte graphique grand public (llama.cpp).

Ce qu’un LLM ne fait pas
  • Il ne « sait » pas ce qui est vrai : il produit le token le plus probable. D’où les hallucinations, réduites mais pas éliminées par RLHF et la recherche documentaire (RAG).
  • Il ne calcule pas : l’arithmétique est apprise par cœur, mal ; d’où l’appel à des outils (calculatrice, Python) — ce que fait cet assistant que vous utilisez.
  • Son contexte est fini (n² oblige) ; il n’a pas de mémoire entre les conversations sauf si on la lui donne.
  • Ses biais sont ceux de ses données ; son « alignement » est un problème de recherche ouvert (module L24).

04 / Les grands modèles

Au-delà du texte : vision, robotique, modèles génératifs

Le transformeur est devenu l’architecture universelle : texte (GPT), images (ViT, DALL·E), son (Whisper), protéines (AlphaFold 2), jeux (AlphaStar), robots (RT-2). La raison profonde : c’est un opérateur générique sur des ensembles de vecteurs, qui apprend lui-même quelles relations comptent. Comprendre ses limites — et ce qui viendra après — est l’un des sujets de recherche les plus actifs au monde.

Cours

Cours 1 — Modèles de langue : définition, factorisation, évaluation

Définition. Un modèle de langue attribue une probabilité à toute suite de tokens x1…xT. Par la règle de la chaîne (probabilités, L11), P(x1..T) = Πt P(xt | x1..t−1) : il suffit de modéliser « le suivant sachant le passé ». Un modèle n-gramme tronque le passé aux n−1 derniers tokens (Markov d’ordre n−1) ; un réseau récurrent le résume dans un état ; un transformeur regarde tout le contexte (borné par la fenêtre).

MesureFormuleInterprétation
Entropie croiséeH = −(1/T) Σ log2 P(xt | passé)Bits par token ; ce qu’on minimise à l’entraînement
Perplexité2H (ou eH en nats)« Nombre de choix équiprobables équivalent » ; uniforme sur V tokens : V
RepèresGPT-2 : ≈ 20-30 de perplexité par mot sur WikiText ; humains ≈ 10-12Plus bas = meilleur ; dépend du tokeniseur et du corpus, donc à comparer à tokeniseur égal

Échantillonner. À la génération, tirer selon P (température 1), ou aplatir/aiguiser avec une température τ : P ∝ exp(logits/τ) — τ → 0 : argmax (déterministe, répétitif) ; τ > 1 : plus créatif, plus d’erreurs. Top-k / top-p : ne tirer que parmi les k meilleurs / la masse p. Ces réglages expliquent le comportement des assistants IA.

Cours

Cours 2 — Exemple travaillé : dériver la passe arrière de l’attention

Avant. S = QKᵀ/√d, A = softmax(S) (par ligne), O = AV. Notons dO = ∂J/∂O (donné par la couche suivante).

  1. Traverser O = AV : dA = dO Vᵀ, dV = Aᵀ dO (règles de la couche affine, L14).
  2. Traverser le softmax par ligne : pour une ligne a = softmax(s), la jacobienne est diag(a) − a aᵀ, donc dS = a ⊙ (dA − ⟨dA, a⟩) — le terme ⟨dA, a⟩ = Σj dAj aj est soustrait à toute la ligne. En matrices : dS = A ⊙ (dA − rowsum(dA ⊙ A)).
  3. Traverser S = QKᵀ/√d : dQ = dS K/√d, dK = dSᵀ Q/√d.
  4. Masque causal : les positions masquées ont a = 0, donc dS = 0 : rien à faire de plus.

Vérification (comme toujours) par différences finies sur une petite matrice — c’est exactement ce que fait la cellule du mini-transformeur du cours (méthode attention_causale). Coût : mêmes produits n×n que l’avant ; mémoire O(n²) pour stocker A — d’où FlashAttention, qui recalcule A par blocs dans la passe arrière plutôt que de la garder.

Cours

Cours 3 — Utiliser un LLM comme composant : API, prompts, RAG, agents, évaluation

TechniquePrincipeQuandPiège
Prompt structuréRôle, tâche, format de sortie (JSON), exemples (few-shot)ToujoursAmbiguïté → réponses variables ; tester sur 50 cas
Chaîne de penséeDemander les étapes avant la réponseRaisonnement, calculPlus long, plus cher ; les étapes peuvent être fausses mais la réponse juste, et l’inverse
RAGChercher des documents (plongements + similarité cosinus) et les mettre dans le contexteConnaissances privées ou récentesQualité de la recherche ; le modèle peut ignorer ou contredire le contexte
Outils / fonctionsLe modèle émet un appel (calculatrice, SQL, API du robot), le programme l’exécute et renvoie le résultatCalcul exact, actionsValider chaque appel avant exécution ; jamais d’action irréversible sans confirmation
AgentBoucle observer → réfléchir → agir, avec mémoireTâches multi-étapesBoucles infinies, coûts, dérive ; borner le nombre d’étapes
Ajustement (fine-tuning, LoRA)Réentraîner quelques paramètres sur vos donnéesStyle, format, domaine étroitCoût, données étiquetées, oubli
ÉvaluationJeu de test avec réponses attendues ; métriques automatiques + relecture humaine ; LLM-juge avec prudenceAvant tout déploiementContamination (le test est dans les données d’entraînement)

Règle pour un robot piloté par LLM : le modèle propose, un programme classique vérifie (plage des commandes, sécurité, L21) et exécute. Le LLM n’est jamais dans la boucle de sécurité.

TP guidé

TP — nanoGPT sur vos propres textes, puis un modèle local avec outils (sur PC, 3 h)

Exercices

Exercices auto-corrigés — modèles de langue

Exercice 1 — Trigramme lissé et perplexité

Écrivez Trigramme : entraînement par comptage sur des mots (préfixe de deux tokens « <s> <s> », fin « </s> »), probabilité avec lissage additif α, log_prob(phrase) et perplexite(phrases) (en base e, par token, fin de phrase comprise). Vérifiez que la perplexité sur le corpus d’entraînement est plus basse que sur des phrases nouvelles, et qu’un α plus grand la rapproche de |V|.

Correction
class Trigramme:
    def __init__(self, phrases, alpha=0.1):
        self.alpha = alpha; self.c3 = Counter(); self.c2 = Counter(); self.vocab = {"</s>"}
        for ph in phrases:
            t = ["<s>", "<s>"] + ph.split() + ["</s>"]; self.vocab |= set(t[2:])
            for a, b, c in zip(t, t[1:], t[2:]): self.c3[(a, b, c)] += 1; self.c2[(a, b)] += 1
    def prob(self, w1, w2, w3): return (self.c3[(w1, w2, w3)] + self.alpha) / (self.c2[(w1, w2)] + self.alpha * len(self.vocab))
    def log_prob(self, phrase):
        t = ["<s>", "<s>"] + phrase.split() + ["</s>"]
        return sum(math.log(self.prob(a, b, c)) for a, b, c in zip(t, t[1:], t[2:]))
    def perplexite(self, phrases):
        n = sum(len(p.split()) + 1 for p in phrases); return math.exp(-sum(self.log_prob(p) for p in phrases) / n)

Exercice 2 — Plongements de position sinusoïdaux et attention relative

a) positions_sinus(n, d) : la matrice de l’article (« Attention Is All You Need », §3.5) : PE[pos, 2i] = sin(pos/100002i/d), PE[pos, 2i+1] = cos(…). b) Montrez numériquement que PE[pos+k] est une fonction linéaire de PE[pos] pour k fixé : pour chaque paire (2i, 2i+1), une rotation de l’angle k·ωi. Écrivez decaler(PE_pos, k, d) qui applique ces rotations et vérifiez qu’on retrouve PE[pos+k].

Correction
def positions_sinus(n, d):
    pos = np.arange(n)[:, None]; i = np.arange(0, d, 2)[None, :]; ang = pos / 10000 ** (i / d)
    PE = np.zeros((n, d)); PE[:, 0::2] = np.sin(ang); PE[:, 1::2] = np.cos(ang); return PE
def decaler(pe, k, d):
    out = pe.copy()
    for j, i in enumerate(range(0, d, 2)):
        w = 1 / 10000 ** (i / d); c, s = np.cos(k * w), np.sin(k * w)
        sn, cs = pe[i], pe[i + 1]
        out[i], out[i + 1] = sn * c + cs * s, cs * c - sn * s          # sin(a+b), cos(a+b)
    return out

Exercices

Exercices auto-corrigés — attention et génération

Exercice 3 — Attention multi-têtes avec masque de remplissage (padding)

Dans un lot, les séquences ont des longueurs différentes : on complète par des tokens de remplissage qui ne doivent recevoir aucune attention. Écrivez attention_multi(X, Wq, Wk, Wv, Wo, h, masque)masque (n,) vaut True pour les positions réelles : les colonnes de remplissage reçoivent −∞ avant le softmax. Vérifiez que la sortie des positions réelles ne change pas quand on modifie les vecteurs des positions de remplissage.

Correction
def attention_multi(X, Wq, Wk, Wv, Wo, h, masque):
    n, d = X.shape; dk = d // h
    Q, K, V = [(X @ Wm).reshape(n, h, dk).transpose(1, 0, 2) for Wm in (Wq, Wk, Wv)]
    S = Q @ K.transpose(0, 2, 1) / np.sqrt(dk); S = np.where(masque[None, None, :], S, -1e9)
    A = np.exp(S - S.max(-1, keepdims=True)); A /= A.sum(-1, keepdims=True)
    return (A @ V).transpose(1, 0, 2).reshape(n, d) @ Wo

Exercice 4 — Recherche en faisceau (beam search)

Avec le modèle Trigramme de l’exercice 1, écrivez beam_search(modele, largeur, max_len) qui génère la phrase la plus probable (log-prob totale) en gardant à chaque étape les largeur meilleures hypothèses ; comparez à la génération gloutonne (largeur 1). Une hypothèse est terminée quand elle émet </s>.

Correction
def beam_search(m, largeur=3, max_len=8):
    faisceau = [(["<s>", "<s>"], 0.0)]; finis = []
    for _ in range(max_len + 1):
        cand = []
        for seq, lp in faisceau:
            for w in m.vocab:
                p = lp + math.log(m.prob(seq[-2], seq[-1], w))
                if w == "</s>": finis.append((seq[2:], p))
                else: cand.append((seq + [w], p))
        faisceau = sorted(cand, key=lambda c: -c[1])[:largeur]
        if not faisceau: break
    return max(finis, key=lambda c: c[1])

05 / Défis

Défi ★ — Tokenisation BPE

Consigne

Implémentez l’algorithme Byte-Pair Encoding : partir des caractères, fusionner itérativement la paire adjacente la plus fréquente en un nouveau token, 30 fois, sur le corpus de noms. Affichez le vocabulaire obtenu et la tokenisation de « vortexon ». Puis encodez et décodez un texte : vérifiez que décoder(encoder(t)) == t. Pourquoi les LLM comptent-ils mal les lettres d’un mot ?

Correction (extrait)
from collections import Counter
mots = [list(m) + ["_"] for m in corpus.split()]
fusions = []
for _ in range(30):
    paires = Counter((a, b) for m in mots for a, b in zip(m, m[1:]))
    if not paires: break
    (a, b), _ = paires.most_common(1)[0]; fusions.append((a, b))
    mots = [[*m] for m in mots]
    for m in mots:
        i = 0
        while i < len(m) - 1:
            if m[i] == a and m[i + 1] == b: m[i:i + 2] = [a + b]
            else: i += 1
def encoder(texte):
    m = list(texte)
    for a, b in fusions:
        i = 0
        while i < len(m) - 1:
            if m[i] == a and m[i + 1] == b: m[i:i + 2] = [a + b]
            else: i += 1
    return m
print(fusions[:10]); print(encoder("vortexon"))

Un LLM voit « vortexon » comme [« vor », « tex », « on »] : il n’a jamais vu les lettres individuellement, d’où ses difficultés à compter les « r » dans « strawberry » — une limite d’interface, pas d’intelligence.

05 / Défis

Défi ★★ — Visualiser l’attention et sonder le modèle

Consigne

1) Modifiez attention_causale pour stocker la matrice A ; après entraînement, affichez-la (imshow) pour le nom « pulsar » : quelles positions regardent quoi ? 2) Ajoutez une 2e tête d’attention (séparer Q, K, V en deux moitiés) et comparez les matrices des deux têtes. 3) « Sonde » linéaire : entraînez une régression logistique (module L13) sur les activations de la couche cachée pour prédire « la lettre courante est une voyelle » : le modèle a-t-il appris cette notion sans qu’on la lui donne ?

Piste

Pour 3) : collectez pour chaque (nom, position) le vecteur x.v après le bloc (24 dimensions) et l’étiquette voyelle/consonne de la lettre à cette position ; entraînez logistique ; une exactitude nettement supérieure à la fréquence des voyelles montre que l’information est linéairement décodable dans les représentations. C’est la méthode de l’interprétabilité mécanistique, un domaine de recherche jeune : comprendre ce que les modèles ont appris en lisant leurs activations (Anthropic, OpenAI, DeepMind y consacrent des équipes).

05 / Défis

Défi ★★★ — Esprit prépa : complexité, contexte long et le retour des récurrences

Consigne

1) Mesurez le temps de la passe avant de Attention pour n = 64, 128, 256, 512, 1024 : vérifiez le O(n²) (et O(n² d) en mémoire pour A). 2) Implémentez l’attention linéaire : remplacer softmax(QKᵀ)V par φ(Q)(φ(K)ᵀV) avec φ(x) = elu(x) + 1 ; montrez que le coût devient O(n d²) et que, en causal, elle s’écrit comme une récurrence (un « état » d × d mis à jour à chaque token — exactement un RNN !). 3) Comparez la qualité (perte) sur les noms de robots avec l’attention softmax. 4) Lisez le résumé de « Mamba » (Gu & Dao, 2023) ou de « RWKV » et expliquez en quoi ces modèles sont des RNN qui s’entraînent comme des transformeurs. Quel est l’enjeu pour un robot qui doit traiter un flux de capteurs de plusieurs heures ?

Correction (extrait : attention linéaire causale comme récurrence)
def phi(x): return np.where(x > 0, x, np.exp(x) - 1) + 1          # elu + 1 > 0
def attention_lineaire_causale(Q, K, Vv):
    n, dk = Q.shape; S = np.zeros((dk, Vv.shape[1])); z = np.zeros(dk); out = np.zeros_like(Vv)
    for t in range(n):                                                # récurrence : état S (dk × dv), normaliseur z
        S += np.outer(phi(K[t]), Vv[t]); z += phi(K[t])
        out[t] = phi(Q[t]) @ S / (phi(Q[t]) @ z + 1e-9)
    return out
n, dk = 8, 4; Q, K, Vv = [rng.normal(0, 1, (n, dk)) for _ in range(3)]
# Version « parallèle » équivalente pour vérifier (masque causal explicite)
A = np.tril(phi(Q) @ phi(K).T); A /= A.sum(1, keepdims=True)
print(np.allclose(A @ Vv, attention_lineaire_causale(Q, K, Vv)))

L’état S résume tout le passé en d×d nombres : coût constant par token, contexte illimité — comme un RNN — mais entraînable en parallèle par la forme matricielle. Le prix est une capacité de « rappel exact » plus faible que le softmax (qui peut pointer précisément une position). Pour un robot, un modèle à état constant est la seule option pour un flux continu ; c’est pourquoi ces architectures (SSM, Mamba, xLSTM) sont très étudiées en robotique et en traitement du signal. Question ouverte : quelle est la bonne combinaison ?

06 / Vérification

Pourquoi un transformeur a-t-il besoin de plongements de position ?

Deux questions supplémentaires

1. Pourquoi le RNN oublie-t-il les positions lointaines ? Le gradient traverse k multiplications par Wh et tanh′ : il décroît exponentiellement en k.

2. Que mesure la perplexité ? exp de l’entropie croisée moyenne : le nombre effectif de choix entre lesquels le modèle hésite à chaque token.

Référence

Les mots à retenir

MotDéfinition
Modèle de langueP(token suivant | contexte).
Perplexitéexp(entropie croisée moyenne).
Token / BPEUnité de texte ; fusions de paires fréquentes.
PlongementVecteur appris par symbole.
RNN / LSTMÉtat récurrent ; portes contre l’oubli.
Attention (Q, K, V)softmax(QKᵀ/√d)V ; O(n²).
Masque causalInterdire de voir le futur.
Multi-têtesPlusieurs attentions en parallèle, concaténées.
Bloc transformeurAttention + MLP, avec normalisation et résiduels.
SFT / RLHFAjustement supervisé / par préférences humaines.
Lois d’échellePerte en loi de puissance du calcul, des données, des paramètres.
Attention linéaire / SSMCoût linéaire, forme récurrente.

Pour continuer

Vous savez ce qu’il y a dans un LLM

Module suivant : l’apprentissage par renforcement — apprendre par essais et récompenses : bandits, Q-learning, gradient de politique, et le lien avec la robotique et le RLHF.

À faire chez soi

← L14SommaireL16 : Apprentissage par renforcement →