Un token n'a qu'un seul vecteur d'embedding, toujours le même, qu'il apparaisse dans « le chat dort » ou « chat, réponds-moi ». Pourtant un LLM traite ces deux occurrences très différemment. Ce qui comble l'écart entre un vecteur fixe et un sens qui dépend du contexte, c'est l'attention : le mécanisme qui recalcule la représentation de chaque token en le mettant en relation avec tous les autres tokens de la séquence.
C'est la pièce qui revient partout sans jamais être détaillée : elle explique le cache KV, elle sous-tend le calcul du prefill et du decode, elle transforme les tokens en une séquence de vecteurs que la couche de sortie finit par convertir en logits. Ouvrons la boîte.
Le problème que l'attention résout
Un mot polysémique montre bien le problème. Dans « L'avocat a mangé un avocat avant l'audience », les deux occurrences du mot « avocat » partagent le même embedding de départ — le même point de la table de vocabulaire — alors que leur sens n'a rien à voir. Un réseau qui traiterait chaque token isolément, embedding par embedding, ne pourrait jamais lever cette ambiguïté : rien dans le vecteur de « avocat » pris seul ne dit s'il s'agit du fruit ou du métier.
Ce qui désambiguïse, c'est le contexte — les autres mots de la phrase. « mangé » et « audience » suffiraient à un lecteur humain. L'attention formalise exactement ça : chaque token « regarde » tous les autres tokens de la séquence et ajuste sa représentation en fonction de ceux qui lui sont pertinents. Le résultat n'est plus un embedding statique de vocabulaire, mais une représentation contextualisée — différente à chaque occurrence, même pour un mot identique.
Requête, clé, valeur : la mécanique
Concrètement, chaque token de la séquence génère trois vecteurs à partir de son embedding, via trois matrices apprises pendant l'entraînement : une requête Q (ce qu'il cherche), une clé K (ce qu'il propose comme information) et une valeur V (ce qu'il transmet si on le choisit). Ces trois rôles sont la seule idée réellement nouvelle du mécanisme — tout le reste n'est que du calcul dessus.
Pour calculer la nouvelle représentation d'un token, on compare sa requête Q à la clé K de tous les tokens du contexte — lui compris — via un produit scalaire : plus Q et K pointent dans la même direction, plus le score est élevé. Ce score est ensuite divisé par √d_k, la racine carrée de la dimension des vecteurs, pour empêcher les produits scalaires de grandir avec la dimension au point de saturer le softmax qui suit — sans cette normalisation, la fonction finirait presque toujours par donner tout son poids à un seul token, quelle que soit sa pertinence réelle.
Le softmax transforme ensuite ces scores en poids positifs qui somment à un — un poids pour chaque paire (token courant, token du contexte). La nouvelle représentation du token courant est enfin la somme des vecteurs V de tous les tokens, pondérée par ces poids : les tokens pertinents contribuent fortement, les autres presque pas.
L'attention ne voit pas l'ordre des mots
Le calcul décrit ci-dessus est parfaitement symétrique par rapport à la position : permuter deux tokens en entrée permute leurs sorties à l'identique, sans rien changer au résultat des autres. « Le chien mord le chat » et « Le chat mord le chien » produiraient, côté attention pure, des scores identiques entre les mêmes paires de mots. Pour que l'ordre compte, on ajoute un encodage positionnel à chaque embedding avant de calculer Q, K et V — un vecteur qui code le rang du token dans la séquence, calculé par une formule fixe (sinus/cosinus dans le transformer original) ou appris (RoPE dans la plupart des LLM récents). Sans cet ajout, un transformer ne serait rien de plus qu'un sac de mots sophistiqué.
Multi-tête : plusieurs points de vue en parallèle
Un seul jeu de projections Q/K/V ne capture qu'un type de relation à la fois. Le multi-head attention en fait tourner plusieurs en parallèle — huit, seize, parfois plus selon le modèle — chacune sur un sous-espace plus petit de l'embedding : la dimension totale est répartie entre les têtes, pas dupliquée, donc le coût de calcul global reste comparable à celui d'une seule tête pleine dimension. Les sorties de toutes les têtes sont concaténées puis reprojetées une seule fois.
L'intérêt, c'est la spécialisation. Dans un modèle entraîné, on observe empiriquement des têtes qui se concentrent sur l'accord grammatical entre un sujet et son verbe, d'autres sur la référence d'un pronom à son antécédent, d'autres encore sur la simple proximité positionnelle — le mot juste avant. Aucune de ces spécialisations n'est programmée : elle émerge de l'entraînement, parce que chaque tête possède sa propre matrice de projection à optimiser.
Le prix à payer : complexité quadratique
Calculer un score entre chaque paire de tokens coûte O(n²) en longueur de séquence n : doubler le contexte quadruple le calcul d'attention et la taille du cache K/V qui en découle. C'est la raison structurelle pour laquelle les très longs contextes restent chers, indépendamment de la puissance brute du GPU — ce n'est pas un problème d'optimisation logicielle, c'est la forme mathématique du mécanisme.
| Stratégie | Portée | Complexité calcul | Mémoire cache K/V | Usage |
|---|---|---|---|---|
| Attention complète | tous les tokens précédents | O(n²) | O(n) | défaut, qualité maximale |
| Fenêtre glissante | les w derniers tokens seulement | O(n·w) | O(w) | contextes très longs, débit prioritaire |
| Attention groupée (GQA) | tous les tokens, clés/valeurs mutualisées entre têtes | O(n²) inchangé | réduite (÷ nb de groupes) | alléger la bande passante mémoire en decode |
Le point commun à ces variantes : aucune ne change ce que fait une attention — comparer une requête à des clés, pondérer des valeurs — elles changent seulement combien de paires sont effectivement calculées ou combien de mémoire chaque paire mobilise. La quantification attaque un axe différent et complémentaire : elle ne réduit pas le nombre de paires, mais le nombre de bits utilisés pour représenter chaque poids appris.
Ce que l'attention n'est pas
- Ce n'est pas une recherche exacte. Les poids sont continus ; sauf cas dégénéré où le softmax sature, aucun token n'est jamais choisi « tout ou rien ».
- Ce n'est pas une règle écrite à la main. Les mêmes matrices Wq, Wk, Wv s'appliquent à tous les tokens de la séquence — la spécialisation observée par tête vient de l'entraînement, pas d'un code qui la programme.
- Ce n'est pas toute la couche. Chaque couche d'un transformer enchaîne une attention puis un petit réseau appliqué token par token, et ces couches sont empilées des dizaines de fois avant d'atteindre les logits finaux.
FAQ
Pourquoi diviser par √d_k ?
Le produit scalaire Q·K croît avec la dimension des vecteurs : plus d_k est grand, plus les scores ont une variance élevée. Un score trop grand pousse le softmax vers une distribution presque binaire, ce qui aplatit le gradient et bloque l'apprentissage. Diviser par √d_k ramène la variance des scores dans une plage stable, indépendante de la dimension choisie.
Le multi-head attention coûte-t-il plus cher qu'une seule tête ?
Non, à dimension totale égale : répartir l'embedding entre 8 têtes de dimension d/8 coûte, en calcul brut, à peu près la même chose qu'une seule tête de dimension d. Le gain n'est pas en vitesse, il est en expressivité — plusieurs relations captées en parallèle plutôt qu'une seule moyenne de compromis.
L'attention a-t-elle remplacé les RNN ?
Dans les LLM actuels, oui : le transformer a supplanté les réseaux récurrents (RNN/LSTM) comme architecture dominante, précisément parce que l'attention traite tous les tokens en parallèle — bon pour le prefill — là où un RNN doit les parcourir un par un, sans possibilité de paralléliser sur la longueur de la séquence.