- Key idea
- x ← x + Attn(LN(x)); x ← x + MLP(LN(x)); stack N times; count parameters ≈ 12·N·d² + vocab·d
- Real world
- An assembly line of identical workstations. Each station has the same two tools, a “look around” tool and a “think about it” tool, and adds its work to the product on the belt. A bigger factory just has more stations and wider belts.
- In an LLM
- GPT-2 small is 12 identical blocks with width 768: 124 million parameters. Llama 3 70B is 80 blocks with width 8,192: 70.6 billion. Same recipe, different sizes.
One block, start to finish
You have met every part. A pre-norm , as used by GPT-2 and Llama, is:
where is the whole sequence, one row per token. Six steps:
- LayerNorm each token’s row (a copy; itself is kept). Normalization.
- Attention on the normalised copy: queries, keys, values, scores, mask, softmax, mix, then an output matrix that maps the heads’ results back to the model width. Queries, keys and values, multi-head.
- Add the attention output to . The residual stream.
- LayerNorm again.
- MLP on each token separately: widen, activate, narrow. The MLP.
- Add the MLP output.
The shape on the main path never changes: tokens × numbers in, out. That is what lets blocks stack.
The toy uses width 4 so you can read every number. GPT-2 small does the same with width 768, 12 heads of size 64 and an MLP of width 3072, in each of its 12 blocks.
The whole model
A full decoder-only transformer is:
- Embed: look up each token’s row in the embedding table, add position information. Embeddings, positions.
- Blocks: run the matrix through identical blocks (each with its own weights).
- Final norm and unembed: normalise, multiply by the output matrix to get one score per vocabulary token. Unembedding.
| Model | Blocks | Width | Heads | MLP width | Vocabulary |
|---|---|---|---|---|---|
| GPT-2 small | 12 | 768 | 12 | 3,072 | 50,257 |
| GPT-2 XL | 48 | 1,600 | 25 | 6,400 | 50,257 |
| Llama 3 8B | 32 | 4,096 | 32 (8 KV) | 14,336 | 128,256 |
| Llama 3 70B | 80 | 8,192 | 64 (8 KV) | 28,672 | 128,256 |
Counting the parameters
Every parameter lives in one of five places: the embedding table, the attention matrices, the MLP matrices, the norms, and (unless it is tied to the embedding) the output matrix. With full multi-head attention and a 4× MLP, each block has about weights, so
For GPT-2 small: in the blocks plus in the embeddings, 123.5M; the biases, norms and position table bring it to exactly 124,439,808. Try the presets:
Every preset matches the published checkpoint to the last parameter. Two things to notice:
- Blocks grow with , embeddings with . In GPT-2 small the token and position tables are 32% of the model; in Llama 3 70B the embedding and output matrices together are only 3%. Small models spend a surprising share on their vocabulary.
- Bytes follow directly. Stored in bf16 (2 bytes per parameter) Llama 3 8B needs about 16 GB just for its weights, before the KV cache and activations. How much memory a model needs works through this, and how quantization shrinks it.
A car factory has a line of identical workstations. At each one, a worker looks at the car and at the cars next to it on the line (attention), thinks about what this car needs (the MLP), and bolts on a part (the add). Nothing is ever removed. A bigger factory doesn’t invent new kinds of station; it builds more of them and makes the line wider. Doubling the width of every station roughly quadruples the cost of each one, which is the .
GPT-2 small (2019) and Llama 3 70B (2024) are built from the same block. Llama swaps LayerNorm for RMSNorm, GELU for SwiGLU, learned positions for RoPE, and shares keys and values across groups of heads. It is 6.7 times deeper and 10.7 times wider, and 10.7² × 6.7 ≈ 760: almost all of the 567-fold growth in parameters (124M to 70.6B) comes from width squared times depth.
What one block costs to run
Each parameter in a block is used once per token in one multiply and one add, so a forward pass costs about floating-point operations per token, plus the attention scores that grow with the context length. What a forward pass costs works this out properly.
The complete count
With query heads, key–value heads, head size , MLP width and vocabulary :
Check it yourself
from transformers import AutoModelForCausalLM
m = AutoModelForCausalLM.from_pretrained("gpt2")
print(sum(p.numel() for p in m.parameters())) # 124439808 (the tied output matrix is counted once)In papers
- The original Transformer: Vaswani et al., 2017.
- GPT-2 (pre-norm, learned positions): Radford et al., 2019.
- Llama 3 (RMSNorm, SwiGLU, RoPE, GQA): Grattafiori et al., 2024.
- Parameter and compute counts: Kaplan et al., 2020, section 2.1.