Module 08 · Part II · The Transformer
Three Families: Encoder, Encoder-Decoder, Decoder
The 2017 Encoder-Decoder and Cross-Attention, BERT, T5, Decoder-Only Models and the Causal Mask, and Where the Normalization Goes
By the end you can choose the right family for a task and explain why decoder-only won for generation.
Check yourself ▸
Before you start, can you already…
- ☐trace one token through a full transformer and name every tensor's shape along the way. (Module 7)
If any is shaky, the linked module teaches it.
The block of Module 7 can be wired into three kinds of model. An encoder lets every token see the whole input, which suits understanding text. A decoder lets each token see only what came before, which suits writing it. The 2017 Transformer used both, joined by cross-attention. This module covers the three families, why decoder-only won for language models, and a small wiring choice, where the normalization goes, that decides whether deep models train at all.
8.1 The 2017 Encoder-Decoder and Cross-Attention
The 2017 Transformer was built for translation: an encoder reads the source sentence and a decoder writes the translation one token at a time. Each is a stack of six identical layers:
In words. Six encoder layers represent the source, each token seeing the whole sentence. In each of six decoder layers, a target token first attends to the target tokens before it, then across to the encoder's output. Each "Add & LayerNorm" adds a sub-layer's input to its output and normalizes.
Cross-Attention: Shapes and Mechanics
Cross-attention is how the decoder reads the source. Queries come from the decoder's vectors ( target tokens), keys and values from the encoder's output ( source tokens):
The weights form an table, one row per target token and one column per source token, each entry between 0 and 1:
There is no causal mask here. The decoder's self-attention must hide future target tokens, but the source is complete before decoding starts, so every target token may attend to every source token.
8.2 Encoder-Only: BERT and Masked Language ModelingRun this section
BERT (Devlin et al., 2019) keeps only the encoder, with no causal mask, and uses a different self-supervised task: hide about 15% of the tokens and predict them from everything else. With the hidden positions and the text with them hidden:
Of the chosen tokens, 80% become a special [MASK] token, 10% a random token and 10% stay unchanged, so the model cannot learn that only [MASK] positions matter. Seeing both sides makes encoders strong at classification, search, tagging and embeddings, but they cannot generate text left to right, since nothing trains them to predict from the past alone. Encoders reappear wherever a model only has to read: image encoders in Module 25 and text recognizers in Module 27.
8.3 Encoder-Decoder After 2017: T5
T5 (Raffel et al., 2020) treats every task as text in, text out: translation, summarization, classification (the label written as a word) and question answering share one model and one loss.
Its pretraining task, span corruption, sits between BERT's blanks and next-token prediction: random spans are each replaced by one sentinel token, and the decoder writes the missing spans in order:
- input:
Thank you <X> me to your party <Y> week. - target:
<X> for inviting <Y> last <Z>
Encoder-decoders remain strong when input and output are different kinds of sequence and the whole input is available up front: translation, speech recognition (Whisper is one) and summarization. But they spend parameters on both halves, and decoder-only models proved simpler to scale and just as capable once large, which is why this course's language models are decoder-only.
| family | attention inside the model | typical pretraining task | good at |
|---|---|---|---|
| encoder-only (BERT) | every token sees every token | fill in masked tokens | classification, search, embeddings |
| encoder-decoder (T5) | encoder: all; decoder: causal + cross | fill in masked spans | translation, summarization, speech |
| decoder-only (GPT) | causal | predict the next token | generation, chat, general use |
8.4 Decoder-Only: Predicting the Next TokenRun this section
The GPT models keep only the decoder: a stack of masked self-attention blocks trained to predict the next token. GPT-1 (2018), GPT-2 (2019) and GPT-3 (2020) showed that this design keeps improving with size and data, and most large language models since, including Meta's LLaMA family, use it.
By the chain rule of probability (section 3.3), the probability of a text of tokens is a product of next-token probabilities:
means "multiply these together". So a model that answers "what is the next token, given everything so far?" can score any text and generate text of any length. Training maximizes the likelihood of real text (section 3.4) by minimizing the negative log-likelihood (NLL):
means all tokens before position . This is the cross-entropy loss of section 3.5 applied at every position, with the gradient of section 4.3, and is the perplexity.
In words. The whole sequence goes in at once, the mask hides each position's future, and the loss is computed at every position in one pass. Training is parallel even though generation is sequential.
The Causal Attention Mask Matrix
In training the model sees all tokens at once, but position predicts token and must not see it, or it could copy the answer. The causal mask of section 6.4 prevents this (row is the looking token, column the token looked at):
Added inside the softmax, as in Module 6:
every future position gets exactly zero weight:
( means "for all".) Each row's remaining weights still sum to 1. So one parallel pass gives every position the view it would have during generation, and a 1,024-token training sequence yields 1,024 predictions and loss terms from a single forward pass.
Rows are queries, columns are keys. Shaded cells are allowed (0 in the mask); blank cells are blocked (−∞). Hover or tap a row.
Switch to the causal mask and hover the third row. Before you do, say how many keys it can see, and how many next-token predictions one 6-token training sequence gives.
In words. An attention mask as a grid, queries as rows and keys as columns, in three settings: bidirectional (encoder), causal (decoder) and cross-attention. Hovering a row highlights the keys it can see and, for the causal mask, names the token that position predicts.
8.5 Where the Norm Goes: Post-LN and Pre-LNRun this section
LayerNorm can go in one of two places around each residual addition. The 2017 Transformer put it after, Post-LN:
is attention or the feed-forward network, and counts sub-layers, not blocks. GPT-2 (2019) moved LayerNorm to each sub-layer's input, Pre-LN, as GPT-3 and LLaMA do (LLaMA with RMSNorm, section 15.3):
Pre-LN never normalizes the main path, so it adds one final LayerNorm, ln_f, before the output layer. Xiong et al. (2020) explained why Pre-LN trains more easily: in Post-LN the gradients near the output are large at the start, so the learning rate must be ramped up slowly (a warmup); Pre-LN can often train without one.
In the reference implementation.
src/transformer/transformer_block.pyuses the Post-LN wiring from 2017, so on this point it differs from GPT-2. Lab 3b runs the two wirings against each other.
In words. Post-LN normalizes the sum, so the normalization sits on the main path. Pre-LN normalizes only the copy entering the sub-layer, leaving the main path a plain addition.
Mathematical Proof of Gradient Highway in Pre-LN
In Pre-LN nothing on the residual stream is normalized, so unrolling across sub-layers gives the input plus every layer's contribution:
Differentiate with respect to ; the first term gives the identity :
depends on no layer's weights, so part of the gradient always reaches the bottom unscaled. In the RNN of section 5.2 the gradient is a product of per-step factors; here the layer terms are added to , not multiplied into it. Post-LN has no such clean term, because the gradient passes back through a LayerNorm at every layer.
Caveat. The term protects only the residual stream, the direct route from to . The other terms in the sum each pass through a LayerNorm and a sub-layer, and those still rescale the gradient.
Measured (lab 3b,
experiments/ab_layernorm.py). The same 49,792 parameters, wired two ways, give outputs that differ by at initialization. The identity path shows up as a gradual trend, not a sudden break. As depth grows from 4 to 32 layers, the gradient reaching the embeddings changes by a factor of under Post-LN and grows by a factor of under Pre-LN, so the ratio between the two widens from to .
Measured (lab 3b). At this size the effect is modest: at 32 layers Post-LN's gradient is 2.5 times smaller than Pre-LN's, not gone. A 600-step training comparison at two layers separates the two wirings by validation loss, which is within the run-to-run noise of a single seed.
Exercises
Which family would you choose for (a) classifying support tickets into ten categories, (b) translating documents, (c) a chat assistant? Give one reason each.
Write out the causal mask for four tokens and say how many entries are .
A decoder-only model is trained on one sequence of 2,048 tokens. How many next-token predictions contribute to the loss in that single forward pass?
Unroll the Pre-LN update over three sub-layers and write in terms of .
BERT replaces only 80% of its chosen tokens with [MASK], 10% with random tokens and leaves 10% unchanged. What would go wrong if it used [MASK] for all of them?
Core Academic References
- Vaswani et al. (2017): Attention Is All You Need. NeurIPS 2017. arXiv:1706.03762
- Devlin et al. (2019): BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding. NAACL 2019. arXiv:1810.04805
- Raffel et al. (2020): Exploring the Limits of Transfer Learning with a Unified Text-to-Text Transformer (T5). JMLR 21(140): 1–67. arXiv:1910.10683
- Radford et al., OpenAI (2018): Improving Language Understanding by Generative Pre-Training (GPT-1). Technical Report.
- Radford et al., OpenAI (2019): Language Models are Unsupervised Multitask Learners (GPT-2). Technical Report.
- Radford et al., OpenAI (2023): Robust Speech Recognition via Large-Scale Weak Supervision (Whisper). ICML 2023. arXiv:2212.04356
- Xiong et al. (2020): On Layer Normalization in the Transformer Architecture (Pre-LN analysis). ICML 2020. arXiv:2002.04745
Measured labs
These labs test what this module says by running real code. Each card states the idea, what the experiment found, and what it does not prove. The numbers come from experiments/results.json, the output of running experiments/run_all.sh on the tiny-transformers reference code at version 137f7ef. The project checks fail if a measured number in the module text stops matching that file.
Lab 3b — where LayerNorm goes (Post-LN vs Pre-LN), up to 32 layers
What the module says
Section 8.5 says Pre-LN, which normalizes the input of each sub-layer, is the modern standard. Its argument is that the residual connections then give the gradient a direct identity path, , so it reaches the first layer without shrinking.
What the experiment found
The repository's transformer_block.py is Post-LN: it normalizes after each residual addition, as the original 2017 Transformer did. The two wirings have the same 49,792 parameters per block, yet with identical weights their outputs differ by 0.83 at initialization. The identity path shows up as a trend, not a cliff. From 4 to 32 layers the gradient reaching the embeddings stays flat under Post-LN (×0.97) and grows under Pre-LN (×1.62), so Pre-LN's advantage widens from 1.5× to 2.5×.
- what the code uses
- post_ln
- what section 8.5 recommends
- pre_ln
- output difference at initialization
- 0.8295
- Pre-LN ÷ Post-LN gradient, 4 32 layers
- 1.504x → 2.503x
- validation loss after 600 steps
- 2.071 vs 2.0556
same weights, different order of operations
gradient size at the embeddings
Post-LN vs Pre-LN, 2 layers; lower is better
What this does not prove
The effect is real but mild. On a model this small, Post-LN at 32 layers gets 2.5 times less gradient than Pre-LN, not zero: the direction the module predicts holds, the size of the effect does not reach "vanishing". Also, the identity path protects the residual stream, not the weights inside each sub-layer. History: Post-LN is the 2017 original; GPT-2 (2019) already normalized sub-layer inputs, and Xiong et al. (2020) explained why that trains more stably.
Run it yourself
cd tiny-transformers && .venv/bin/python experiments/ab_layernorm.pyThe code being tested