Transformer : Guide complet — Architecture Transformer et Self-Attention
Résumé — Le Transformer, introduit par Vaswani et al. dans l’article Attention Is All You Need (2017), est une architecture de réseau de neurones qui a révolutionné le traitement des séquences. Contrairement aux RNN et LSTM qui traitent les données séquentiellement, le Transformer utilise exclusivement des mécanismes d’attention pour connecter chaque élément de la séquence à tous les autres. Cette architecture est à la base de BERT, GPT, T5 et de tous les grands modèles de langage modernes.
Principe mathématique
1. Scaled Dot-Product Attention
L’attention fondamentale repose sur la formule Query-Key-Value :
$$\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{Q K^T}{\sqrt{d_k}}\right) V$$
- Q (Query) : ce que je cherche
- K (Key) : ce que chaque élément peut offrir
- V (Value) : le contenu réel de chaque élément
- $\sqrt{d_k}$ : facteur d’échelle pour éviter les softmax saturés lorsque $d_k$ est grand
Le produit $QK^\top$ calcule la similarité entre chaque query et chaque key. Le softmax transforme ces scores en poids de distribution. Enfin, ces poids pondèrent les values pour produire la sortie.
2. Multi-Head Attention
Au lieu d’une seule attention, on utilise plusieurs têtes en parallèle :
$$\text{MultiHead}(Q, K, V) = \text{Concat}(\text{head}_1, …, \text{head}_h) W^O$$
$$\text{head}_i = \text{Attention}(Q W_i^Q, K W_i^K, V W_i^V)$$
Chaque tête apprend un aspect différent des relations (syntaxe, sémantique, dépendance à longue distance, etc.) et la concaténation fusionne ces perspectives.
3. Positional Encoding
L’attention n’a pas de notion de position intrinsèque. On ajoute un encodage positionnel aux embeddings :
Les dimensions paires utilisent le sinus et les dimensions impaires le cosinus, selon les équations standards :
$$
\mathrm{PE}(pos, 2i) = \sin\left(\frac{pos}{10000^{2i / d_{\text{model}}}}\right)
$$
$$
\mathrm{PE}(pos, 2i+1) = \cos\left(\frac{pos}{10000^{2i / d_{\text{model}}}}\right)
$$
Ces signaux sinusoïdaux permettent au modèle de déduire les positions relatives entre tokens grâce aux propriétés des fonctions trigonométriques.
4. Architecture Encoder-Decoder
Encoder : $N$ couches identiques, chacune composée de :
1. Multi-Head Self-Attention
2. Add & Norm ($x + \text{Sublayer}(x)$ puis LayerNorm)
3. Feed-Forward : $\text{FFN}(x) = \max(0, x W_1 + b_1) W_2 + b_2$
4. Add & Norm
Decoder : $N$ couches identiques avec une couche supplémentaire :
1. Multi-Head Self-Attention (avec masque causal pour empêcher la fuite d’information future)
2. Multi-Head Cross-Attention (requêtes du décodeur, clés/valeurs de l’encoder)
3. Feed-Forward
4. Add & Norm à chaque étape
5. Variantes architecturales
Le Transformer original est encoder-decoder, mais deux familles dérivées dominent aujourd’hui :
| Architecture | Couches | Usage | Exemples |
|---|---|---|---|
| Encoder-only | Encoder uniquement | Compréhension (classification, NER) | BERT, RoBERTa, DeBERTa |
| Decoder-only | Décodeur uniquement | Génération de texte | GPT, LLaMA, Claude |
| Encoder-Decoder | Les deux | Traduction, résumé | T5, BART, mBART |
Intuition
Avant les Transformers, les RNN et LSTM lisaient un texte mot par mot, comme un lecteur lent qui avance lettre par lettre. Pour comprendre la fin d’une longue phrase, ils devaient se souvenir du début à travers leur état caché, ce qui causait des pertes d’information.
Le Transformer, lui, lit toute la phrase en même temps.
Pensez à la différence entre :
– Lire une phrase en la découvrant lettre par lettre à travers un trou d’épingle (RNN)
– Voir toute la phrase d’un coup et comprendre instantanément les liens entre les mots (Transformer)
Dans la phrase « Le chat que le chien poursuivait depuis ce matin s’est réfugié sur l’arbre le plus proche », pour comprendre à quoi se réfère « s’est réfugié », il faut remonter jusqu’à « chat ». Le RNN doit parcourir 14 mots séquentiellement pour faire ce lien. Le Transformer connecte directement « s’est réfugié » à « chat » en un seul calcul d’attention, indépendamment de la distance.
De plus, comme tous les tokens sont traités en parallèle, le Transformer est massivement plus rapide à entraîner sur GPU que les RNN.
Implémentation Python
1. Scaled Dot-Product Attention from scratch
import numpy as np
def scaled_dot_product_attention(Q, K, V, mask=None):
"""Attention(Q, K, V) = softmax(Q @ K^T / sqrt(d_k)) @ V"""
d_k = Q.shape[-1]
scores = Q @ K.T / np.sqrt(d_k) # [seq_len, seq_len]
if mask is not None:
scores = np.where(mask == 0, -1e9, scores)
scores = scores - np.max(scores, axis=-1, keepdims=True)
weights = np.exp(scores) / np.sum(np.exp(scores), axis=-1, keepdims=True)
output = weights @ V
return output, weights
# Exemple d’utilisation
d_k = 64
Q = np.random.randn(10, d_k) # 10 tokens, 64 dimensions
K = np.random.randn(10, d_k)
V = np.random.randn(10, d_k)
output, attn_weights = scaled_dot_product_attention(Q, K, V)
print(f"Attention weights shape: {attn_weights.shape}") # (10, 10)
2. Positional Encoding
def positional_encoding(max_len, d_model):
"""Positional encoding sinusoidal."""
pe = np.zeros((max_len, d_model))
position = np.arange(0, max_len)[:, np.newaxis]
div_term = np.exp(np.arange(0, d_model, 2) * -(np.log(10000.0) / d_model))
pe[:, 0::2] = np.sin(position * div_term)
pe[:, 1::2] = np.cos(position * div_term)
return pe
# Visualisation
pe = positional_encoding(100, 512)
print(f"Positional encoding: {pe.shape}") # (100, 512)
3. Transformer Encoder Layer complète
import numpy as np
class TransformerEncoderLayer:
def __init__(self, d_model, n_heads, d_ff, dropout=0.1):
self.d_model = d_model
self.n_heads = n_heads
self.d_ff = d_ff
self.head_dim = d_model // n_heads
self.dropout = dropout
# Projections pour multi-head attention
self.W_Q = np.random.randn(d_model, d_model) * 0.01
self.W_K = np.random.randn(d_model, d_model) * 0.01
self.W_V = np.random.randn(d_model, d_model) * 0.01
self.W_O = np.random.randn(d_model, d_model) * 0.01
# Feed-forward
self.W_1 = np.random.randn(d_model, d_ff) * 0.01
self.b_1 = np.zeros(d_ff)
self.W_2 = np.random.randn(d_ff, d_model) * 0.01
self.b_2 = np.zeros(d_model)
def layer_norm(self, x):
mean = np.mean(x, axis=-1, keepdims=True)
var = np.var(x, axis=-1, keepdims=True)
return (x - mean) / np.sqrt(var + 1e-8)
def multi_head_attention(self, Q, K, V, mask=None):
batch_size = Q.shape[0]
seq_len = Q.shape[1]
Q_h = (Q @ self.W_Q).reshape(batch_size, seq_len, self.n_heads, self.head_dim)
K_h = (K @ self.W_K).reshape(batch_size, seq_len, self.n_heads, self.head_dim)
V_h = (V @ self.W_V).reshape(batch_size, seq_len, self.n_heads, self.head_dim)
Q_h = Q_h.transpose(0, 2, 1, 3)
K_h = K_h.transpose(0, 2, 1, 3)
V_h = V_h.transpose(0, 2, 1, 3)
scores = Q_h @ K_h.transpose(0, 1, 3, 2) / np.sqrt(self.head_dim)
if mask is not None:
scores = np.where(mask == 0, -1e9, scores)
weights = np.exp(scores - np.max(scores, axis=-1, keepdims=True))
weights = weights / np.sum(weights, axis=-1, keepdims=True)
heads = weights @ V_h
heads = heads.transpose(0, 2, 1, 3).reshape(batch_size, seq_len, self.d_model)
return heads @ self.W_O
def feed_forward(self, x):
"""FFN(x) = ReLU(x @ W_1 + b_1) @ W_2 + b_2"""
return np.maximum(0, x @ self.W_1 + self.b_1) @ self.W_2 + self.b_2
def __call__(self, x, mask=None):
attn_out = self.multi_head_attention(x, x, x, mask)
x = self.layer_norm(x + attn_out)
ff_out = self.feed_forward(x)
x = self.layer_norm(x + ff_out)
return x
# Démonstration
encoder = TransformerEncoderLayer(256, 8, 1024)
x = np.random.randn(2, 20, 256)
out = encoder(x)
print(f"Encoder output: {out.shape}")
4. Transformer complet avec Keras
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
class TransformerBlock(layers.Layer):
def __init__(self, embed_dim, num_heads, ff_dim, rate=0.1):
super().__init__()
self.att = layers.MultiHeadAttention(
num_heads=num_heads, key_dim=embed_dim // num_heads
)
self.ffn = keras.Sequential([layers.Dense(ff_dim, activation='relu'), layers.Dense(embed_dim)])
self.layernorm1 = layers.LayerNormalization(epsilon=1e-6)
self.layernorm2 = layers.LayerNormalization(epsilon=1e-6)
self.dropout1 = layers.Dropout(rate)
self.dropout2 = layers.Dropout(rate)
def call(self, inputs, training=False):
attn = self.att(inputs, inputs)
attn = self.dropout1(attn, training=training)
out1 = self.layernorm1(inputs + attn)
ffn = self.ffn(out1)
ffn = self.dropout2(ffn, training=training)
return self.layernorm2(out1 + ffn)
# Modèle complet pour classification de texte
vocab_size, max_len, embed_dim = 10000, 200, 256
num_heads, ff_dim, n_layers = 8, 1024, 4
inputs = keras.layers.Input(shape=(max_len,))
x = layers.Embedding(vocab_size, embed_dim)(inputs)
positions = tf.range(start=0, limit=max_len, delta=1)
x += layers.Embedding(max_len, embed_dim)(positions)
for _ in range(n_layers):
x = TransformerBlock(embed_dim, num_heads, ff_dim)(x)
x = layers.GlobalAveragePooling1D()(x)
outputs = layers.Dense(1, activation='sigmoid')(x)
model = keras.Model(inputs, outputs)
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
Hyperparamètres
| Hyperparamètre | Paper originale | Moderne (LLM) | Description |
|---|---|---|---|
| $d_{model}$ | 512 | 4096-8192 | Dimension des embeddings |
| $n_{\text{heads}}$ | 8 | 32-64 | Nombre de têtes d’attention |
| $n_{\text{layers}}$ | 6 (enc) + 6 (dec) | 32-128 | Nombre de couches empilées |
| $d_{ff}$ | 2048 | 16384 | Dimension du feed-forward |
dropout |
0.1 | 0.0-0.1 | Régularisation |
| $L_{\text{max}}$ | 512 | 4096-128000 | Longueur maximale de la séquence |
Avantages du Transformer
- Parallélisation massive : Contrairement aux RNN qui traitent les tokens un par un, le Transformer traite toute la séquence en parallèle. L’entraînement est 5 à 10 fois plus rapide sur GPU.
- Dépendances longue distance : La distance entre deux tokens n’affecte pas le nombre d’opérations pour les connecter.
- Universalité : La même architecture fonctionne pour la traduction, la classification, la génération, la vision (ViT), l’audio (Whisper) et même les données tabulaires.
- Scalabilité : Les performances continuent de s’améliorer avec plus de données, plus de paramètres et plus de compute. C’est cette propriété qui a permis l’échelle des LLMs modernes.
Limites du Transformer
- Complexité quadratique : L’attention coûte $O(n^2)$ en mémoire et calcul par rapport à la longueur de séquence. Pour des séquences de 100 000 tokens, cela devient prohibitif.
- Consommation énergétique : Entraîner un Transformer de 175 milliards de paramètres consomme autant d’énergie que des centaines de foyers pendant un an.
- Besoin colossal en données : Les Transformers atteignent leur plein potentiel uniquement avec des datasets de milliards de tokens.
- Interprétabilité : Les poids d’attention ne correspondent pas toujours aux dépendances linguistiques réelles du modèle.
4 cas d’usage concrets
1. Traduction automatique (Google Translate)
Le Transformer original a été conçu pour la traduction anglais-allemand et anglais-français. L’encodeur lit la phrase source, le décodeur génère la phrase cible token par token avec un masque causal. Les résultats ont surpassé tous les systèmes basés sur RNN et sont toujours utilisés dans les moteurs de traduction modernes.
2. Classification de documents juridiques
Un cabinet d’avocats utilise un Transformer encoder-only (comme BERT ou DeBERTa) pour classifier automatiquement des milliers de contrats par type (NDA, bail, contrat de travail) et extraire des clauses spécifiques. Le fine-tuning ne nécessite que quelques centaines d’exemples annotés.
3. Modèle de langage génératif (GPT)
Les modèles de type GPT utilisent un Transformer decoder-only entraîné sur des billions de tokens pour prédire le token suivant. La génération autoregressive produit du texte cohérent sur des milliers de mots, capable de rédiger, traduire, coder et raisonner.
4. Vision par Transformer (ViT)
Le Vision Transformer (ViT) applique l’architecture Transformer aux images en les découpant en patches (comme des tokens visuels). Un modèle ViT pré-entraîné sur ImageNet atteint des performances comparables aux CNNs de pointe, avec l’avantage d’une meilleure scalabilité à grande échelle.
Conclusion
Le Transformer est incontestablement l’architecture la plus influente du machine learning de cette décennie. En remplaçant la récurrence par l’attention, il a permis un bond en avant dans presque tous les domaines du traitement des séquences. De BERT à GPT, de la traduction automatique aux modèles de langage génératifs, cette architecture unique a démontré une capacité de généralisation sans précédent.
Même si la recherche explore désormais des alternatives (Mamba, RWKV, architectures à état linéaire) pour réduire la complexité quadratique, le Transformer reste le standard de l’industrie et continuera de dominer pour les années à venir.
Voir aussi
- Python : Apprendre à utiliser pandas
- Créer des Ellipses Croisées en Python : Guide Complet pour la Visualisation et la Manipulation

