Chapitre 10 · 16 min
Le bloc transformer complet
Assemble attention, résidus et couches feed-forward dans le premier squelette de modèle local proche de GPT.
Tu as toutes les pièces. Assemblons la vraie unité.
Un bloc transformer est l’unité qu’on empile pour faire un transformer. Chaque bloc fait deux choses :
- Attention multi-têtes : chaque token peut regarder les autres.
- Réseau feed-forward (FFN) : chaque représentation de token est traitée indépendamment. Les deux sont enveloppés dans la mécanique résiduelle + LayerNorm du chapitre 9. La sortie garde la même forme que l’entrée, ce qui permet d’empiler des blocs identiques.
1. Le réseau feed-forward
Chaque bloc contient un MLP par token :
La dimension cachée est souvent 4× d_model. Le FFN ne mélange pas les tokens entre eux ; seule l’attention fait ça. Son rôle est de réfléchir à ce que l’attention a ramené.
Code · JavaScript
La sortie a la même forme que l’entrée : [seq_len × d_model].
2. Le bloc complet
On compose attention et FFN avec le pattern pre-norm :
Code · JavaScript
C’est le bloc transformer entier. Il marche parce que le flux résiduel garde une échelle stable, l’attention route l’information entre positions, et le FFN traite chaque position.
3. Empiler les blocs + unembedding
L’architecture transformer est simplement N blocs à la suite, une LayerNorm finale, puis une matrice d’unembedding qui projette vers les logits du vocabulaire.
Code · JavaScript
C’est un transformer. Les poids sont aléatoires, donc les logits n’ont pas encore de sens, mais la forme est bonne. Un vrai LLM est cette architecture avec masquage causal, embeddings positionnels, nombres beaucoup plus grands et entraînement massif.
Ce qu’on a sauté
Un vrai transformer ajoute aussi :
- Token embeddings : ici on partait de
Xdéjà embedded. - Position encoding ou RoPE : sans position, l’attention ne connaît pas l’ordre.
- Masquage causal : GPT ne peut pas regarder les tokens futurs.
- Dropout pendant l’entraînement.
- Paramètres de LayerNorm appris.
Ce sont des ajouts importants, mais ils ne changent pas le squelette.
4. Créer le premier squelette de modèle
Crée llm/model.py :
"""A tiny GPT-shaped model skeleton.
Chapter 12 replaces the list math with PyTorch tensors. The architecture stays:
token embedding, position embedding, transformer blocks, final logits.
"""
from __future__ import annotations
from llm.attention import Matrix, causal_attention, matmul
from llm.nn import add, layer_norm, linear, relu
# [1]
def feed_forward(x: Matrix, w1: Matrix, b1: list[float], w2: Matrix, b2: list[float]) -> Matrix:
return [linear(relu(linear(row, w1, b1)), w2, b2) for row in x]
def transformer_block(
x: Matrix,
wq: Matrix,
wk: Matrix,
wv: Matrix,
ffn_w1: Matrix,
ffn_b1: list[float],
ffn_w2: Matrix,
ffn_b2: list[float],
) -> Matrix:
# [2]
attended = causal_attention(layer_norm(x), wq, wk, wv)
# [3]
x = add(x, attended)
# [4]
return add(x, feed_forward(layer_norm(x), ffn_w1, ffn_b1, ffn_w2, ffn_b2))
# [5]
def logits(hidden: Matrix, unembed: Matrix) -> Matrix:
return matmul(layer_norm(hidden), unembed)- [1]
feed_forwardapplique le même MLP à chaque token. - [2] commence par attention pre-norm.
- [3] ajoute l’update attentionnelle au flux résiduel.
- [4] répète le motif avec le FFN.
- [5]
logitsconvertit les vecteurs cachés en scores de vocabulaire.
Ce fichier n’est pas encore un modèle entraînable. Son rôle est de rendre l’architecture concrète avant la version PyTorch.
Recap
- Le FFN est un MLP par token : linéaire → GELU → linéaire. - Le bloc = attention + FFN,
tous deux pre-norm + résiduel. - Un transformer = N blocs + LayerNorm finale + unembedding. -
Ton projet local a maintenant
llm/model.py. - L’invariant input shape = output shape permet d’empiler autant de blocs que nécessaire.
Pour aller plus loin
- Vaswani et al., “Attention Is All You Need”.
- Karpathy, “Let’s build GPT from scratch”.
- The Annotated Transformer.
- Step by Token, chapitre 5.
Prochaine étape : fin de la partie III. La partie IV commence avec préparer un dataset.