The MLP (multilayer perceptron) language model of section 5 reads a fixed window of tokens and treats each position with its own weights. The transformer (Vaswani et al., 2017) replaces that with attention: every token looks at every earlier token and decides, from their content, how much of each to take.
This page builds, in order: attention; its masked and multi-head forms; the rotary position encoding and grouped-query attention used by current open models; and finally the full transformer block. Each part has a small example with numbers.
Attention: a soft lookup
Think of a dictionary lookup: you have a question, you compare it with the labels of the entries, and you read the entry whose label matches. Attention does the same thing, but softly: it compares the question with every label, and reads a weighted mix of all the entries, with more weight where the match is better.
Each token produces three vectors from its embedding, with three learned linear layers:
- a query : what it is looking for (the question);
- a key : what it offers (the label);
- a value : what it passes on (the content of the entry).
The output for a token is a weighted average of all the values, where each weight says how well the token's query matches that key:
where:
- stacks the queries as rows, shape for tokens; stacks the keys, shape ; stacks the values, shape ;
- is the table of all dot products between queries and keys ( is transposed): entry measures how well query matches key ;
- holds these scores, divided by (explained below);
- the softmax is taken along each row, turning the scores of one query into weights that add up to 1;
- multiplying by takes, for each query, the weighted average of the values.
Example. Three tokens, , with queries and keys equal for simplicity: token 0 has , token 1 has , token 2 has . Values: , , .
- Scores for token 0: its query against the three keys gives dot products ; divided by : .
- Weights: softmax of the scores, . Token 0 matches tokens 0 and 2 best.
- Output: .
For token 1 the weights are and the output : it takes more of , whose key matches its query.
Bahdanau, Cho and Bengio (2015) introduced attention for translation; the transformer made it the only way tokens interact.
Why divide by . If the entries of and are independent, with mean 0 and variance 1, their dot product is a sum of products, and its variance is . With the scores have a standard deviation of about 11. Softmax of numbers that far apart puts almost all the weight on one token, and its gradients become tiny, so the model stops learning which tokens to attend to. Dividing by brings the variance back to 1 (Vaswani et al., section 3.2.1).
Example. In one random draw of 8 scores with standard deviation 11, the largest softmax weight is : all the attention goes to one token. The same scores divided by give a largest weight of : the attention is spread over several tokens.
Causal masking
A language model must not see the future: the prediction at position may use tokens up to only. Otherwise, during training, it could simply read the answer. A causal mask sets every score above the diagonal to before the softmax, so those weights become exactly 0:
where is the position of the query and the position of the key. Since , a token gets no weight from any later token.
Example. The same three tokens. The scores, with the mask applied:
query key 0 key 1 key 2 token 0 0.71 token 1 0 0.71 token 2 0.71 0.71 1.41 After the softmax, the weights of the three rows are , and . Token 0 can only look at itself, so its output is exactly . Token 2, the last one, sees everything, as before. The allowed cells form a lower triangle.
This is what makes training efficient: one forward pass over a sequence of length computes all next-token predictions at once, each one seeing only its own past.
Multi-head attention
One attention pattern per layer is limiting: a token may need to look at the previous word for grammar, and at a name ten tokens back for meaning. Multi-head attention (MHA) runs several attentions side by side, each with its own queries, keys and values. It splits the model dimension into heads of size , runs attention in each head independently, places the results side by side and mixes them with an output projection :
where holds the input vectors, is the output of attention in head , and the square brackets mean "place side by side".
Example. and heads of size 4. Each token's query vector of 8 numbers is read as two queries of 4 numbers: the first 4 for head 0, the last 4 for head 1. Each head computes its own scores and weights, and returns 4 numbers per token. Side by side they make 8 numbers again, and , an matrix, mixes them.
In code there is no loop over heads: project once to shape , reshape to , transpose to , run batched attention, then undo the transpose and the reshape. The einsum exercise of the NumPy section is exactly this shape work.
Position: rotary embeddings
Attention by itself ignores order: shuffle the tokens, and each output is shuffled the same way. "dog bites man" and "man bites dog" would look the same. The original transformer added fixed sine and cosine waves to the embeddings. RoPE (rotary position embedding; Su et al., 2024) instead rotates the queries and keys, by an angle that grows with the position.
The numbers of a vector are grouped into pairs, and each pair is treated as a point in the plane. Pair of the vector at position is rotated by the angle :
where:
- and are the two numbers of pair , and the rotated ones;
- is the token's position: ;
- is the rotation speed of pair : the first pairs turn fast, the last ones slowly.
Example. The pair rotated by : and , so it becomes , the same point turned a quarter circle. Its length does not change, only its direction.
The key property: rotating by and by changes their dot product only through the difference . So attention scores depend on the relative position of two tokens, and nothing is added to the values.
Example. One pair, , query and key ; without rotation, .
- Query at position 3, key at position 1 (distance 2): the score becomes .
- Query at position 7, key at position 5 (distance 2 again): , exactly the same.
- Query at position 5, key at position 7 (distance ): , different.
The score depends on how far apart the two tokens are, not on where they are.
LLaMA (Large Language Model Meta AI) uses RoPE. Implementations differ in how they pair dimensions: the RoFormer paper pairs neighbours , as here, while some libraries pair dimension with . The two are equivalent up to a fixed reordering of the weights.
Multi-query and grouped-query attention
When a model generates text, it keeps the keys and values of every previous token in memory, in a cache (section 11), so that it does not recompute them for every new token. That cache, not the computation, limits how many texts can be generated at once and how long they can be. Fewer key/value heads mean a smaller cache:
- Multi-query attention (Shazeer, 2019) keeps query heads but a single key head and a single value head, shared by all of them.
- GQA (grouped-query attention; Ainslie et al., 2023) is the middle ground: key/value heads, each shared by a group of query heads. Query head uses key/value head , where rounds down.
Example. query heads and key/value heads: each group of query heads shares one key/value head. Query heads 0 to 3 use head 0, query heads 4 to 7 use head 1, and so on: query head 13 uses . The cache stores 8 heads instead of 32: it is 4 times smaller than with full multi-head attention.
The transformer block
A model is a stack of identical blocks. Each block has two sub-layers:
- attention, where tokens exchange information;
- an MLP, where each token is processed on its own.
Each sub-layer is wrapped in a residual connection (He et al., 2016): its output is added to its input, so the block only adds a correction to what it receives:
where holds the token vectors and is LayerNorm (layer normalization) or RMSNorm (root mean square layer normalization) from section 4.
Why add instead of replace? With the addition, the gradient has a direct path from the loss back to the first layers (the derivative of always contains the 1 from ), so even a stack of dozens of blocks still trains. The vector that flows through the stack, collecting all these corrections, is called the residual stream.
Normalizing the input of each sub-layer (pre-norm) rather than its output is what GPT-2 (Generative Pre-trained Transformer 2) did, and Xiong et al. (2020) showed it makes training stable without a long warmup.
The MLP widens each vector and narrows it back. GPT-2 uses , with a hidden size of ; GELU (Gaussian error linear unit) is a smooth version of ReLU (rectified linear unit). LLaMA uses SwiGLU (Swish-gated linear unit; Shazeer, 2020), a gated variant with three matrices:
where:
- and widen the vector, and narrows it back;
- is the SiLU (sigmoid linear unit), times its sigmoid;
- is the elementwise product: the second branch, , works as a gate that scales each entry of the first.
Example. For one entry, if gives and gives : , times the gate , is . A gate of 0 would switch that entry off entirely.
A full model is: token embedding, blocks, a final norm, and a linear layer to logits. Every piece of that line is now an exercise you have written.
References
- A. Vaswani et al., Attention Is All You Need, NeurIPS, 2017. (arXiv)
- D. Bahdanau, K. Cho, Y. Bengio, Neural Machine Translation by Jointly Learning to Align and Translate, ICLR, 2015.
- J. Su, M. Ahmed, Y. Lu, S. Pan, W. Bo, Y. Liu, RoFormer: Enhanced Transformer with Rotary Position Embedding, Neurocomputing 568, 127063, 2024. (arXiv)
- N. Shazeer, Fast Transformer Decoding: One Write-Head is All You Need, arXiv, 2019. Multi-query attention.
- J. Ainslie et al., GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints, EMNLP, 2023.
- K. He, X. Zhang, S. Ren, J. Sun, Deep Residual Learning for Image Recognition, CVPR, 770–778, 2016.
- R. Xiong et al., On Layer Normalization in the Transformer Architecture, ICML, 2020.
- N. Shazeer, GLU Variants Improve Transformer, arXiv, 2020. SwiGLU.
- D. Hendrycks, K. Gimpel, Gaussian Error Linear Units (GELUs), arXiv, 2016.
- A. Radford et al., Language Models are Unsupervised Multitask Learners, OpenAI, 2019. Section 2.3: layer normalization moved to the input of each sub-block.
- H. Touvron et al., LLaMA: Open and Efficient Foundation Language Models, arXiv, 2023. Section 2.2: pre-normalization, SwiGLU, rotary embeddings.