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 chose.
Un bloc est l’unité qu’on empile pour faire un . Chaque bloc fait deux choses, dans l’ordre :
- — laisser chaque regarder tous les autres .
- Réseau () — traiter la représentation de chaque indépendamment.
Les deux sont emballés dans la machinerie + du chapitre 9. La sortie du bloc a la même forme que son entrée, et c’est précisément ce qui permet d’empiler des blocs identiques.
Ce chapitre a trois cellules exécutables : construire le , assembler un bloc, puis empiler deux blocs et ajouter une unembedding pour obtenir une passe avant complète. Ensuite tu créeras le premier llm/model.py de ton dépôt local. Il restera minuscule et surtout occupé à vérifier des formes, mais il ressemblera enfin à un .
Même phrase jouet qu’aux chapitres 8 et 9.
d_model = 8,n_heads = 4,ffn_hidden = 16,n_blocks = 2,vocab_size = 5. Minuscule — mais l’architecture est la même que celle de GPT.
1. Le réseau feed-forward
Chaque bloc contient un par , qui tourne indépendamment sur la représentation de chacun. Il a deux couches linéaires séparées par une non-linéarité :
La dimension cachée ffn_hidden vaut d’habitude 4 × d_model (le a donc bien plus de que la sous-couche d’). La non-linéarité est la descendante lisse de qu’utilisent les modernes ; on te la fournit.
Point crucial : le ne laisse pas les se voir entre eux. Le seul mélange entre dans un bloc , c’est l’. Le rôle du est de réfléchir à ce que l’ est allée chercher.
À toi de jouer · JavaScript
La sortie a la même forme que l’entrée — toujours [seq_len × d_model]. C’est le contrat que respecte chaque sous-couche d’un .
2. Le bloc complet
Compose maintenant l’ et le avec la recette pre-norm :
Le chapitre câble l’ et le avec leurs poids déjà liés ; la cellule montre directement la composition.
À toi de jouer · JavaScript
C’est tout le bloc . S’il fonctionne, c’est grâce à la combinaison de trois propriétés : le flux garde une échelle constante ( + addition), l’ laisse l’information circuler entre positions, et le laisse chaque position réfléchir à cette information de son côté.
3. Empiler les blocs + unembedding
L’architecture n’est que N blocs à la suite, avec une finale et une matrice d’unembedding qui projette le dernier état caché vers des sur le .
La sortie est [seq_len × vocab_size]. Une ligne de par position d’entrée. Pour la autorégressive, on depuis le de la dernière ligne.
À toi de jouer · JavaScript
Ça, c’est un . Le modèle a des poids aléatoires, donc le graphe en barres du bas ne veut rien dire — mais la forme est la bonne. Un vrai , c’est cette même architecture avec quelques corrections (, encodage positionnel, des nombres beaucoup plus grands) entraînée sur des milliards de de texte.
Ce qu’on a laissé de côté
Un vrai aurait aussi :
- Des de . On est parti de
Xcomme s’il était déjà embarqué. Un vrai modèle commence par aller chercher la ligne de chaque dans une matrice d’. - Un encodage positionnel (ou RoPE). L’ est équivariante par permutation : sans information de position, le modèle ne distingue pas
the cat satdesat cat the. Les vrais modèles ajoutent ou entrelacent un signal de position. - Le . Dans un décodeur seul (façon GPT), le i n’a pas le droit de regarder les j > i pendant l’. On l’implémente en mettant les scores des positions futures à −∞ avant le .
- Du pendant l’.
- Des de LayerNorm (γ, β) appris, et non figés à 0 et 1.
La plupart de ces ajouts tiennent en une ligne. Aucun ne change la forme de l’architecture. Ce que tu viens de construire est le cœur ; le reste est de la plomberie.
4. Créer ton 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)Ce squelette est une carte du modèle complet :
- [1]
feed_forwardapplique le même à chaque ligne de . Il ne mélange pas les positions ; l’ l’a déjà fait. - [2] ouvre le bloc par l’ pre-norm : normaliser d’abord, puis router l’information entre .
- [3] réinjecte la mise à jour de l’ dans le flux .
- [4] répète le même motif avec le réseau : normaliser, transformer, réinjecter.
- [5]
logitsconvertit les vecteurs cachés en scores sur le . Une ligne de veut dire « les scores de chaque suivant possible, à cette position ».
Ce fichier est volontairement incomplet : pas d’initialisation apprise, pas d’, pas de . Son rôle est de rendre l’architecture concrète avant que la version PyTorch n’en fasse quelque chose de rapide et d’entraînable.
Recap
- Le est un par : linéaire → → linéaire. Sa couche cachée est plus large (d’habitude 4 × d_model).
- Le bloc = sous-couche d’ + sous-couche , toutes deux en pre-norm + . La sortie a la même forme que l’entrée.
- Un = N blocs à la suite + finale + unembedding. La sortie, ce sont des
[seq_len × vocab_size]. - Ton projet local a maintenant
llm/model.py, le premier squelette en forme de GPT. - L’invariant du bloc — forme d’entrée = forme de sortie — est ce qui permet d’en empiler autant qu’on veut. Un modèle moderne en a des dizaines.
- , encodage positionnel, vrais , sont tous des ajouts d’une ligne par-dessus cet échafaudage. Le squelette architectural, c’est ce que tu viens d’écrire.
Pour aller plus loin
- Vaswani et al., « Attention Is All You Need » (2017). Le papier complet du .
- « Let’s build GPT from scratch » de Karpathy — construit le même modèle de bout en bout en PyTorch.
- The Annotated Transformer — implémentation ligne par ligne du papier, avec chaque modification explicitée.
- Step by Token, chapitre 5 traite le sous l’angle de la compréhension.
Prochaine étape : c’est la fin de la partie III. La partie IV commence avec préparer un dataset — ton projet local existe déjà, il est temps de lui donner de vraies données.