Notation used on this pagesymbols, meanings, shapes
    01

    Multi-head attention · a worked continuation

    What else could this token read?

    The river-bank prefix from Part IIThe1fisherman2sat3beside4the5river6bank7and8watched9the10___The known prefix ends here.The updated final “the” will predict the next word.

    Which parts of this prefix would help you choose a continuation?

    Illustrative coat head readingsPossible head roles — an illustration, not a trained model’s attention1Maya2wore3a4hooded5red6wool7coatreceiverHead / query: what to look forKey: a matching sourceValue: what it can send1 · Which colour?redcolour: redStill to retrieve: material and detail.

    At “coat”, colour is useful—but so are material and shape. One head can read several words. The question is whether we want the same mixture for every feature.

    These next examples illustrate possible readings, not attention measured in our trained language model. A query is a numerical vector, not an English question. Each source also has a numerical key and value. The arrows highlight plausible useful sources; they do not mean all other sources are masked or have exactly zero weight. In deeper layers, source rows can already contain context.

    Illustrative coat head readingsPossible head roles — an illustration, not a trained model’s attention1Maya2wore3a4hooded5red6wool7coatreceiverHead / query: what to look forKey: a matching sourceValue: what it can send1 · Which colour?redcolour: red2 · Which material?woolmaterial: wool3 · Which detail?hoodeddetail: hoodKeep colour, material and detail as separate messages.

    One head could favour red, another wool, another hooded. Each receives the same input rows but can return a different weighted message. The output projection combines those messages.

    This is a possible division of work, not three manually assigned jobs. All three heads see all seven rows, including coat. Head-specific projections can expose different matching and value features. A single head may also encode several attributes; the extra capability is independent source weighting, not a rule that each head can understand only one concept.

    Illustrative grammar head readingsPossible head roles — an illustration, not a trained model’s attention1The2dogs3near4the5gate6usuallyreceiverHead / query: what to look forKey: a matching sourceValue: what it can send1 · Who is the subject?dogsplural subject2 · Where are they?gatelocation cluePossible next words: “bark at visitors”“dogs” controls agreement; the nearer noun “gate” is singular.

    The plural subject and the location are both useful clues. Separate heads can preserve them without having to favour the same source.

    The receiver is usually, the last known token—not the as-yet-unknown word bark. “The dogs near the gate usually bark at visitors” is one possible continuation. Dogs supports bark, not barks; the nearer singular noun gate does not control subject agreement. Both useful source words occur before the receiver. These semantic roles are illustrative, not measured trained heads.

    Illustrative event head readingsPossible head roles — an illustration, not a trained model’s attention1Ravi2dropped3the4glass5.6ItreceiverHead / query: what to look forKey: a matching sourceValue: what it can send1 · What does “It” refer to?glassobject: glass2 · What happened to it?droppedevent: droppingPossible continuation: “broke”The object alone does not tell us what happened.

    At “It”, one reading could retrieve the object; another could retrieve what happened to it. Combining glass and dropping makes “broke” a plausible continuation, not a certainty.

    We treat punctuation as a token in this illustration: It is receiver 6. Both useful sources are in its known prefix. The two heads run in parallel, so the event head does not wait for the reference head’s result. Contextual input rows from earlier layers can help make these matches.

    Illustrative event head readings after changing the actionPossible head roles — an illustration, not a trained model’s attention1Ravi2washed3the4glass5.6ItreceiverHead / query: what to look forKey: a matching sourceValue: what it can send1 · What does “It” refer to?glassobject: glass2 · What happened to it?washedevent: washingPossible continuation: “gleamed”Same object. A different event gives a different clue.

    The glass is unchanged. “Washed” supports a different continuation, such as “gleamed”. Separate messages carry both the object clue and the event clue.

    The arrows are a schematic of useful information, not a promise that a trained attention map remains unchanged after a word substitution. All head projections learn jointly from prediction loss. Interpretable roles sometimes emerge, but some heads overlap or can be pruned; see Voita et al. (2019). More heads are a capacity choice to test, not an automatic improvement.

    The final the uses one query to retrieve one weighted messageThe1fisherman2sat3beside4the5river6bank7and8watched9the10___Receiver: the final “the”. Its updated row predicts the blank.Query at this receiverWeights from matching keysMix source valuesHead 1 · q(1)Setting clues?Favour riverAlso read the other tokensMessage 1setting informationIllustrative head roles. The queries and messages are numerical vectors.

    One query chooses how much to read from each token. The head mixes their value vectors into one message for the final “the”.

    The prefix is “The fisherman sat beside the river bank and watched the ___”. The final known token, the at slot 10, is the receiver. The blank has no query yet. One head can read several words and carry several features in its message. It uses one shared set of source weights for all coordinates of that message. “Setting clues?” describes a possible role of the numerical query, not a literal question or a manually assigned task.

    The same final the receives two independently weighted messagesThe1fisherman2sat3beside4the5river6bank7and8watched9the10___Receiver: the final “the”. Its updated row predicts the blank.Query at this receiverWeights from matching keysMix source valuesHead 1 · q(1)Setting clues?Favour riverAlso read the other tokensMessage 1setting informationHead 2 · q(2)Person clues?Favour fishermanAlso read the other tokensMessage 2person informationIllustrative head roles. The queries and messages are numerical vectors.

    Same receiver, two queries. Each head has its own keys, values and weights. One can favour river, the other fisherman. We combine their messages afterward.

    Each head learns its own W_Q, W_K and W_V, applied to the same input rows. At this receiver that produces one query per head. Both heads can read all ten known tokens and run in parallel. Training can discover useful roles, but does not assign setting and person labels or guarantee that the heads specialize this way.

    Optional: why separate weights can help

    In a separate two-source toy, let river have value [10, 1] and fisherman [2, 8]. A single head with weights [0.8, 0.2] returns [8.4, 2.4]. Both coordinates use that same mixture. Two heads can instead select one value coordinate each: a setting head with weights [0.8, 0.2] gives 8.4, and a person head with weights [0.2, 0.8] gives 6.6. With these fixed values, one river weight cannot be both 0.8 and 0.2. This illustrates independent source weighting, not a proof that every one-head network fails. These invented numbers are separate from the full-sentence worksheet that follows.

    Head 1 reads the known prefixHead 1: setting cluesThe1fisherman2sat3beside4the5river6bank7and8watched9the10___0.71810 · theThicker arrow = more attention weightreceiver

    Back to all ten tokens. Our hand-chosen head 1 gives river a large weight. Its values carry setting information to the receiver.

    These are hand-chosen two-head parameters applied to Part II’s exact input rows. “Setting” and “person” name the intended behaviour of this example, not jobs assigned to trained heads. Arrows show information moving from source to receiver; their widths encode computed attention weights.

    Head 2 reads the known prefixHead 2: person cluesThe1fisherman2sat3beside4the5river6bank7and8watched9the10___0.69310 · theThicker arrow = more attention weightreceiver

    The sentence and receiver stay fixed. A different head gives fisherman a large weight.

    Two different reading patternsHead 1: setting cluesThe1fisherman2sat3beside4the5river6bank7and8watched9the10___0.71810 · theThicker arrow = more attention weightreceiverHead 2: person cluesThe1fisherman2sat3beside4the5river6bank7and8watched9the10___0.69310 · theThicker arrow = more attention weightreceiver

    Two heads can retain different source mixtures at the same token. They run in parallel, not one after the other.

    02

    Multi-head attention · a worked continuation

    How does a second head get a different view?

    Queries, keys and values in each headQueries, keys and values in each headEach head learns its own WQ, WK and WV.

    Visual inspiration: 3Blue1Brown’s attention lesson and Jay Alammar’s Illustrated Transformer. We keep our own river-bank example, numbers and Part II row-vector convention. A head-specific superscript labels a head; a subscript still labels a token.

    Recall Part II: the query asks, the key matches, the value supplies contentMaya cycled home in the rain. Cold and tired, Maya reachedfor a hooded red wool coat. She …q: what is needed?She × WQWhich earlier person?k: what can match?Maya × WKPerson candidatev: what is sent?Maya × WVCold, tired; cycled in rain“Maya” and “She” here mean their current embedding rows.

    In Part II’s illustrative later-layer example, the query asks for a person. Maya’s key can match that request. Her value supplies useful details. A head uses separate projections for these roles.

    This is the verbal example from Part II. It illustrates possible later-layer representations, not measured model outputs. Earlier layers may have gathered the preceding facts into the second Maya row. Every token has a query, key and value, even though this example follows only She’s query and Maya’s key/value. The actual vectors contain numbers, not written questions or records.

    The same next-token path, with every matrix shape in two parallel headsOne sentence: 10 tokens, width 4. Two heads, width 2 each.Input E[10×4]Head 1Q [10×2]K [10×2]V [10×2]QKᵀ: [10×10]scale, causal mask, softmaxA: [10×10]H(1) = A(1)V(1)[10×10] × [10×2]= [10×2]Head 2Q [10×2]K [10×2]V [10×2]QKᵀ: [10×10]scale, causal mask, softmaxA: [10×10]H(2) = A(2)V(2)[10×10] × [10×2]= [10×2]Concatenatetwo [10×2] → [10×4]Project with WO[10×4] × [4×4]keep original E [10×4]ΔE [10×4]+E′ = E + ΔE[10×4], same as ELast row [1×4]next-token prediction MLP

    Each head keeps all 10 tokens. \(A\) weights source tokens; \(H=AV\) contains their messages. Concatenation joins coordinates, so the result is \(10\times4\), not \(20\times2\).

    This diagram shows one sentence, without a batch axis. E has shape [10, 4]. Each head has separate W_Q, W_K and W_V of shape [4, 2], producing Q, K and V each [10, 2]. QKᵀ multiplies [10, 2] by [2, 10] to give [10, 10] scores. Scaling, causal masking and row-wise softmax preserve that shape. A has one row per receiver and one column per source. H = AV multiplies [10, 10] by [10, 2] to produce [10, 2]: one message per receiver. The final receiver’s individual q, k, v and message rows are each [1, 2].

    H¹ and H² concatenate to [10, 4]. W_O [4, 4] gives ΔE [10, 4], then E′ = E + ΔE is also [10, 4]. Its last row [1, 4] feeds the prediction MLP. Here the joined width already equals the embedding width. W_O still learns to mix head outputs into embedding coordinates. In general its shape is [n_heads × d_v, d_model]. It is not an inverse of the input projections.

    The familiar query, key and value roles, now repeated in two headsReceiver: the final “the” in the river-bank prefixHeadQuery asks aboutA useful sourceValue carries1the settingriversetting clues2the personfishermanperson cluesEach head computes q, k and v for every token.We follow one receiver and two useful sources.

    Head 1 can ask about the setting; head 2 can ask about the person. These are interpretations of our chosen numbers. Training learns the projections without assigning these jobs.

    Head 1: multiply the same four-coordinate embedding by its own query projectionReceiver: final “the”q₁₀(1) = e₁₀ WQ(1)e₁₀: four input coordinates0.0water0.0finance0.0person2.3glue×WQ(1) [4 × 2]water?finance?00000011q₁₀(1) [1 × 2]2.32.3First query coordinate: 0×0 + 0×0 + 0×0 + 2.3×1 = 2.3Second query coordinate: 0×0 + 0×0 + 0×0 + 2.3×1 = 2.3

    In this toy, the final “the” has only a nonzero glue coordinate. The two columns of \(W_Q\) turn that input into water and finance matching features.

    E contains position-aware embedding rows, not token IDs. The water, finance, person and glue axes are invented for teaching; glue is our toy feature for function words such as “the”. W_Q reads all four input coordinates. Each query coordinate is a dot product with one column of W_Q. These sparse matrices are hand-chosen; learned projections need not be sparse or interpretable. The head superscripts label heads, not powers. Query/key width is two per head here; Part II’s single-head toy used three.

    Head 2: multiply the same four-coordinate embedding by its own query projectionReceiver: final “the”q₁₀(2) = e₁₀ WQ(2)e₁₀: four input coordinates0.0water0.0finance0.0person2.3glue×WQ(2) [4 × 2]person?glue?00000010q₁₀(2) [1 × 2]2.30.0First query coordinate: 0×0 + 0×0 + 0×0 + 2.3×1 = 2.3Second query coordinate: 0×0 + 0×0 + 0×0 + 2.3×0 = 0.0

    The input embedding stays [0, 0, 0, 2.3]. Head 2 uses its own \(W_Q\). Its query requests the person feature and gives zero weight to the glue matching feature.

    Each source embedding supplies a key for matching and a value for the weighted messageSource input eⱼ: [water, finance, person, glue]Head 1: river[3.1, −0.1, 0.0, 0.1]WK(1) selects water, financeWV(1) selects water, financek = [3.1, −0.1]v = [3.1, −0.1]Head 2: fisherman[2.0, 0.1, 2.1, 0.0]WK(2) selects person, glueWV(2) selects person, gluek = [2.1, 0.0]v = [2.1, 0.0]kⱼ = eⱼ WK sets the match. vⱼ = eⱼ WV supplies the message.

    Each head has its own \(W_K\) and \(W_V\), both \(4\times2\) here. For easy arithmetic, they select the same coordinates. Keys determine matching; values carry the numbers we mix.

    Head 1 uses W_K = W_V = [[1,0],[0,1],[0,0],[0,0]], selecting water and finance. Head 2 uses W_K = W_V = [[0,0],[0,0],[1,0],[0,1]], selecting person and glue. Each matrix is [4,2] and acts on every source row, including sources not shown here. W_K and W_V are distinct parameters with equal numerical entries in this worksheet. They need not be equal in a trained model. In Part II’s Maya example they served different roles too.

    Head 1: all ten query rows and key rows, with the receiver and example source highlightedToken and positionQ(1) = E WQ(1)[10 × 4] × [4 × 2] = [10 × 2]water?finance?2.32.30.00.01.91.92.02.02.32.30.10.10.80.82.22.21.71.72.32.3K(1) = E WK(1)[10 × 4] × [4 × 2] = [10 × 2]waterfinance0.10.02.00.10.30.00.6−0.10.10.13.1−0.10.70.7−0.10.10.70.00.00.0 1 The 2 fisherman 3 sat 4 beside 5 the 6 river 7 bank 8 and 9 watched10 theFollow query row 10 (“the”) and key row 6 (“river”).

    Every token has a query and a key. We follow the last query row, for the final “the”, and compare it with all ten key rows.

    Head 1: one query times the transposed key matrix yields ten dot productsq₁₀(1) =2.32.3Receiver 10: final “the”q [1 × 2] × Kᵀ [2 × 10] = raw scores r [1 × 10]Kᵀwaterfinanceraw r1The0.10.00.232fisherman2.00.14.833sat0.30.00.694beside0.6−0.11.155the0.10.10.466river3.1−0.16.907bank0.70.73.228and−0.10.10.009watched0.70.01.6110the0.00.00.00Column 6: query · river key2.3 × 3.1 + 2.3 × (−0.1) = 7.13 + (−0.23) = 6.90

    Transposing \(K\) puts each source key in a column. Multiplying the query row by \(K^\top\) gives one raw dot product per source.

    Head 1: scale all ten dot products and divide each exponential by the sum of all ten exponentialsTen matching scores, normalized within this headSourceRaw dot rs = r / √2exp(s)Weight α 1 The0.230.1631.1770.006 2 fisherman4.833.41530.4270.166 3 sat0.690.4881.6290.009 4 beside1.150.8132.2550.012 5 the0.460.3251.3840.008 6 river6.904.879131.5040.718 7 bank3.222.2779.7460.053 8 and0.000.0001.0000.005 9 watched1.611.1383.1220.01710 the0.000.0001.0000.005Shared total: Σ exp(s) = 183.244river weight: 131.504 / 183.244 ≈ 0.718

    Divide each dot product by √2, then apply softmax across these ten sources. The final query can see all ten tokens. The unrounded weights sum to one.

    Every source contributes to the denominator, including the receiver itself. All displayed numbers are rounded; calculations use full precision. At receiver row 10, the causal mask permits source positions 1 through 10. Earlier query rows cannot read later sources. The notebook also computes the full masked attention matrix. A numerically stable softmax subtracts the maximum score before exponentiating; this leaves the normalized weights unchanged.

    Head 1: attention weights for each sourceReceiver: the final “the”, at position 10Source j 1 The 2 fisherman 3 sat 4 beside 5 the 6 river 7 bank 8 and 9 watched10 theWeight α10,j0.0060.1660.0090.0120.0080.7180.0530.0050.0170.005α10,6 ≈ 0.718Receiver 10 reads source 6: river71.8% of this head’s total weightThese are the softmax weights we just calculated.

    \(\alpha_{10,j}\) tells us how much the final “the” reads from source \(j\). Keep these weights as we bring in the values.

    These are the ten entries of row 10 of this head’s attention matrix A. We display them vertically so each weight can sit beside its source word and value row. The receiver stays fixed at token 10. Only the source index j changes.

    Head 1: pair each source weight with its value rowReceiver: the final “the”, at position 10Source j 1 The 2 fisherman 3 sat 4 beside 5 the 6 river 7 bank 8 and 9 watched10 theWeight α10,j0.0060.1660.0090.0120.0080.7180.0530.0050.0170.005Value row vⱼ[0.1, 0.0][2.0, 0.1][0.3, 0.0][0.6, −0.1][0.1, 0.1][3.1, −0.1][0.7, 0.7][−0.1, 0.1][0.7, 0.0][0.0, 0.0]V = E WV10 source rows × 2river: its weight pairs with its own two-number value row.

    Each source supplies a two-number value row from this head’s V matrix. The source words and weights stay in place, so we can see which weight belongs to which value.

    V = E W_V has one row per source. These are projected value vectors, not token IDs or attention weights. Our hand-chosen worksheet gives W_K and W_V equal entries, but they remain separate parameters with different roles. The keys helped compute the weights. V supplies the content to mix.

    Head 1: multiply both value coordinates by the same source weightReceiver: the final “the”, at position 10Source j 1 The 2 fisherman 3 sat 4 beside 5 the 6 river 7 bank 8 and 9 watched10 theWeight α10,j0.0060.1660.0090.0120.0080.7180.0530.0050.0170.005Value row vⱼ[0.1, 0.0][2.0, 0.1][0.3, 0.0][0.6, −0.1][0.1, 0.1][3.1, −0.1][0.7, 0.7][−0.1, 0.1][0.7, 0.0][0.0, 0.0]Contribution α10,j vⱼ[0.001, 0.000][0.332, 0.017][0.003, 0.000][0.007, −0.001][0.001, 0.001][2.225, −0.072][0.037, 0.037][−0.001, 0.001][0.012, 0.000][0.000, 0.000]×=river: [α10,6 × (3.1), α10,6 × (−0.1)] ≈ [2.225, −0.072]

    Multiply both value coordinates by the same source weight. The new column shows exactly what each source contributes. Displayed numbers are rounded.

    The calculation below the rows expands the highlighted source’s scalar–vector multiplication coordinate by coordinate. A small weight scales down the entire value vector. The sign of each coordinate comes from the value. All products use the unrounded weights.

    Head 1: sum all ten weighted value rows to obtain the messageReceiver: the final “the”, at position 10Source j 1 The 2 fisherman 3 sat 4 beside 5 the 6 river 7 bank 8 and 9 watched10 theWeight α10,j0.0060.1660.0090.0120.0080.7180.0530.0050.0170.005Value row vⱼ[0.1, 0.0][2.0, 0.1][0.3, 0.0][0.6, −0.1][0.1, 0.1][3.1, −0.1][0.7, 0.7][−0.1, 0.1][0.7, 0.0][0.0, 0.0]Contribution α10,j vⱼ[0.001, 0.000][0.332, 0.017][0.003, 0.000][0.007, −0.001][0.001, 0.001][2.225, −0.072][0.037, 0.037][−0.001, 0.001][0.012, 0.000][0.000, 0.000]×=Sum each contribution coordinate:m₁₀(1) =2.617−0.018weight row [1 × 10] × V [10 × 2] = message [1 × 2]

    Add the first coordinates to get the first message coordinate. Add the second coordinates to get the second. All ten sources contribute. The sum uses unrounded products.

    This is one row of H = AV: m₁₀ = Σⱼ α₁₀,ⱼ vⱼ. The original weight row has shape [1,10] and V has shape [10,2], giving a [1,2] message. We arranged the weights vertically only to make the source correspondence visible. The displayed products are rounded, but the sums use full precision.

    Head 2: all ten query rows and key rows, with the receiver and example source highlightedToken and positionQ(2) = E WQ(2)[10 × 4] × [4 × 2] = [10 × 2]person?glue?2.30.00.00.01.90.02.00.02.30.00.10.00.80.02.20.01.70.02.30.0K(2) = E WK(2)[10 × 4] × [4 × 2] = [10 × 2]personglue0.12.32.10.00.61.90.12.00.02.30.00.10.10.80.12.20.71.70.02.3 1 The 2 fisherman 3 sat 4 beside 5 the 6 river 7 bank 8 and 9 watched10 theFollow query row 10 (“the”) and key row 2 (“fisherman”).

    Every token has a query and a key. We follow the last query row, for the final “the”, and compare it with all ten key rows.

    Head 2: one query times the transposed key matrix yields ten dot productsq₁₀(2) =2.30.0Receiver 10: final “the”q [1 × 2] × Kᵀ [2 × 10] = raw scores r [1 × 10]Kᵀpersonglueraw r1The0.12.30.232fisherman2.10.04.833sat0.61.91.384beside0.12.00.235the0.02.30.006river0.00.10.007bank0.10.80.238and0.12.20.239watched0.71.71.6110the0.02.30.00Column 2: query · fisherman key2.3 × 2.1 + 0.0 × 0.0 = 4.83 + (0.00) = 4.83

    Transposing \(K\) puts each source key in a column. Multiplying the query row by \(K^\top\) gives one raw dot product per source.

    Head 2: scale all ten dot products and divide each exponential by the sum of all ten exponentialsTen matching scores, normalized within this headSourceRaw dot rs = r / √2exp(s)Weight α 1 The0.230.1631.1770.027 2 fisherman4.833.41530.4270.693 3 sat1.380.9762.6530.060 4 beside0.230.1631.1770.027 5 the0.000.0001.0000.023 6 river0.000.0001.0000.023 7 bank0.230.1631.1770.027 8 and0.230.1631.1770.027 9 watched1.611.1383.1220.07110 the0.000.0001.0000.023Shared total: Σ exp(s) = 43.908fisherman weight: 30.427 / 43.908 ≈ 0.693

    Divide each dot product by √2, then apply softmax across these ten sources. The final query can see all ten tokens. The unrounded weights sum to one.

    Every source contributes to the denominator, including the receiver itself. All displayed numbers are rounded; calculations use full precision. At receiver row 10, the causal mask permits source positions 1 through 10. Earlier query rows cannot read later sources. The notebook also computes the full masked attention matrix. A numerically stable softmax subtracts the maximum score before exponentiating; this leaves the normalized weights unchanged.

    Head 2: attention weights for each sourceReceiver: the final “the”, at position 10Source j 1 The 2 fisherman 3 sat 4 beside 5 the 6 river 7 bank 8 and 9 watched10 theWeight α10,j0.0270.6930.0600.0270.0230.0230.0270.0270.0710.023α10,2 ≈ 0.693Receiver 10 reads source 2: fisherman69.3% of this head’s total weightThese are the softmax weights we just calculated.

    \(\alpha_{10,j}\) tells us how much the final “the” reads from source \(j\). Keep these weights as we bring in the values.

    These are the ten entries of row 10 of this head’s attention matrix A. We display them vertically so each weight can sit beside its source word and value row. The receiver stays fixed at token 10. Only the source index j changes.

    Head 2: pair each source weight with its value rowReceiver: the final “the”, at position 10Source j 1 The 2 fisherman 3 sat 4 beside 5 the 6 river 7 bank 8 and 9 watched10 theWeight α10,j0.0270.6930.0600.0270.0230.0230.0270.0270.0710.023Value row vⱼ[0.1, 2.3][2.1, 0.0][0.6, 1.9][0.1, 2.0][0.0, 2.3][0.0, 0.1][0.1, 0.8][0.1, 2.2][0.7, 1.7][0.0, 2.3]V = E WV10 source rows × 2fisherman: its weight pairs with its own two-number value row.

    Each source supplies a two-number value row from this head’s V matrix. The source words and weights stay in place, so we can see which weight belongs to which value.

    V = E W_V has one row per source. These are projected value vectors, not token IDs or attention weights. Our hand-chosen worksheet gives W_K and W_V equal entries, but they remain separate parameters with different roles. The keys helped compute the weights. V supplies the content to mix.

    Head 2: multiply both value coordinates by the same source weightReceiver: the final “the”, at position 10Source j 1 The 2 fisherman 3 sat 4 beside 5 the 6 river 7 bank 8 and 9 watched10 theWeight α10,j0.0270.6930.0600.0270.0230.0230.0270.0270.0710.023Value row vⱼ[0.1, 2.3][2.1, 0.0][0.6, 1.9][0.1, 2.0][0.0, 2.3][0.0, 0.1][0.1, 0.8][0.1, 2.2][0.7, 1.7][0.0, 2.3]Contribution α10,j vⱼ[0.003, 0.062][1.455, 0.000][0.036, 0.115][0.003, 0.054][0.000, 0.052][0.000, 0.002][0.003, 0.021][0.003, 0.059][0.050, 0.121][0.000, 0.052]×=fisherman: [α10,2 × (2.1), α10,2 × (0.0)] ≈ [1.455, 0.000]

    Multiply both value coordinates by the same source weight. The new column shows exactly what each source contributes. Displayed numbers are rounded.

    The calculation below the rows expands the highlighted source’s scalar–vector multiplication coordinate by coordinate. A small weight scales down the entire value vector. The sign of each coordinate comes from the value. All products use the unrounded weights.

    Head 2: sum all ten weighted value rows to obtain the messageReceiver: the final “the”, at position 10Source j 1 The 2 fisherman 3 sat 4 beside 5 the 6 river 7 bank 8 and 9 watched10 theWeight α10,j0.0270.6930.0600.0270.0230.0230.0270.0270.0710.023Value row vⱼ[0.1, 2.3][2.1, 0.0][0.6, 1.9][0.1, 2.0][0.0, 2.3][0.0, 0.1][0.1, 0.8][0.1, 2.2][0.7, 1.7][0.0, 2.3]Contribution α10,j vⱼ[0.003, 0.062][1.455, 0.000][0.036, 0.115][0.003, 0.054][0.000, 0.052][0.000, 0.002][0.003, 0.021][0.003, 0.059][0.050, 0.121][0.000, 0.052]×=Sum each contribution coordinate:m₁₀(2) =1.5520.538weight row [1 × 10] × V [10 × 2] = message [1 × 2]

    Add the first coordinates to get the first message coordinate. Add the second coordinates to get the second. All ten sources contribute. The sum uses unrounded products.

    This is one row of H = AV: m₁₀ = Σⱼ α₁₀,ⱼ vⱼ. The original weight row has shape [1,10] and V has shape [10,2], giving a [1,2] message. We arranged the weights vertically only to make the source correspondence visible. The displayed products are rounded, but the sums use full precision.

    Two complete head calculations produce two messages before concatenation and output projectionHead 1q₁₀(1) K(1)ᵀ / √2ten scoressoftmaxOwn weight rowriver: 0.718× V(1)2.617−0.018m₁₀(1) [1 × 2]Head 2q₁₀(2) K(2)ᵀ / √2ten scoressoftmaxOwn weight rowfisherman: 0.693× V(2)1.5520.538m₁₀(2) [1 × 2]Both heads read the same input E. Their score lists and value sums stay separate.

    Each head has now computed its own scores, weights and message. We keep both messages, then concatenate and project them into the embedding update.

    As in Part II, αᵢⱼ is one attention weight and A stores the full attention matrix. The walkthrough follows its final row, A[10, :], using one-based token positions. Q/K choose weights and V supplies the weighted content. All ten source contributions enter each message. The calculations are independent across heads and can run in parallel.

    A four-coordinate dot product adds both matching contributions before one softmaxJoin the same query and key coordinates into one wider head.q₁₀ =2.32.32.30.0setting coordinatesperson coordinatesriver6.90+0.00= 6.90÷ √43.45fisherman4.83+4.83= 9.66÷ √44.83setting dotperson dotone score

    A wider query/key can compare more features. Their products still add into one score per source, followed by one softmax.

    For this controlled comparison, concatenate the two existing Q, K and V projections into width-four projections. A single wide head scales its dot products by √4. The two narrower heads each scale by √2 and normalize separately. The example changes no projection entries and does not compare separately trained models.

    The same projection coordinates yield one attention row or two separately normalized rowsReceiver 10 readsriverfishermanothersvalue widthOne wide head0.1780.7080.1134Head 1: setting0.7180.1660.1162Head 2: person0.0230.6930.2842One shared mixture of four coordinates, or two separate mixtures of two.

    A wider value vector carries more features with one shared weight row. Two heads can favour river for setting features and fisherman for person features.

    Every displayed attention row sums to one over all ten sources; “others” combines the remaining eight. One wide head uses its single row for all four value coordinates. The two heads use different rows for their two-coordinate values, preserving both mixtures before W_O combines them. This is a useful architectural choice, not proof that one wide head cannot learn useful relationships or that more heads always improve accuracy. See Attention Is All You Need, §3.2.2.

    03

    Multi-head attention · a worked continuation

    How do the two messages update the token?

    Combining the head messagesCombining the head messagesConcatenate, project into embedding space, then add the update.

    Concatenate two messages; do not average themm₁₀(1) · setting messagem₁₀(2) · person message2.617−0.0181.5520.5382.617−0.0181.5520.5382 coordinates + 2 coordinates → 4 coordinates

    Concatenation keeps the two messages separate. It does not add them coordinate by coordinate.

    Map the joined message back to the four input coordinatesjoined message2.617−0.0181.5520.538×WO · 4 × 4100001000.250100001head 1 rowshead 2 rowsΔe₁₀ =3.005−0.0181.5520.538First coordinate: 2.617 + 0.25 × 1.552 = 3.005

    \(W_O\) learns how the head messages contribute to the embedding update. Four joined coordinates become four update coordinates.

    Equivalently, split W_O into a two-row block per head. Then Δe = m¹ W_O¹ + m² W_O²: each head contributes a four-coordinate update. Concatenation followed by one matrix multiplication computes exactly that sum. We retain the standard concat notation from the original Transformer paper.

    Keep the original embedding row and add the context updateoriginal e₁₀0.0000.0000.0002.300waterfinancepersonglue+ update Δe₁₀3.005−0.0181.5520.538= updated e′₁₀3.005−0.0181.5522.838

    Exactly as in Part II: \(e_i\) is the input embedding row, \(\Delta e_i\) is the context update, and \(e_i^{\prime}=e_i+\Delta e_i\).

    The same next-token path, with every matrix shape in two parallel headsOne sentence: 10 tokens, width 4. Two heads, width 2 each.Input E[10×4]Head 1Q [10×2]K [10×2]V [10×2]QKᵀ: [10×10]scale, causal mask, softmaxA: [10×10]H(1) = A(1)V(1)[10×10] × [10×2]= [10×2]Head 2Q [10×2]K [10×2]V [10×2]QKᵀ: [10×10]scale, causal mask, softmaxA: [10×10]H(2) = A(2)V(2)[10×10] × [10×2]= [10×2]Concatenatetwo [10×2] → [10×4]Project with WO[10×4] × [4×4]keep original E [10×4]ΔE [10×4]+E′ = E + ΔE[10×4], same as ELast row [1×4]next-token prediction MLP

    The last updated row still feeds the prediction MLP. More heads change how it reads context—not what the target means.

    Change to the cheque sentence. These controls recalculate Q, K, V, both attention rows, the messages and the vocabulary prediction from the hand-chosen parameters. This is a worked arithmetic explorer, not a trained language model.

    04

    Multi-head attention · a worked continuation

    What are the matrix shapes?

    The same calculation for every tokenThe same calculation for every tokenTrace the receiver row through the full matrices.

    Project ten four-coordinate input rows into ten two-coordinate rowsE10 × 4×WQ(1)4 × 2Q(1)10 × 2Every token uses the same projection matrix within this head.

    Ten input rows × four coordinates. Multiplying by a 4 × 2 projection gives ten query rows × two coordinates.

    One head: query-key scores, a causal weight grid, then weighted valuesQ(1)10 × 2×K(1)ᵀ2 × 10÷ √2masksoftmaxA(1)10 × 10××××××××××××××××××××××××××××××××××××××××××××××V(1)10 × 2H(1)10 × 2Each highlighted row follows receiver 10. Columns of A are source positions.

    Keep Part II’s notation: \(M\) is the causal mask, \(A=\operatorname{softmax}(QK^\top/\sqrt{d_k}+M)\), and \(H=AV\) stores the message rows.

    Two independent attention grids computed from the same input snapshotsame E10 × 4Head 1own Q, K, Vown projectionsA(1)10 × 10×××××××××××××××××××××××××××××××××××××××××××××× VH(1)10 × 2Head 2own Q, K, Vown projectionsA(2)10 × 10×××××××××××××××××××××××××××××××××××××××××××××× VH(2)10 × 2

    Both heads receive the same E. Each uses its own projections, attention grid and values. Neither reads the other head’s output.

    Concatenate columns, project, and preserve the token axisH(1)10 × 2H(2)10 × 2;Concat(H(1), H(2))10 × 4×WO4 × 4ΔE10 × 410 message rows → 10 update rows. The token count stays fixed.

    \(\Delta E=\operatorname{Concat}(H^{(1)},H^{(2)})W_O\), then \(E^{\prime}=E+\Delta E\). The superscript labels the head; each matrix still has ten token rows.

    05

    Multi-head attention · a worked continuation

    Write the familiar operations twice

    One head in codeE → Q, K, V → scores → weights → message

    No new attention rule is needed. We reuse the same calculation with different learned matrices.

    def head(E, W_Q, W_K, W_V):
        Q, K, V = E @ W_Q, E @ W_K, E @ W_V
        scores = Q @ K.T / math.sqrt(Q.shape[-1])
        future = torch.ones(len(E), len(E), dtype=torch.bool).triu(1)
        A = scores.masked_fill(future, -torch.inf).softmax(-1)
        return A @ V
    Combine two head outputs in codeH(1), H(2) → concatenate columns → WO → ΔE

    This unbatched example keeps one row per token. The notebook checks these outputs against the printed numbers.

    H1 = head(E, W_Q1, W_K1, W_V1)
    H2 = head(E, W_Q2, W_K2, W_V2)
    joined = torch.cat([H1, H2], dim=-1)  # [10, 4]
    delta_E = joined @ W_O
    E_prime = E + delta_E
    The same next-token path, with every matrix shape in two parallel headsOne sentence: 10 tokens, width 4. Two heads, width 2 each.Input E[10×4]Head 1Q [10×2]K [10×2]V [10×2]QKᵀ: [10×10]scale, causal mask, softmaxA: [10×10]H(1) = A(1)V(1)[10×10] × [10×2]= [10×2]Head 2Q [10×2]K [10×2]V [10×2]QKᵀ: [10×10]scale, causal mask, softmaxA: [10×10]H(2) = A(2)V(2)[10×10] × [10×2]= [10×2]Concatenatetwo [10×2] → [10×4]Project with WO[10×4] × [4×4]keep original E [10×4]ΔE [10×4]+E′ = E + ΔE[10×4], same as ELast row [1×4]next-token prediction MLPLossobserved target y

    Next-token cross-entropy trains all the projections together. We do not label training examples “setting head” or “person head”.

    At training time, the observed next-token ID is the target for the vocabulary logits. At inference time, weights remain fixed: tokenize a prefix, run the model, choose a next token and append it. These steps are unchanged from Part II; the full executable loop remains in Notebook 7.

    A bias adds a learned offset after the projection; the displayed offset is illustrativeOne head: qᵢ = eᵢ WQ + bQbias=Falsee₁₀ WQ2.32.3bias=True2.32.3+0.2−0.1=2.52.2bQ: learned offset [2]The same bQ is added to every token row in this head.

    The offset [0.2, −0.1] is illustrative. With bias=True, training learns it along with the projection weights. We use bias=False so the code matches the worksheet.

    A projection bias is shared across token positions (and batches). Position embeddings depend on the position, so these are different operations. A bias can shift the projection even for a zero input. Omitting it keeps this example simpler; it is not a general claim that bias-free attention is better.

    The bias flag controls all query, key, value and output projection biases, not the separate prediction MLPInside nn.MultiheadAttentionQ = E WQ + bQoffset after input projectionK = E WK + bKoffset after input projectionV = E WV + bVoffset after input projectionΔE = Concat(H(1), H(2)) WO + bOoffset after output projectionOur worksheet and trained attention layers: bias=False.The separate prediction MLP still has its own biases.

    PyTorch defaults to bias=True. In this lesson, bias=False removes the Q, K, V and output offsets. It leaves the mask, position embeddings and separate prediction layers unchanged.

    With total width four, bias=True adds four query offsets, four key offsets, four value offsets and four output offsets: 16 additional trainable scalars. Within each head the Q/K/V bias slice has width two. PyTorch stores the three input offsets together in in_proj_bias and the output offset in out_proj.bias. The distinct add_bias_kv option is not the bias flag discussed here. Official API documentation. Both our scratch implementation and trained attention variants omit attention projection biases; their prediction MLP layers retain biases.

    PyTorch returns the projected update, before our residualE → nn.MultiheadAttention → ΔE

    Here E has shape [10, 4]: one unbatched sequence. PyTorch handles the projections and mixing. A contains two 10 × 10 weight grids.

    mha = nn.MultiheadAttention(embed_dim=4, num_heads=2, bias=False)
    future = torch.ones(10, 10, dtype=torch.bool).triu(1)
    delta_E, A = mha(E, E, E, attn_mask=future,
                     average_attn_weights=False)
    E_prime = E + delta_E

    The library layer does not add position embeddings, the residual or the vocabulary classifier. attn_mask=future blocks future sources. average_attn_weights=False returns each head’s weight grid separately; it changes the returned diagnostic weights, not how the head messages combine. The notebook copies identical weights from our scratch implementation to PyTorch and checks both ΔE and the per-head attention weights. See the PyTorch documentation.

    06

    Multi-head attention · a worked continuation

    Do more heads help our trained model?

    The same next-token path, with every matrix shape in two parallel headsOne sentence: 10 tokens, width 4. Two heads, width 2 each.Input E[10×4]Head 1Q [10×2]K [10×2]V [10×2]QKᵀ: [10×10]scale, causal mask, softmaxA: [10×10]H(1) = A(1)V(1)[10×10] × [10×2]= [10×2]Head 2Q [10×2]K [10×2]V [10×2]QKᵀ: [10×10]scale, causal mask, softmaxA: [10×10]H(2) = A(2)V(2)[10×10] × [10×2]= [10×2]Concatenatetwo [10×2] → [10×4]Project with WO[10×4] × [4×4]keep original E [10×4]ΔE [10×4]+E′ = E + ΔE[10×4], same as ELast row [1×4]next-token prediction MLP

    We have computed both heads, joined their messages, applied the output projection and added the residual. The final updated row still goes to the familiar prediction MLP.

    This repeats the same two-head diagram after the detailed arithmetic, rather than introducing a new architecture. The worksheet uses ten rows of width four. Revisit Head 1’s calculations, Head 2’s calculations, or the output projection. Notebook 7 executes the complete forward pass, including the prediction MLP and vocabulary probabilities. This teaching model has one attention update and a prediction MLP; it is not a diagram of every component in a deep production Transformer.

    The trained MLP concatenates all token rows before its prediction layersSame 64-token input window · vocabulary size C = 4,000Token lookup64 rows × 64Concatenate1 × 4,096MLP hidden layer4,096 → 256 + ReLULogits256 → 4,000Different input slots occupy different parts of the flattened vector.No attention layer or separate position table in this baseline.Vocabulary softmax → next-token probabilities.

    Now look at the genuinely trained browser models. The MLP reads all 64 token rows in fixed order. Its hidden layer receives 4,096 inputs.

    This diagram follows FixedWindowMLP in wordlm.py and the saved comparison protocol. Each token embedding has 64 coordinates; concatenating 64 rows gives 4,096. A 4,096→256 hidden layer with ReLU feeds a 256→4,000 vocabulary layer. The fixed concatenation order already distinguishes slots. This baseline has no separate learned position table. Its larger first MLP matrix helps explain its parameter count.

    The trained single-head browser model, including learned positions, output projection, residual and prediction MLPSame 64-token input window · vocabulary size C = 4,000Token lookup64 rows × 64Position lookup64 rows × 64+Input E64 × 64One attention headQ, K, V: each 64 × 64A = softmax(masked scores)H = AV: 64 × 64Output WO64 × 64keep E+E′ = E + ΔElast row: 1 × 64Prediction MLP64 → 256 + ReLUVocabulary logits256 → 4,000Vocabulary softmax → next-token probabilities.

    Add learned position rows before Q, K and V. One head mixes the sources, the output projection transforms its message, and the residual preserves E. Only the last updated row feeds the prediction MLP.

    The trained single-head model uses d_model = d_k = d_v = 64. The full attention matrix has shape 64×64, with future and padding masks. The optimized browser inference path computes only the final query row, which gives the same next-token logits as taking the last row of this full diagram. The 64→256 prediction MLP replaces the baseline’s much wider 4,096→256 input layer.

    The trained four-head browser model, including learned positions, output projection, residual and prediction MLPSame 64-token input window · vocabulary size C = 4,000Token lookup64 rows × 64Position lookup64 rows × 64+Input E64 × 64Head 1: H is 64 × 16Head 2: H is 64 × 16Head 3: H is 64 × 16Head 4: H is 64 × 16Concat H: 64 × 64Output WO64 × 64keep E+E′ = E + ΔElast row: 1 × 64Prediction MLP64 → 256 + ReLUVocabulary logits256 → 4,000Vocabulary softmax → next-token probabilities.

    The input and output widths stay 64. Each head now uses 16 coordinates and its own softmax. Concatenation restores width 64 before the output projection, residual and the same prediction MLP.

    This follows MultiHeadAttentionLM in multihead.py. Each head has Q, K and V of shape 64×16 and its own 64×64 attention matrix. Its H = AV has shape 64×16. Concatenating four H matrices gives 64×64, followed by W_O [64×64]. Both attention variants add learned absolute position rows to token rows. The comparison does not isolate the effect of position encoding, because it does not train a position-free attention ablation. Head count, total width, parameter count, measured losses and device timings must be distinguished.

    More heads divide a fixed total width into smaller per-head projections1 head × 64 coordinates644 heads × 16 coordinates16161616WQ, WK, WV and WO stay 64 × 64.

    The trained comparison uses width 64. Four smaller heads give four attention patterns with the same attention parameter count as one wide head.

    Implementations often concatenate the per-head W_Q matrices into one large W_Q (and similarly for K and V). They project E first and reshape the projected coordinates into heads. They do not assign disjoint slices of raw E to different heads. Our two-head arithmetic toy uses width 4; this experiment uses width 64.

    Model; Cross-entropy ↓; Perplexity ↓ModelCross-entropy ↓Perplexity ↓Embedding → MLP3.94351.59One attention head3.44531.34Four attention heads3.36028.78

    For each run, perplexity = exp(cross-entropy using natural logs). Lower means higher probability for observed tokens. The table averages three seeds on the same test stories.

    Same 6,000-document TinyStories subset, tokenizer, 64-token context, 6,000-update budget and validation selection. Seeds 11, 29 and 47. More heads helped this experiment; this is not a guarantee for every dataset, head count or generated continuation. Full experiment and variation across seeds.

    Model; Parameters; Training / seedModelParametersTraining / seedEmbedding → MLP2,332,83258.2 sOne attention head1,321,12065.6 sFour attention heads1,321,12069.7 s

    Mean training times on Apple M2 Max/MPS, including validation. The two attention models have equal parameter counts; four heads took slightly longer.

    Compare real language models in the browserTraining prefix → held-out story → a different kind of promptCompare the continuation and generation time on your device.

    Open the live models ↗ · Work through the code · Inspect the trained experiment

    The demo runs actual checkpoints with WebGPU or WebAssembly. It includes training, held-out and outside-domain prompts and measures generation locally. Normalization, full Transformer blocks and complexity remain in the optional Part 2B reference. Formula source: Attention Is All You Need, §3.2.2. Visual teaching references: 3Blue1Brown and Jay Alammar.

    07

    Multi-head attention · a worked continuation

    Next: classify an image with the same attention operation

    Image patches become embedding rows through one shared projectionA tiny 8 × 8 grayscale imageFour 4 × 4 patchesFlatten each patch12344 rows × 16 pixel valuesWpatch16 × 4Patch embeddingsEpatch: 4 × 4The shared projection makes one embedding row per patch.

    Replace word tokens with image patches. Flatten each patch and apply the same learned projection. Attention still receives a sequence of embedding rows.

    This is a tiny shape illustration, not a trained vision model: an 8×8 grayscale image split into four 4×4 patches. Each patch has 16 pixel values; a shared 16×4 projection produces four-dimensional rows. For RGB images the flattened width is P²×3. The general sequence length is (H/P)×(W/P). The projection is learned from the image task.

    A CLS row gathers image information before a two-class predictionFour patch rows + one learned [CLS] row + position rows[CLS]patch 1patch 2patch 3patch 4E: 5 × 4Transformer encoderMHA: 2 heads × 2 coordinatesthen a per-row MLPresiduals + normalization insideUpdated rows: 5 × 4All image patches are visible: no future-token mask.Final [CLS]: 1 × 4Classifier: 4 → 2Two class logitsSoftmax → vertical-stripe / horizontal-stripe probabilities

    The [CLS] row gathers image information. Its final representation feeds a classifier trained with the image label. Next: Vision I, from pixels to an image class ↗

    For this two-class shape example, prepend one learned CLS token to the four patch embeddings, then add position embeddings to the five rows. A Transformer encoder updates all five rows. The final CLS row feeds a two-logit classifier; softmax produces class probabilities and cross-entropy uses the observed image label. Image classification does not need a causal future-token mask. The full encoder also includes per-row MLPs, residuals and normalization, which are grouped here rather than silently omitted. This is a roadmap, not a benchmark or a completed classifier. See An Image is Worth 16×16 Words. Cross-attention remains a separate optional continuation.