A modern LLM is a stack of identical transformer blocks (decoder-only variety), 30 to 120+ of them. Each block has the same structure:
flowchart TB IN["x (input vector)"] --> N1["Norm"] N1 --> ATT["Attention<br/>tokens talk to each other"] ATT --> ADD1(("+")) IN ==>|"residual shortcut"| ADD1 ADD1 --> N2["Norm"] N2 --> MLP["MLP<br/>each token thinks alone"] MLP --> ADD2(("+")) ADD1 ==>|"residual shortcut"| ADD2 ADD2 --> OUT["x′ (output vector)"]
As equations (this is the “pre-norm” layout most modern models use):
The vector for each token flows up through the blocks, getting refined at each step. Think of the residual stream as a shared bus that each block reads from and writes small updates to.
8a. Self-attention
The important bit
The problem it solves: to understand a token, you often need information from other tokens. In “The trophy didn’t fit in the suitcase because it was too big,” what “it” refers to depends on context. Attention is the mechanism that lets each token gather relevant info from other tokens.
How it works. For every token’s vector , we compute three different vectors using three learned matrices:
| Vector | Name | Think of it as |
|---|---|---|
| query | ”What am I looking for?“ | |
| key | ”What do I contain / advertise?“ | |
| value | ”What info do I hand over if you attend to me?” |
Then for a sequence, stacking all tokens into matrices , , :
Breaking down the attention formula, one symbol at a time
Piece Name What it does , , query, key, value matrices One row per token. tokens → rows K transposed Flip so rows become columns (so the multiply lines up) raw scores Dot product of every query with every key → an grid. Entry = “how relevant is token to token ?“ scale factor is the length of each key vector. Dividing by its square root keeps the scores from getting huge causal mask A grid of s and s that blocks looking at the future normalizer Turns each row of scores into weights between 0 and 1 that sum to 1 weighted average Each token’s output = weighted mix of all the value vectors
Step by step:
flowchart LR X["Token vectors X"] --> QKV["Make Q, K, V<br/>(3 matrix multiplies)"] QKV --> S["Scores<br/>Q·Kᵀ"] S --> SC["Scale<br/>÷ √d_k"] SC --> MK["Mask<br/>hide the future"] MK --> SM["Softmax<br/>rows sum to 1"] SM --> OUT["Weighted sum<br/>of V"]
-
: dot product of every token’s query with every token’s key. This gives an matrix of raw relevance scores.
-
: scaling so the dot products don’t get huge and push softmax into saturated, near-zero-gradient territory.
-
(the causal mask): when predicting the next token, a token may only look at itself and earlier tokens, never the future (otherwise it would just copy the answer during training). Future positions get added so softmax makes them :
-
softmax: turns each row of scores into weights that sum to 1. (Full formula in Sampling.)
-
: each token’s output is the weighted average of all the value vectors, using those weights.
What the causal mask looks like
For the 5-token sentence “The cat sat on mat”, a ✓ means “this row’s token is allowed to look at this column’s token”:
looks at → The cat sat on mat The ✓ · · · · cat ✓ ✓ · · · sat ✓ ✓ ✓ · · on ✓ ✓ ✓ ✓ · mat ✓ ✓ ✓ ✓ ✓ It’s a triangle. Every token sees itself and everything to its left, nothing to its right. The
·cells are the entries in .
So attention is a learned, content-based, soft lookup. Each token asks a question (query), every earlier token offers a key, the best matches contribute their values.
Multi-head attention. Instead of one attention operation, run of them in parallel (e.g. 32 or 64 heads), each with its own smaller . Concatenate the results and mix with another matrix :
Different heads specialize in different things. Researchers have found heads that track syntax, heads that copy the previous occurrence of a token (induction heads, central to in-context learning), heads that attend to the previous token, and so on.
Cost note
The score matrix means attention cost grows quadratically with sequence length ( tokens → scores). That’s the main reason long context is expensive, and why a lot of engineering (FlashAttention, grouped-query attention, sliding windows) exists to cut the cost.
Try it
Watch attention run on real text, step by step, in 3D: Transformer Explainer and LLM Visualization.
8b. The MLP
After attention mixes information between tokens, the MLP (multi-layer perceptron, also called the feed-forward network) processes each token’s vector independently:
| Step | What happens |
|---|---|
| Expand the vector to a larger dimension (typically ~4× wider) | |
| Apply the non-linearity (see Common activation functions) | |
| Project back down to the original size |
A gated variant like SwiGLU has a similar total size.
If attention is “communication between tokens,” the MLP is “computation within a token.” A big chunk of the model’s factual knowledge appears to be stored in these layers, acting like a fuzzy key-value memory. About two thirds of a transformer’s parameters live in the MLPs.
8c. Residual connections and normalization
Residual connection: each sublayer’s output is added to its input rather than replacing it:
This lets gradients flow straight back through the network during backprop (fixing the vanishing gradient problem that made deep nets impractical), and it makes each layer an incremental refinement instead of a total rewrite.
Normalization (LayerNorm / RMSNorm): rescales the vector at various points to keep numbers in a stable range. Without it, activations blow up or shrink across dozens of layers and training falls apart. Modern models usually apply it before each sublayer (“pre-norm”).
RMSNorm formula, broken down
Piece Meaning Square every entry, then add them all up Divide by the count → the mean of the squares Square root → the “typical size” (root mean square, hence RMS) Tiny number so we never divide by zero Divide each entry by the typical size → the vector now has a standard scale A learned per-entry gain, so the model can re-scale if it wants
← 7. Embeddings · Contents · 9. Stacking it up and producing output →