Module 4 — Encodage de position, absolu puis rotatif
L'attention traite les jetons comme un ensemble, pas comme une séquence. Sans intervention, elle ne distingue pas « le chat mange la souris » de « la souris mange le chat », alors que le sens est opposé. Ce module explique pourquoi, puis décline les trois familles d'encodage de position — sinusoïdal, appris, rotatif — que l'on rencontre en 2026 dans BERT, GPT et leurs descendants.
L'attention est permutation-équivariante
C'est un fait souvent perçu à contrecœur, mais il se démontre en une ligne. Si l'on permute les lignes de la matrice d'entrée , les projections , , subissent la même permutation, la matrice subit la permutation sur ses deux axes, et la sortie de l'attention subit la même permutation. Autrement dit, l'attention ne voit pas l'ordre ; elle voit qui pointe vers qui, indépendamment du rang de chaque jeton dans la séquence.
Sur notre fil rouge, cela pose immédiatement problème : « 3 mars 2026 » et « 2026 mars 3 » ont le même sac de jetons, alors que seul le premier ordre correspond à une date française. Le modèle doit pouvoir dire « le jeton en position 1 est différent du jeton en position 3 » pour apprendre la structure de la date. Puisque l'attention ne peut pas fournir cette information, il faut la lui ajouter en entrée.
Encodage sinusoïdal, la solution d'origine
Vaswani et ses coauteurs proposent en 2017 un encodage entièrement construit à la main, sans paramètre appris. À chaque position et chaque dimension du plongement, on ajoute une composante :
Puis on additionne simplement cet encodage au plongement de jeton :
Le choix des fréquences n'est pas arbitraire. Les basses dimensions oscillent vite (petite période) et les hautes dimensions oscillent lentement (grande période) ; l'encodage couvre ainsi une gamme d'échelles, ce qui donne à l'attention de quoi comparer des distances longues et courtes. Une propriété intéressante en découle : pour un décalage fixe , s'exprime comme une transformation linéaire de . Le modèle peut donc apprendre à compter des décalages entre positions sans avoir vu chaque position en absolu.
import torch
import math
def encodage_sinusoidal(L: int, d: int) -> torch.Tensor:
pe = torch.zeros(L, d)
position = torch.arange(L).unsqueeze(1).float()
div = torch.exp(torch.arange(0, d, 2).float() * -(math.log(10000.0) / d))
pe[:, 0::2] = torch.sin(position * div)
pe[:, 1::2] = torch.cos(position * div)
return pe
pe = encodage_sinusoidal(L=32, d=64)
print(pe.shape) # (32, 64)
Positions apprises, l'approche de BERT
BERT retient une variante plus simple : une table de plongement de positions, entièrement apprise. Une matrice contient un vecteur par position possible, et l'on ajoute la ligne correspondante au plongement de jeton, exactement comme dans le cas sinusoïdal.
L'avantage est la souplesse : le modèle peut ajuster la représentation de chaque position à sa guise. L'inconvénient est la rigidité en longueur. Si , aucune position au-delà de 512 n'a de plongement associé, et le modèle ne peut pas traiter une séquence plus longue sans réentraîner cette table. C'est pourquoi les modèles de type BERT plafonnent en général à 512 jetons.
import torch.nn as nn
class PositionsApprises(nn.Module):
def __init__(self, L_max: int, d: int):
super().__init__()
self.pe = nn.Embedding(L_max, d)
def forward(self, x):
L = x.size(1)
positions = torch.arange(L, device=x.device)
return x + self.pe(positions)
RoPE, la position rotative des modèles récents
Le Rotary Position Embedding, ou RoPE, introduit par Su et al. en 2021, est aujourd'hui la solution dominante dans les modèles de langue récents. LLaMA, Qwen, Gemma, Mistral l'utilisent tous. L'idée est ingénieuse : au lieu d'ajouter un vecteur de position, on fait tourner les vecteurs et dans le plan complexe, avec un angle proportionnel à la position.
Formellement, on découpe chaque vecteur en paires de coordonnées et on applique à chaque paire une rotation d'angle dépendant de la position. La conséquence est mathématiquement remarquable : le produit scalaire après rotation ne dépend plus que de la différence . Autrement dit, RoPE donne à l'attention une notion relative de position, ce qui a deux effets pratiques importants :
- il n'y a plus de longueur maximale codée en dur ; le modèle peut être appliqué à des séquences plus longues que celles vues à l'entraînement, avec une dégradation progressive au lieu d'un mur ;
- des techniques d'extension de contexte (interpolation, YaRN) permettent d'augmenter la longueur maximale utile de plusieurs facteurs sans réentraîner tout le modèle.
Pour un cours ou un projet personnel, l'encodage sinusoïdal reste le plus lisible et se code en dix lignes. Pour un modèle jouet réentraîné à chaque fois, les positions apprises sont plus flexibles. Pour un modèle de production visant les longs contextes, RoPE est le standard actuel.
Extrapolation, interpolation et le problème du contexte long
Un modèle entraîné avec un contexte de 4096 jetons peut-il travailler sur 32 000 jetons ? La réponse dépend directement de l'encodage :
| Encodage | Comportement au-delà de la longueur d'entraînement |
|---|---|
| Positions apprises | Erreur d'index : la table ne contient pas ces positions |
| Sinusoïdal absolu | Le modèle voit des positions qu'il n'a pas rencontrées à l'entraînement ; performance dégradée mais définie |
| RoPE brut | Fonctionne mais les scores d'attention peuvent devenir instables |
| RoPE + interpolation | Le facteur de fréquence est mis à l'échelle, extension propre |
| RoPE + YaRN | Extension par facteur 8 à 32 sur les grands modèles récents |
L'ordre dans le tableau reflète l'évolution du domaine : la contrainte de longueur, qui semblait figée en 2018, a progressivement été levée par des ajustements de l'encodage de position, sans changer l'architecture du reste du Transformer.
Sur le fil rouge
Pour notre tâche de traduction de dates, une séquence source fait au plus une vingtaine de jetons et une séquence cible dix. Le contexte long n'est pas un enjeu ; on retiendra donc un encodage sinusoïdal dans l'implémentation finale, pour sa simplicité pédagogique. Nous verrons au module 10 la ligne exacte qui l'ajoute au plongement, et pourquoi on l'y additionne juste avant la première couche d'attention.
On ajoute l'encodage de position au plongement de jeton ; on ne le concatène pas. La concaténation obligerait à doubler la dimension et à réserver la moitié à la position, ce qui gaspille de la capacité. L'addition maintient la dimension et laisse le réseau démêler ce qui vient du jeton et ce qui vient de la position — il y parvient très bien.
En résumé
- L'attention étant permutation-équivariante, elle est aveugle à l'ordre ; il faut lui injecter l'information de position par un signal ajouté aux plongements.
- L'encodage sinusoïdal utilise des fréquences échelonnées ; il n'a aucun paramètre et permet de représenter des décalages linéairement.
- Les positions apprises (BERT) sont plus flexibles mais imposent une longueur maximale rigide, généralement 512 jetons.
- RoPE applique une rotation aux vecteurs et et rend l'attention relative aux distances entre jetons, ce qui autorise l'extension propre à des contextes longs.
Module suivant : les connexions résiduelles et la normalisation par couche, sans lesquelles un Transformer profond ne s'entraîne pas.