A Plain-English Guide to Transformer Architecture A developer published a plain-English guide to transformer architecture, explaining how large language models like GPT-4, Claude, Gemini, and Llama process text from tokenization to autoregressive generation. The guide covers key concepts such as self-attention, multi-head attention, training via backpropagation, and inference economics, with interactive tools linked inline. A from-scratch walkthrough of what happens between typing a prompt and getting a response — no ML background assumed. Every large language model — GPT-4, Claude, Gemini, Llama — is built on the same core idea. This guide follows that idea in the exact order data actually flows through a real model: text in, tokens, vectors, 80 stacked layers, and a word out — then loops back to explain how the model got trained in the first place. Read it top to bottom; each section is built to need only what came before it. The One Job an LLM Has https://gist.github.com/starred.atom 1-the-one-job-an-llm-has Vectors — Turning Words Into Math https://gist.github.com/starred.atom 2-vectors--turning-words-into-math The Full Pipeline at a Glance https://gist.github.com/starred.atom 3-the-full-pipeline-at-a-glance Tokenization https://gist.github.com/starred.atom 4-tokenization The Embedding Layer https://gist.github.com/starred.atom 5-the-embedding-layer Positional Encoding — Teaching the Model Word Order https://gist.github.com/starred.atom 6-positional-encoding--teaching-the-model-word-order Self-Attention — The Core Idea https://gist.github.com/starred.atom 7-self-attention--the-core-idea Self-Attention — The Mechanics Q, K, V https://gist.github.com/starred.atom 8-self-attention--the-mechanics-q-k-v Multi-Head Attention https://gist.github.com/starred.atom 9-multi-head-attention Inside One Transformer Block — Causal Masking, Residuals, LayerNorm & the Feed-Forward Network https://gist.github.com/starred.atom 10-inside-one-transformer-block--causal-masking-residuals-layernorm--the-feed-forward-network Why the Middle of a Long Prompt Gets Ignored https://gist.github.com/starred.atom 11-why-the-middle-of-a-long-prompt-gets-ignored Autoregressive Generation — How Words Actually Get Produced https://gist.github.com/starred.atom 12-autoregressive-generation--how-words-actually-get-produced The KV Cache — Why Generation Isn't Recomputed From Scratch https://gist.github.com/starred.atom 13-the-kv-cache--why-generation-isnt-recomputed-from-scratch Temperature & Sampling — Controlling Randomness https://gist.github.com/starred.atom 14-temperature--sampling--controlling-randomness How Training Actually Updates the Weights — Backpropagation & Gradient Descent https://gist.github.com/starred.atom 15-how-training-actually-updates-the-weights--backpropagation--gradient-descent Why Hallucination Happens — Architecturally https://gist.github.com/starred.atom 16-why-hallucination-happens--architecturally From Raw Model to Helpful Assistant SFT & RLHF https://gist.github.com/starred.atom 17-from-raw-model-to-helpful-assistant-sft--rlhf Model Scale & Emergent Abilities https://gist.github.com/starred.atom 18-model-scale--emergent-abilities Embeddings — The Geometry of Meaning https://gist.github.com/starred.atom 19-embeddings--the-geometry-of-meaning Inference Economics — Why LLMs Cost What They Cost https://gist.github.com/starred.atom 20-inference-economics--why-llms-cost-what-they-cost Glossary — Every Term in One Place https://gist.github.com/starred.atom 21-glossary--every-term-in-one-place Three free, interactive tools are linked inline below, right in the sections they visualize best, rather than bundled at the end — so you can pull one up the moment it's relevant instead of hunting for it later. In one line:an LLM is a single next-word predictor, run over and over — nothing else is happening underneath the hood. Strip away the hype, and every LLM is trained to do exactly one thing: Given some text, predict the most likely next word. That's it. There's no search engine inside, no database of facts, no reasoning module bolted on. The entire architecture — every layer, every mechanism described below — exists purely to answer one question, over and over, one word at a time: "Given what came before, what word comes next?" Everything from writing an essay to solving a math problem to holding a conversation is this same mechanism, repeated thousands of times in a row. Every section below is either part of how that one prediction gets made, or part of how the model got good at making it — keep that map in mind as you go. In one line:every word becomes a list of numbers, and similar words end up as similar-looking lists — that single trick is what everything else in this guide is built on top of. A vector is just a list of numbers. Nothing mystical about it. "cat" → 0.2, 0.8, -0.1, 0.5, 0.3, ... "dog" → 0.3, 0.7, -0.2, 0.4, 0.3, ... "car" → 0.9, -0.1, 0.8, -0.3, 0.1, ... In real models these lists have 512, 1024, or even 4096 numbers. Each number is called a dimension . The key insight that makes all of this work: meaning becomes geometry . Words with similar meaning end up with similar number patterns — so "cat" and "dog" land close together in this number-space, while "car" lands far away. You can literally do arithmetic on meaning this way more on that in section 19 https://gist.github.com/starred.atom 19-embeddings--the-geometry-of-meaning . For now, just accept one fact: every word gets converted into a vector before any computation happens. The model never touches raw text — only numbers. The next section shows the full journey these vectors take from raw text to a finished word. In one line:text becomes token IDs, token IDs become vectors, 80 stacked layers refine those vectors by having tokens talk to each other, and the last vector gets turned into a probability over every possible next word — then the whole thing repeats for the next word. We'll use one running example throughout this guide: Input: "The cat sat on the" Goal: predict → "mat" Here's the entire journey from text to output, every step numbered — including the two loops running at the same time layers stacked sequentially, heads running in parallel inside each layer and the outer autoregressive loop that wraps around the whole thing. Every numbered step here gets its own dedicated section later in this guide. ╔════════════════════════════════════════════════════════════════════╗ ║ TRANSFORMER ARCHITECTURE — ONE FULL GENERATION STEP ║ ╚════════════════════════════════════════════════════════════════════╝ STEP 1 · INPUT TEXT "The cat sat on the" │ ▼ STEP 2 · TOKENIZER text → integer IDs 464, 3797, 3332, 319, 262 │ ▼ STEP 3 · EMBEDDING LOOKUP each ID → vector e.g. 4096 numbers 5 tokens → 5 vectors │ ▼ STEP 4 · + POSITIONAL ENCODING position info added to each vector so the model knows token order │ ▼ ┌──────────────────────────────────────────────────────────────────┐ │ STEP 5 · TRANSFORMER LAYER STACK ↻ × 80 LAYERS, SEQUENTIAL │ │ layer 1's output → layer 2's input → ... → layer 80's output │ │ │ │ ┌────────────────────────────────────────────────────────────┐ │ │ │ ONE LAYER = │ │ │ │ │ │ │ │ 5a · MULTI-HEAD SELF-ATTENTION │ │ │ │ ┌──────┐ ┌──────┐ ┌──────┐ ↻ × 32 HEADS, │ │ │ │ │Head 1│ │Head 2│ │Head 3│ ... PARALLEL, inside THIS │ │ │ │ └──────┘ └──────┘ └──────┘ one layer only │ │ │ │ each head: Q,K,V → attention scores → weighted sum of V │ │ │ │ causal mask applied — no token attends to future tokens │ │ │ │ │ concatenate all 32 heads' outputs │ │ │ │ ▼ │ │ │ │ 5b · ADD & NORM residual connection + layer norm │ │ │ │ │ │ │ │ │ ▼ │ │ │ │ 5c · FEED-FORWARD NETWORK per-token transformation │ │ │ │ │ │ │ │ │ ▼ │ │ │ │ 5d · ADD & NORM residual connection + layer norm │ │ │ └───────────────────────┬────────────────────────────────────┘ │ │ │ output feeds into next layer as input │ │ ▼ │ │ ↻ repeat steps 5a–5d, 80 times total │ └──────────────────────────────────────────────────────────────────┘ │ ▼ STEP 6 · OUTPUT LINEAR LAYER last token's final vector × weight matrix → 100,000 raw NUMBERS logits — NOT percentages yet │ ▼ STEP 7 · TEMPERATURE SCALING logit ÷ temperature → adjusted logits still raw numbers │ ▼ STEP 8 · SOFTMAX adjusted logits → 100,000 PERCENTAGES probabilities, sum = 100% │ ▼ STEP 9 · SAMPLE pick one token from the probability distribution → e.g. "mat" │ ▼ STEP 10 · STOP CHECK EOS token? max tokens hit? stop sequence matched? │ NO │ YES ▼ ▼ STEP 11 · APPEND TOKEN STOP — return final text new token added to the sequence │ └───────────────↻ LOOP BACK TO STEP 5 sequence is now one token longer — a full forward pass runs again from step 5 Legend — what each loop means: ↻ × 80 LAYERS → sequential, stacked depth-wise layer 1 must finish before layer 2 starts this is "how deep" the network is ↻ × 32 HEADS → parallel, inside a single layer only all 32 run simultaneously, then merge this is "how many relationship types" one layer can detect at once ↻ OUTER LOOP → the autoregressive generation loop steps 5 through 11 repeat once per generated token, until stop One-line description per step: 1 text arrives as-is, human readable 2 text split into tokens, each mapped to an integer ID 3 each ID looked up in embedding table → vector 4 position information injected so order is not lost 5 80 layers refine the representation, each layer's 32 heads simultaneously detecting different relationships 6 final vector converted to one raw score per vocabulary word 7 temperature reshapes those raw scores before normalizing 8 raw scores converted into a real probability distribution 9 one token drawn from that distribution 10 check whether generation should stop here 11 if not stopping, append the token and repeat from step 5 See it move.Static diagrams only go so far.renders this bbycroft.net/llm exactpipeline as an animated, rotatable 3D model of a real, runnable GPT nano-GPT, GPT-2, and others — tokens flowing into embeddings, splitting into attention heads, merging back through Add & Norm, expanding through the feed-forward network, and collapsing down to logits at the output layer, all in one structure you can orbit around. It's the single best way toseethe shape of the steps above instead of just reading them. Worth pulling up now, before diving into the details, and again after finishing section 10 once every piece has a name. The rest of this guide walks through each of these steps in detail — starting with tokenization step 2 . In one line:text gets chopped into word-ish chunks and each chunk is swapped for a lookup number — step 2 of the pipeline above, and the very first thing that happens to your prompt. The model doesn't read text — it reads numbers. A tokenizer splits text into chunks called tokens roughly one word or word-piece and maps each token to an integer ID from a fixed vocabulary. "The cat sat on the" ↓ ↓ ↓ ↓ ↓ 464 3797 3332 319 262 These IDs are just lookup indices — no meaning yet. Meaning arrives in the next step. Vocabulary size varies by model: GPT-4 → ~100,000 tokens Claude → ~100,000 tokens Llama 3 → ~128,000 tokens Tokens aren't always whole words — rare or long words get split into sub-word pieces: "cat" → 1 token common word, gets its own ID "sat" → 1 token "Kathmandu" → 2 tokens less common, gets split "unprecedented" → 3 tokens long word, split into sub-pieces "धन्यवाद" → 4 tokens non-Latin script, more splits This is exactly why non-English text often costs more per word when you're billed by the token — tokenizers are trained mostly on English data, so other languages get sliced into more pieces. With text turned into IDs, the next step is turning those IDs into the vectors introduced in section 2. In one line:each token ID gets swapped for its learned vector — step 3 of the pipeline — and from this point on the model only ever works with numbers, never text again. Every integer ID gets looked up in a giant table to retrieve its vector. This table is learned during training. 464 → 0.12, 0.87, -0.34, 0.56, 0.01, ... ← "The" 3797 → 0.23, 0.91, -0.12, 0.44, 0.32, ... ← "cat" 3332 → -0.45, 0.34, 0.78, -0.23, 0.67, ... ← "sat" 319 → 0.67, -0.12, 0.34, 0.89, -0.45, ... ← "on" 262 → 0.11, 0.88, -0.33, 0.54, 0.02, ... ← "the" From this point forward, the model never sees the original text again — only these vectors. But these raw vectors are missing one thing: any sense of where each token sits in the sentence. That's step 4 of the pipeline, and the very next section. In one line:a "which position am I" signal gets mixed into every token's vector right here, immediately after the embedding lookup — step 4 of the pipeline — because the comparison math used in the next few sections has no built-in concept of order on its own. Section 5 mentioned that positional information gets added right after the embedding lookup. This section explains why that step exists and how it works — before diving into self-attention itself in sections 7 through 9. The next few sections explain self-attention — the mechanism that lets tokens exchange context by comparing their vectors mathematically. The one fact you need up front: that comparison is pure arithmetic, and pure arithmetic has no built-in concept of order. Sentence 1: "cat sat" Sentence 2: "sat cat" Without positional information, the vector for "cat" is identical in both sentences. The model can't tell which came first. But "dog bites man" and "man bites dog" contain the same words with entirely different meanings — the model needs position. Before the first transformer block runs, a positional vector is added to each token's embedding: token embedding positional vector final input vector "cat" 0.23, 0.91 + position 2 0.10, 0.30 = 0.33, 1.21 "sat" -0.45, 0.34 + position 3 0.20, 0.40 = -0.25, 0.74 Now the same word at different positions produces different final vectors — this is exactly the vector that gets handed to attention in the next section. Sinusoidal the original 2017 Transformer paper — position vectors computed from sine/cosine functions at different frequencies. Fixed, not learned. Doesn't generalize well beyond the training sequence length. Learned positional embeddings GPT-2 — a lookup table learned during training, one vector per position, up to a maximum length. Position 1 → 0.12, -0.34, 0.56, ... Position 2 → 0.45, 0.23, -0.12, ... ... Position 4096 → ... ← hard limit, nothing beyond this RoPE — Rotary Position Embeddings Llama and most modern models — instead of adding a position vector, RoPE rotates the Query and Key vectors introduced in the next section by an angle based on position, right before they get compared. Relative position gets baked directly into the attention score itself. RoPE generalizes far better beyond training length, which is why models can later be extended to much longer context windows than they were originally trained on: Original training context: 4,096 tokens With RoPE + fine-tuning: can extend to 128k, 200k, 1M tokens Without RoPE: hard ceiling at training length This is the mechanical reason Claude can handle 200k tokens of context and Gemini 1.5 Pro handles 1M. Learned embeddings: position vectors only exist up to position N; anything beyond is undefined territory for the model RoPE: angle rotation becomes extreme at very long distances; attention scores degrade past a point Both approaches: attention cost is O N² — quadratic. N = 10,000 → 100 million dot products N = 100,000 → 10 billion dot products Context windows aren't just a pricing decision — they're a real architectural and hardware constraint, since doubling context length quadruples the attention computation. With position now baked into every vector, the model is finally ready for the main event: self-attention. In one line:every token looks at every other token in the sentence and decides how much of its meaning to borrow from each one — the single mechanism that replaced older, sequential models and made modern LLMs possible. This is the single most important idea in modern AI. Let's build the intuition before touching any math. Consider this sentence: "The animal didn't cross the street because it was too tired." What does "it" refer to? The animal, not the street. You resolved that by connecting "it" back to "animal" across seven words. That connection between two distant parts of a sentence is called a long-range dependency . More examples: "The trophy didn't fit in the suitcase because it was too big." ↑ what does "it" mean here? → "trophy" far back in the sentence "The bank by the river where they used to fish before the factory was built in 1987 might close." ↑ "bank" = river bank or financial bank? → resolved by "river", 3 words away Human brains resolve these instantly. A model needs an explicit mechanism to do it — that mechanism is self-attention. Before transformers, language models used RNNs Recurrent Neural Networks , which process text one word at a time, carrying forward a single "memory" vector: token 1 → hidden state h1 ↓ token 2 → hidden state h2 ← h2 is computed from h1 + token 2 ↓ token 3 → hidden state h3 ← h3 is computed from h2 + token 3 ↓ ... and so on That hidden state is a single fixed-size vector, overwritten at every step. Imagine reading a 300-page book but you're only allowed one sticky note — every new sentence forces you to erase your note and rewrite a fresh summary. By page 300, almost nothing from page 1 survives. "The cat that had been sitting on the mat every morning since it was a kitten and had never left the house was hungry." RNN at "was hungry": hidden state ≈ ...stuff from recent tokens... nothing from "cat"... By token 501, information from token 1 has been diluted across 500 rewrites. The connection is essentially lost. Self-attention abandons the "carry a summary forward" idea entirely. Instead, every token looks directly at every other token, simultaneously , and decides: "how relevant is each other token to understanding me right now?" "The cat sat on the" T1 T2 T3 T4 T5 When processing T2 "cat" : - How much should I look at T1 "The" ? → some - How much should I look at T3 "sat" ? → a lot cat is doing the sitting - How much should I look at T4 "on" ? → a little - How much should I look at T5 "the" ? → almost nothing Result: T2's vector gets updated with relevant information from all other tokens This is direct, parallel communication between any two tokens — regardless of distance. Token 1 and token 500 communicate with equal ease. No bottleneck, no dilution. The exact mechanics of how a token decides "how much to look at" another token is the subject of the next section. In one line:every token computes three vectors — a question Query , an advertisement Key , and a contribution Value — and attention is just "compare every Query to every Key, then blend Values by how well they matched." Section 7 gave the intuition — every token looks at every other token. This section gives the actual mechanism behind that: how does a token decide how much to borrow from each other token? SELF-ATTENTION INPUT: 5 vectors, one per token already carrying position info from section 6 "The" → 0.12, 0.87, -0.34, 0.56 "cat" → 0.23, 0.91, -0.12, 0.44 "sat" → -0.45, 0.34, 0.78, -0.23 "on" → 0.67, -0.12, 0.34, 0.89 "the" → 0.11, 0.88, -0.33, 0.54 using 4 numbers per vector here for simplicity — real models use up to 4096 SELF-ATTENTION OUTPUT: 5 vectors, one per token — same count, same size — but now each vector carries information borrowed from other tokens "The" → updated — now knows it precedes "cat sat on the mat" "cat" → updated — now knows cat is the one doing the sitting "sat" → updated — now knows who sat and where "on" → updated — knows it connects action to location "the" → updated — knows it precedes a surface/location Same number of vectors in and out, same dimensions — but richer content. The question is: how does each token decide how much to borrow from each other token? That's exactly what Q, K, V computes. Q, K, V come straight out of information retrieval. Think of a search engine: You type a search query: "best hiking trails Nepal" Each webpage has metadata: title, tags, description Each webpage has content: the actual article text QUERY = what you are searching for KEY = the metadata compared against your query VALUE = the actual content you retrieve if the match is good The search engine compares your Query against every Key, scores relevance, then returns a weighted mix of Values. Self-attention does exactly this — except every token is simultaneously a searcher and a page being searched : Every token asks a question Query : "what context do I need?" Every token advertises itself Key : "here is what I contain" Every token offers content Value : "here is what I give if selected" They aren't separate pieces of data — they're three different transformations of the same token vector , produced by multiplying it by three learned weight matrices . A weight matrix is a grid of numbers the model learns during training. Multiplying a vector by it produces a new vector viewed "through a different lens." There are three such matrices — Wq,Wk,Wv— one each for producing the Query, Key, and Value. For token "cat" with embedding vector E cat: E cat × Wq = Q cat cat's question: "what context do I need?" E cat × Wk = K cat cat's advertisement: "here is what I am about" E cat × Wv = V cat cat's offering: "here is what I contribute" This happens for every token, giving us three sets of five vectors. Step 1 — Score every token against every other token. Focus on token "cat." Its Query gets compared against every token's Key using a dot product — a single number measuring how aligned two vectors are higher = more similar . Q cat = 1.0, 0.5 K The = 0.2, 0.1 → dot product = 1.0×0.2 + 0.5×0.1 = 0.25 K cat = 0.9, 0.8 → dot product = 1.0×0.9 + 0.5×0.8 = 1.30 K sat = 0.8, 0.6 → dot product = 1.0×0.8 + 0.5×0.6 = 1.10 K on = 0.1, 0.3 → dot product = 1.0×0.1 + 0.5×0.3 = 0.25 K the = 0.2, 0.2 → dot product = 1.0×0.2 + 0.5×0.2 = 0.30 Raw attention scores for "cat": The: 0.25, cat: 1.30, sat: 1.10, on: 0.25, the: 0.30 This tracks intuition — "cat" is most relevant to itself, and second most relevant to "sat" the action it performs . Step 2 — Scale the scores. Raw scores are divided by √d k the square root of the Key vector's dimension . With dimensions in the thousands, raw dot products can get huge, which destabilizes the next step. Dividing keeps numbers in a sane range — this is the "scaled" in "scaled dot-product attention." Step 3 — Softmax converts scores into probabilities. Softmax takes a list of numbers and converts them into probabilities — all between 0 and 1, all summing to 1. Raw scores after scaling : 0.25, 1.30, 1.10, 0.25, 0.30 After softmax: 0.08, 0.42, 0.36, 0.08, 0.10 The cat sat on the Reading this: "cat" puts 42% of its attention on itself, 36% on "sat", 10% on "the", 8% each on "The" and "on" Step 4 — Weighted sum of Values. Use those probabilities to blend the Value vectors: Output for "cat" = 0.08 × V The + 0.42 × V cat + 0.36 × V sat + 0.08 × V on + 0.10 × V the The result is a new vector for "cat" that says, in effect: I am cat, mostly defined by what I am and what I do sat , with minor contributions from surrounding context. This computation happens for every token, all at once: HOW MUCH DOES EACH ROW TOKEN ATTEND TO EACH COLUMN TOKEN? "The" "cat" "sat" "on" "the" "The" 0.60 0.20 0.10 0.05 0.05 "cat" 0.08 0.42 0.36 0.08 0.10 "sat" 0.05 0.30 0.45 0.15 0.05 "on" 0.05 0.10 0.35 0.40 0.10 "the" 0.05 0.10 0.25 0.35 0.25 Each row sums to 1.0 softmax guarantees this . Each row is a different token's view of what matters. Self-attention never adds or removes tokens — 5 in, 5 out, always. What changes is the content of each vector: BEFORE self-attention: "cat" vector = 0.23, 0.91, -0.12, 0.44 only the meaning of "cat" in isolation AFTER self-attention: "cat" vector = 0.67, 1.23, 0.45, 0.89 "cat" + borrowed context from "sat" — the model now knows this cat is sitting, not sleeping Real-world analogy: five people enter a meeting room, each carrying their own notes. They talk for an hour. Five people leave — same headcount, but each person's understanding has been updated by what they heard from the others. Nothing was added or removed from the room; what changed is what's now in each person's head. One set of Q, K, V, though, can only pick up on one kind of relationship at a time — the next section explains why models run many of these side by side. In one line:instead of computing attention once per layer, the model computes it 32 or more times in parallel, each copy free to specialize in a different kind of relationship — like several editors reading the same sentence, each looking for something different. One set of Q, K, V can only learn one type of relationship at a time. But language has several relationship types happening simultaneously: "The cat sat on the mat" Relationship type 1 — grammatical subject: "cat" relates to "sat" Relationship type 2 — location: "sat" relates to "mat" Relationship type 3 — article: "The" relates to "cat" Relationship type 4 — preposition link: "on" relates to "mat" A single attention "head" tends to lock onto the most dominant relationship and miss the rest. Multi-head attention runs many independent Q/K/V computations in parallel — each with its own weight matrices, each free to specialize in a different kind of relationship. HEAD 1: Wq1, Wk1, Wv1 → learns grammatical subject-verb relations HEAD 2: Wq2, Wk2, Wv2 → learns positional/location relations HEAD 3: Wq3, Wk3, Wv3 → learns coreference what "it" refers to ... HEAD 32: Wq32, Wk32, Wv32 → learns some other pattern All 32 outputs are concatenated and projected back to the original dimension. GPT-3 uses 96 heads per layer; a typical 7-billion-parameter open model uses 32. Nobody assigns a head its job — specialization emerges from training. With 32+ heads per layer and 80 layers stacked, a model is running thousands of relationship detectors on every token, which is how it eventually learns to resolve something like the "trophy/suitcase/it" example from a handful of training examples that showed the same pattern. Worked example — two heads disagreeing on purpose: Sentence: "The trophy didn't fit in the suitcase because it was too big." HEAD 3 coreference specialist scores "it" against: "trophy" → 0.71 ← wins "suitcase" → 0.19 "big" → 0.05 learned: "it" tends to bind to the earlier physical object when the sentence structure implies containment HEAD 7 recency specialist scores "it" against: "trophy" → 0.15 "suitcase" → 0.62 ← wins it's the nearest noun "big" → 0.10 Final blended vector for "it" leans toward "trophy" because head 3's signal is stronger for THIS sentence structure — but if you swapped "big" for "small," head 3's own internal weighting would flip toward "suitcase" instead. Multiple heads vote; the training data decided which votes to trust in which context. This is also why models occasionally get coreference wrong on unusual sentence structures — if a relationship pattern was rare in training data, no head ever specialized in detecting it well. Attention one head or many is only step 5a of the transformer block from section 3's diagram. The next section covers the rest of that block — 5b through 5d — which is just as necessary for any of this to actually train. In one line:each layer is attention tokens talk to each other followed by a feed-forward step each token privately thinks alone , with a causal mask keeping generation honest and residual connections + layer norm making it possible to stack 80 of these without the network falling apart. Section 3's diagram labeled one layer as steps 5a through 5d: attention → Add & Norm → feed-forward → Add & Norm . Sections 7 through 9 only explained 5a. The other three pieces are just as load-bearing — without them, stacking 80 layers wouldn't work at all — so they get a full section here. Section 8 showed attention as every token looking at every other token. That's true for understanding text, but it would be cheating during generation . If token 3 could attend to token 7, the model would be predicting token 4 partly by looking at words that come after it — words that don't exist yet at generation time, and that the model is supposed to be predicting in the first place. The fix is a causal mask also called a look-ahead mask : before softmax runs, every attention score for a "future" token gets forced to negative infinity, so after softmax it becomes exactly 0%. Sentence: "The cat sat on the" T1 T2 T3 T4 T5 Attention scores for T3 "sat" , BEFORE masking: T1: 0.20 T2: 0.35 T3: 0.30 T4: 0.10 T5: 0.05 ↑ ↑ these are FUTURE tokens relative to T3 Attention scores for T3, AFTER causal mask applied: T1: 0.20 T2: 0.35 T3: 0.30 T4: -inf T5: -inf After softmax: T1: 29% T2: 51% T3: 20% T4: 0% T5: 0% T3 can now only ever borrow from T1, T2, and itself — never T4 or T5. Real-world analogy: reading a mystery novel with a bookmark that physically won't let you flip ahead. You can only use clues from pages you've already read — never a peek at chapter 12 while you're still on chapter 3 — which is exactly what keeps both training and generation honest. This single masking rule is why the same architecture can be trained on an entire document at once every token predicts the next token in parallel, each one only seeing what came before it and then used at inference time to generate one token at a time — the constraint is identical in both cases. It's also why this style of model is called decoder-only or causal : contrast this with an encoder like BERT , which is trained without a causal mask because it's meant to understand a whole passage at once, not generate text forward one token at a time. "Add & Norm" is actually two distinct ideas bundled into one step, and both exist to solve the same underlying problem: very deep networks are hard to train. Residual connection "Add" — instead of a layer's output replacing its input, the original input gets added back on top of whatever the layer computed: WITHOUT a residual connection: layer output = Attention x → if the attention layer computes something slightly wrong or unhelpful for a given token, that mistake fully overwrites whatever useful information was already in x WITH a residual connection: layer output = x + Attention x → even in the worst case, x itself always survives, untouched, as a "skip path" running straight through the layer Real-world analogy: imagine a report passing through 80 different editors, one after another. If each editor's markup fully replaced the previous draft, one careless editor on round 40 could wipe out everything good from rounds 1 through 39. A residual connection is like stapling the original page behind every editor's revision — each edit gets added as a note on top, but the original is always still there underneath, so nothing valuable is ever fully erased, only built upon. Stack 80 of these and the effect compounds: every layer has a direct, unobstructed path back to the original embedding. Without this, a network that deep tends to struggle to learn at all — the training signal the gradient computed from the loss function — see section 15 has to travel back through 80 sequential transformations during training, and it tends to shrink to nothing or blow up along the way. The residual path gives it a shortcut that doesn't decay, which is the main reason transformers can be stacked this deep in the first place. Layer normalization "Norm" — after the addition, every token's vector gets rescaled so its values sit in a consistent, stable range roughly mean 0, consistent spread , independent of whatever wild swings happened inside the layer: Before layer norm, a token's vector might drift to something like: 45.2, -103.7, 8.9, 210.4, ... ← values growing unpredictably after 40+ stacked layers, values like this cause training to become unstable — small input changes cause huge output swings After layer norm, the same vector is rescaled to something like: 0.31, -0.87, 0.12, 1.42, ... ← consistent, bounded range Every layer downstream now always receives input in a predictable range, regardless of what happened in the layers before it. Real-world analogy: a sound engineer riding the volume fader before every speaker steps up to the mic at a conference. Without it, one overly enthusiastic speaker could blow out the levels for everyone after them; layer norm resets things to a safe, consistent range before every new sub-layer gets its turn. Together, residuals and layer norm are why a model can be 80 layers deep instead of 4 or 5 — they're plumbing, not intelligence, but the intelligence doesn't work without them. Attention's whole job is letting tokens exchange information with each other . The feed-forward network's job is different: it processes each token's vector independently , with no communication between tokens at all, giving the model a place to actually "think about" what it just gathered from attention. Real-world analogy: attention is a group discussion where everyone talks and listens; the feed-forward step is what happens right after, when each person goes back to their own desk and privately thinks through what they just heard, updating their own notes with no more cross-talk. Attention is the meeting; the FFN is the reflection afterward. Structurally it's simple — two learned linear transformations with a nonlinear activation function in between, usually GELU: FFN x = Linear 2 GELU Linear 1 x Linear 1: expands the vector — e.g. 4096 dimensions → 16,384 dimensions GELU: a nonlinear "squashing" function applied elementwise Linear 2: compresses back down — 16,384 dimensions → 4096 dimensions Why the expand-then-compress shape? The wider middle layer gives the network more room to combine features in complex ways before compressing back to a size that fits into the next attention layer. Why the nonlinearity? Without it, stacking two linear layers back-to-back is mathematically equivalent to a single linear layer — the nonlinearity is what lets the network represent genuinely complex, non-straight-line relationships between inputs and outputs. One number worth internalizing: the FFN is where most of a model's parameters actually live — typically around two-thirds of the total, versus roughly one-third in attention the Wq/Wk/Wv/output matrices from section 8 . Attention gets most of the explanation in tutorials like this one because it's the more novel, more interesting mechanism — but by raw parameter count, the feed-forward layers are the bulk of what you're running and paying for on every forward pass. token vectors in │ ▼ ┌─────────────────────────────┐ │ 5a. Multi-head self-attention │ tokens exchange context │ causal mask applied │ sections 8–9, above └─────────────────────────────┘ │ ▼ x = x + attention output ← 5b: residual "Add" x = LayerNorm x ← "Norm" │ ▼ ┌─────────────────────────────┐ │ 5c. Feed-forward network │ each token "thinks" independently └─────────────────────────────┘ │ ▼ x = x + ffn output ← 5d: residual "Add" x = LayerNorm x ← "Norm" │ ▼ token vectors out → fed into the next layer repeat 80 times See it move.is the best match for this exact section — it visualizes a live GPT-2 block with attention, Add & Norm, and the FFN all animated side by side in the browser, running on text you type yourself. Pull it up here and watch a real vector's values change at each of the 5a–5d sub-steps above. poloclub.github.io/transformer-explainer With one full layer understood, stacking 80 of them is literally just repeating this block 80 times, each one refining the tokens' vectors a little further. Two consequences of that repetition are worth calling out before moving on to what comes after the last layer. In one line:softmax forces every token's attention to sum to 100%, so in a long prompt that 100% gets sliced so thin across thousands of competitors that anything sitting in the middle — with no positional signal marking it as special — tends to lose out to whatever sits at the very start or very end. A well-known failure mode: stuff a fact into the middle of a long prompt, and the model is far more likely to miss it than if that same fact were at the very start or very end. This is often called "lost in the middle." It isn't a bug — it's a direct, mechanical consequence of two things covered in section 8: how attention scores are computed, and how softmax forces them to compete. It's not that the middle is invisible. Attention technically lets every token look at every other token. The real cause is competition inside softmax . SHORT CONTEXT — 5 tokens: Raw scores: 0.25, 1.30, 1.10, 0.25, 0.30 After softmax: 0.08, 0.42, 0.36, 0.08, 0.10 Smallest slice is 0.08 — still meaningful. LONG CONTEXT — 10,000 tokens: Softmax's 1.0 must now be sliced across 10,000 competitors. Average slice ≈ 0.0001. The model has no advance knowledge of which middle token matters — it has to discover relevance purely through the dot-product comparison. Meanwhile: BEGINNING tokens: system prompt lives here → trained to be high priority END tokens: the current user question → recency is a strong signal MIDDLE tokens: no positional signal marking them as important — must win purely on content, diluted among thousands of competing tokens Why training makes this worse: the model learned from internet text, books, and code, where structure almost always looks like: Important instruction / title / topic sentence ... body content ... Conclusion / answer / punchline Over billions of examples, the weight matrices developed a bias: beginning and end positions produce stronger Key vectors, because that's statistically where important information lives in human-written text. Middle positions default to weaker Keys — not because they're unseen, but because the model learned they're usually supporting detail. Concrete failure case: System prompt — 200 tokens Retrieved document 1 — 800 tokens Retrieved document 2 — 800 tokens ← CRITICAL ANSWER IS HERE Retrieved document 3 — 800 tokens Retrieved document 4 — 800 tokens User question — 50 tokens The critical answer sits around position 1,000-1,800 out of ~3,450 tokens, with no positional signal marking it as special. Its Key vector has to outcompete thousands of others. The model often misses it, partially uses it, or under-weights it — even though it's technically "visible." What this means practically: WRONG — burying critical instructions in the middle: System prompt: general behavior rules Long background context Important constraint: never discuss pricing ← often missed More background context User question RIGHT — critical information at the boundaries: System prompt: general behavior rules Important constraint: never discuss pricing ← at the top Background context User question with key constraint repeated ← at the end This is exactly why production retrieval systems RAG rerank retrieved chunks and place the most relevant ones closest to the user's question, instead of just dumping everything in document order. With all 80 layers now explained end to end, the next question is what happens the moment the last layer finishes — how a refined vector actually turns into a word. In one line:the last token's final vector gets turned into a probability for every word in the vocabulary, one word gets picked, it's appended to the sequence, and the entire 80-layer pass runs again — this loop, one word at a time, is all "generation" ever is. Every layer from sections 7 through 11 refines a token's vector using context from the rest of the sequence. This section covers what happens after the last one of those 80 layers — steps 6 through 11 of the pipeline diagram in section 3 — where a refined vector finally turns into an actual word. THE GENERATION LOOP: You give: "The cat sat on the" │ ▼ full forward pass │ ▼ model produces a probability distribution over all words │ ▼ one word is selected: "mat" │ ▼ sequence is now: "The cat sat on the mat" │ ▼ full forward pass again │ ▼ one word is selected: "." │ ▼ continues until stop condition This loop — where the model's own output becomes its next input — is what autoregressive means. Auto = self. Regressive = predicting from previous values. After the final transformer block, only the last token's vector matters for generation — that's the one the next word gets predicted from. Input: "The cat sat on the" ↑ last token: "the" After 80 transformer blocks: "the" vector = 0.45, -0.23, 0.87, ..., 0.12 4096 numbers This vector passes through one final weight matrix sized 4096 × 100,000 that maps it to 100,000 raw scores — one per vocabulary word. These raw, unnormalized scores are called logits : "mat" → 8.42 high — strongly predicted "floor" → 6.31 "wall" → 5.10 "table" → 4.88 "ground" → 4.20 ... "democracy" → -2.30 very low — contextually absurd Softmax converts these 100,000 logits into probabilities summing to 1.0: "mat" → 34.2% "floor" → 18.1% "wall" → 9.3% "table" → 8.7% "ground" → 6.5% "democracy" → 0.0% The model isn't asserting "the answer is mat." It's saying: given everything I've seen, here's my probability estimate for every possible next word. A sorting algorithm always gives the same output for the same input. An LLM does not — a word is sampled from the distribution, like a weighted dice roll: "mat" ████████████████ 34.2% "floor" █████████ 18.1% "wall" ████ 9.3% "table" ████ 8.7% "ground" ███ 6.5% everything █████████████ 23.2% spread across 99,995 other tokens else Run 1: lands on "mat" Run 2: lands on "mat" Run 3: lands on "floor" Run 4: lands on "mat" Run 5: lands on "wall" This is why the same prompt can produce different outputs on different runs. It's deliberate: if the model always picked the single highest-probability word greedy decoding , every run of "write me a poem about mountains" would produce the exact same, statistically-average poem. Sampling turns the model's uncertainty into a creative feature. Greedy decoding — always pick the highest-probability token. Deterministic, fast, but can get stuck in repetitive loops because the locally-best word at each step doesn't guarantee the best overall sentence. Sampling — pick probabilistically from the distribution at each step. Non-deterministic, more varied, more natural. In production this is almost always paired with temperature and top-p controls see section 14 https://gist.github.com/starred.atom 14-temperature--sampling--controlling-randomness . ITERATION 1: ├── forward pass through all 80 transformer blocks ├── output layer produces 100,000 logits for the last token ├── softmax → probability distribution ├── sample → "mat" └── sequence becomes: "The cat sat on the mat" ITERATION 2: ├── forward pass now "mat" attends to all previous tokens too ├── new logits → new distribution ├── sample → "." └── sequence becomes: "The cat sat on the mat." ITERATION 3: ├── forward pass ├── logits show high probability for the end-of-sequence token ├── sample →