The full assembly of the pieces — and an honest measurement of what changes from 124 million to 1.5 billion parameters.
You already know all the pieces. Here is the assembly, with nothing omitted:
TEXT "The capital of France is" | | BPE tokenizer (chapter 01) — not part of the model v IDs [464, 3139, 286, 4881, 318] (5,) | | wte[IDs] + wpe[0..4] (chapter 02) v h_0 (5, 768) | +-- BLOCK 1 ---+ . | x = x + attn(ln_1(x)) communication (chapters 03, 04) . | x = x + mlp (ln_2(x)) computation (chapter 05) . +---------------+ . | . | ... 12 identical blocks, shape always (5, 768) ... . | . +-- BLOCK 12 ---+ | v h_12 (5, 768) | | ln_f — final LayerNorm (exists only because of Pre-LN) v | @ wte.T — weight tying (chapter 02, section 6) v LOGITS (5, 50257) | | we take ONLY the last row: the prediction of the next token v logits[-1] (50257,) | | softmax + sampling strategy (chapter 07) v NEXT TOKEN -> goes back to the top, now with 6 tokens
For a 5-token input, GPT-2 returns 5 predictions, one per position: "what comes after The", "what comes after The capital", and so on. All 5 are computed, always.
In training, all 5 are used — that is what makes pre-training so efficient (chapter 08). In generation, 4 are thrown away and only the last one matters. That "waste" is exactly what the KV cache recovers.
The four GPT-2s are the same model. No new piece, no different trick. Only three numbers change.
| Model | Layers | dmodel | Heads | dk | MLP | Parameters | fp32 |
|---|---|---|---|---|---|---|---|
gpt2 | 12 | 768 | 12 | 64 | 3072 | 124 M | 0.5 GB |
gpt2-medium | 24 | 1024 | 16 | 64 | 4096 | 355 M | 1.4 GB |
gpt2-large | 36 | 1280 | 20 | 64 | 5120 | 774 M | 3.0 GB |
gpt2-xl | 48 | 1600 | 25 | 64 | 6400 | 1558 M | 6.0 GB |
They all share: a vocabulary of 50,257, a context of 1024, byte-level BPE, Pre-LN, GELU, 4× expansion in the MLP, weight tying. The training was the same too — the same WebText, the same 40 GB of text. The only experimental variable was size.
That was, in fact, the thesis of the GPT-2 paper ("Language Models are Unsupervised Multitask Learners", 2019): keep everything fixed and grow, to show that new capabilities emerge from scale alone.
The term that matters is
The intuitive answer ("bigger models write better") does not hold up in the lab.
In this course's initial test, I asked the same thing of all four models, in greedy mode (always the most likely word). The prompt was "The capital of France is":
| Model | Parameters | Generated continuation | Correct? |
|---|---|---|---|
openai-gpt | 117 M | "a great city , and the city is a great city ." | no |
gpt2 | 124 M | "the capital of the French Republic, and the capital of the" | no |
gpt2-medium | 355 M | "Paris, and the capital of France is Paris." | yes |
gpt2-large | 774 M | "the capital of France.\n\nThe capital of France is" | no |
gpt2-xl | 1558 M | "the city of Paris. It is the capital of France and" | yes |
The large (774 M) got wrong a question the medium (355 M) got right. That contradicts the intuition that "more parameters = better", and it is a real result, reproducible on your machine.
Two things are going on. First, greedy is a fragile measure: it takes the most likely word and never backtracks, so a near-tie on the first token decides everything. Second — and more important — a single sample does not measure capability. To say anything serious about a model, you have to measure a distribution over many examples. That is exactly what perplexity does, and it is the subject of chapter 08.
Keep this as mental hygiene: when someone shows you a screenshot of a model getting a question right or wrong, you are looking at a sample of size 1.
When you measure properly, over many examples, three things improve consistently with scale:
The same prompt as in the previous table, now with three examples before it:
Portugal -> Lisbon Japan -> Tokyo Italy -> Rome France ->
| Model | No examples | With 3 examples | Gain | 2nd and 3rd place |
|---|---|---|---|---|
gpt2 | 0.032 | 0.385 | 12× | " Marse", " Nice" |
gpt2-medium | 0.174 | 0.871 | 5× | " Marse", " Lyon" |
gpt2-xl | 0.086 | 0.904 | 10× | " Lyon", " Nice" |
Look at the last column. The runners-up are Marseille, Lyon, Nice — other French cities. The model did not merely get it right: it understood that the task was "country → city of that country" and started erring within the right category. That is the pattern being captured, not a memorized correspondence.
And note: no weight was updated. The only difference between 3.2% and 38.5% was the text that came before the question. Taken to GPT-3 (175 billion parameters), this capability became so strong that it made fine-tuning unnecessary for most tasks — and it is the reason "prompt engineering" became a discipline.
wpe, not the capability.On an ordinary 8-core desktop CPU, in fp32 and without a GPU, measured in practice:
| Model | RAM required | Load from disk | Generation |
|---|---|---|---|
gpt2 | ~0.6 GB | ~1 s | ~40 tok/s |
gpt2-medium | ~1.6 GB | ~2 s | ~15 tok/s |
gpt2-large | ~3.3 GB | ~4 s | ~7 tok/s |
gpt2-xl | ~6.5 GB | ~8 s | ~3 tok/s |
The rule of thumb: fp32 costs 4 bytes per parameter. A 1.5 B model takes up 6 GB in weights alone. That is why quantization exists — in 8 bits, the same model fits in 1.5 GB, and in 4 bits, in 800 MB. Quality drops a little and the practical reach changes completely.
.venv\Scripts\python.exe lab\06_scale.py
config.json, and verifies the parameter-count formula against the true number.large "almost" got it right.gpt2-medium (gpt2-xl has 12.5× the parameters of gpt2. Does it generate 12.5× slower? Measure and explain the difference.The primary sources for the concepts in this chapter. The numbers in the text were measured in the lab; the ideas come from here.
Technical terms here carry their machine-learning meaning. The course glossary shows that meaning next to the others the same words have in IT.
The model produces a probability distribution over 50,257 tokens. Turning that into text is your decision, not its.
Chapter 07 shows that this decision changes the result more than switching models does.