One line of code, and all the consequences that descend from it. The chapter that ties the whole course together.
We have reached the point the course had been preparing for since chapter 00. Here it is, in full, the architectural difference between GPT-2 and RoBERTa:
if causal: att = att.masked_fill(futuro, float("-inf"))
That's it. Everything else is the same or almost the same: the same Transformer block, the same attention equation, the same tokenization algorithm (byte-level BPE), the same GELU, the same LayerNorm, practically identical sizes (124 M vs 125 M, both with 12 layers, 768 dimensions and 12 heads).
gpt2 and roberta-base are structural twins that do opposite things. It is the perfect controlled experiment, and that is why studying them together teaches more than studying either one alone.
This is not an argument — it is a measurement. In lab 04 you run two sentences that are identical up to the middle and different at the end, and measure how much the representation at each position changed:
A: "The cat sat on the mat and slept peacefully all afternoon"
B: "The cat sat on the mat and screamed furiously all afternoon"
^^^^^^^^^^^^^^^^ only this changes
GPT-2 (causal) RoBERTa (bidirectional)
pos token |diff| pos token |diff|
0 'The' 0.00e+00 0 '<s>' 3.31e-01
1 'cat' 0.00e+00 1 'The' 3.66e-01
2 'sat' 0.00e+00 2 'cat' 2.88e-01
3 'on' 0.00e+00 3 'sat' 3.91e-01
4 'the' 0.00e+00 4 'on' 3.40e-01
5 'mat' 0.00e+00 5 'the' 3.01e-01
6 'and' 0.00e+00 6 'mat' 2.59e-01
7 'slept' 7.86e+00 <- changes 7 'and' 3.36e-01
8 ... 1.30e+01 8 'slept' 3.97e-01
...
12 '</s>' 3.27e-01
ZERO up to the swapped word. EVERYTHING changes, even <s>.
The past is untouchable. One word rewrites the sentence.
It is not approximately zero. It is exactly 0.00e+00, bit for bit. The future does not exist for the past in a causal model. And in RoBERTa, swapping one word at the end alters even the opening token.
Everything that follows descends from that line. Follow the chain:
The decoder trades understanding for generation. The encoder trades generation for understanding. There is no free lunch: it is the same architecture, with a minus-infinity sign deciding which way it leans.
| Task | Best family | Why |
|---|---|---|
| Writing, completing, conversing | decoder | only it generates |
| Classifying (spam, sentiment, topic) | encoder | bidirectional is more accurate; much cheaper |
| Semantic search / embeddings | encoder | quality sentence vector, a single pass |
| Extracting entities (NER) | encoder | label per token, context from both sides |
| Extractive answering (finding the span) | encoder | mark start and end in the text |
| Generative answering (writing the answer) | decoder | needs to produce new text |
| Translation, summarization | encoder-decoder | T5, BART — read everything and write everything |
| Detecting machine text | encoder | it is classification — our chapter 12 |
With the success of ChatGPT, it became common sense that "decoders won". That is half true, and the other half matters to anyone building systems.
For classifying, a 125 M RoBERTa-base is still the right choice in production: it runs in milliseconds on a CPU, costs almost nothing, and frequently beats a giant LLM on the specific task it was fine-tuned for. Calling a 70-billion-parameter model to decide whether an e-mail is spam is waste by several orders of magnitude.
And for semantic search, RAG systems commonly use an encoder to produce the embeddings, often derived from BERT or RoBERTa. The decoder writes the answer; the encoder finds what to read.
The practical division, today: encoders find and classify, decoders write.
There is a third arrangement, worth knowing to complete the map. The original 2017 Transformer was like this, and T5 and BART follow the same design:
It is the best of both worlds, at the cost of roughly twice the model. Ideal when input and output are distinct objects — translating from Portuguese to English, summarizing an article. Less useful for open dialogue, where the "input" and the "output" are the same continuous stream of conversation — which helps explain why the world of assistants converged on pure decoders.
Transformer (2017)
encoder + decoder
|
+------------+------------+
| |
DECODER only ENCODER only
(with causal mask) (without causal mask)
| |
GPT-1 (2018) BERT (2018)
117M, BooksCorpus MLM + NSP, WordPiece
| |
GPT-2 (2019) RoBERTa (2019)
124M-1.5B, WebText dynamic MLM, no NSP,
Pre-LN, byte BPE GPT-2's byte BPE, 160GB
| |
GPT-3 (2020) +--> roberta-*-openai-detector
175B, few-shot binary classifier
| trained on GPT-2's outputs
InstructGPT / ChatGPT ^
fine-tuning + RLHF |
| |
modern LLMs -------- generate the text -----+
the detector judges
Note how the drawing closes: the two branches, separated in 2018 by one line of code, meet again in the detector. OpenAI built the hunter with the architecture opposite to the prey's. That is chapter 12.
.venv\Scripts\python.exe lab\04_multihead_mask.py (section 3 — the proof) .venv\Scripts\python.exe lab\10_roberta_mlm.py (the comparison)
Answer without looking anything up. If you get stuck on one, go back to the chapter indicated.
roberta-base has 125 M and gpt2 has 124 M. Where does that 1 million difference come from? (chapters 01 and 02)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.
Chapter 12 closes Part III with the most curious model in the collection: the detector.
And it brings an uncomfortable answer to question 3 of Exercise 0.