Chapitre 9 · 14 min
Multi-têtes et résidus
Combine les têtes, normalise les flux résiduels et rapproche ton code d’attention local d’un bloc transformer empilable.
Au chapitre 8, tu as construit une tête d’attention. Elle calculait un motif d’attention : une matrice de qui écoute qui. C’est un seul motif de routage par couche.
Ce n’est pas assez. Le texte contient plusieurs relations simultanées : accord sujet/verbe, résolution de pronoms, modificateurs, similarité sémantique, biais de position. Un modèle avec une seule doit choisir.
La correction est évidente : lancer plusieurs têtes en parallèle. Chaque tête a ses propres W_Q, W_K, W_V, donc chacune apprend un motif différent. On concatène leurs sorties, puis on projette le résultat. C’est l’attention multi-têtes. La permet d'exploiter plusieurs motifs d'attention en parallèle.
Ce chapitre ajoute aussi les connexions résiduelles et la LayerNorm, les deux pièces qui permettent d’empiler des couches sans effondrer l’entraînement. Les et la sont essentielles pour les réseaux profonds.
1. Combiner plusieurs têtes
Tu as déjà une tête d’attention. Le chapitre fournit quatre sorties de têtes différentes, chacune [seq_len × d_head]. La cellule les combine :
- Concaténer les sorties sur l’axe des features.
- Projeter la matrice concaténée avec
W_O.
Code · JavaScript
Le résultat garde la forme de l’entrée, [seq_len × d_model], mais chaque token reflète maintenant plusieurs motifs d’attention.
2. Inspecter les motifs appris
Les quatre matrices d’attention pré-calculées sont affichées en heatmaps. Certaines sont nettes, d’autres diffuses.
Une façon de mesurer la concentration est l’ : H = -Σ p log p. Une entropie basse signifie que la tête se concentre sur peu de tokens.
Code · JavaScript
Attention patterns across heads · log(n) ≈ 1.61 = maximum entropy for 5 tokens
Head 0
Head 1
Head 2
Head 3
Tu devrais voir des différences entre têtes. Dans un vrai transformer, on trouve des têtes spécialisées : copie, induction, suivi de noms, syntaxe. On nomme des motifs découverts ; on ne les a pas demandés explicitement.
3. Résiduel + LayerNorm
Brancher une sous-couche directement dans la suivante casserait l’entraînement :
- Gradient qui disparaît : chaque couche compresse un peu le signal.
- Dérive des représentations : les magnitudes explosent ou s’écrasent.
La connexion résiduelle corrige le premier point : output = input + sublayer(input). Le gradient a un chemin propre à travers l’addition. La permet aux gradients de circuler proprement à travers les couches profondes.
La LayerNorm corrige le second : après l’addition, on normalise chaque ligne de token à moyenne 0 et écart-type 1. La stabilise l'entraînement en maintenant des échelles d'activation constantes.
Code · JavaScript
Les statistiques de ligne doivent montrer moyenne 0 et écart-type proche de 1. C’est l’invariant que LayerNorm donne à la couche suivante.
Pourquoi ce chapitre compte
Nous avons maintenant les pièces d’un bloc transformer :
- Attention : extraire de l’information d’autres positions.
- Multi-têtes : plusieurs routes d’attention.
- Résiduel + LayerNorm : l’infrastructure pour empiler. Les et la sont essentielles pour les réseaux profonds.
Le prochain chapitre les assemble en bloc complet.
4. Ajouter les helpers résiduels et de normalisation
Ajoute à llm/nn.py :
import math
def add(a: Matrix, b: Matrix) -> Matrix:
return [
[x + y for x, y in zip(row_a, row_b)]
for row_a, row_b in zip(a, b)
]
def layer_norm(x: Matrix, eps: float = 1e-5) -> Matrix:
out: Matrix = []
for row in x:
mean = sum(row) / len(row)
var = sum((value - mean) ** 2 for value in row) / len(row)
denom = math.sqrt(var + eps)
out.append([(value - mean) / denom for value in row])
return outaddest la connexion résiduelle.layer_normtravaille ligne par ligne, donc chaque token est normalisé indépendamment.epsévite la division par zéro.
Tu as maintenant l’invariant du transformer : chaque sous-couche accepte une matrice et retourne une matrice de même forme.
Recap
- L’attention multi-têtes lance
Hattentions en parallèle, concatène et projette. - Des têtes différentes apprennent des motifs différents. - Connexion résiduelle = `output = input
- sublayer(input)`. - LayerNorm stabilise l’échelle de chaque token. - Ton projet local a maintenant les helpers résiduels et LayerNorm.
Pour aller plus loin
- Vaswani et al., “Attention Is All You Need”.
- transformer-circuits.pub.
- He et al., “Deep Residual Learning”.
Prochaine étape : le bloc transformer complet — assembler les pièces dans l’unité qu’on empile.