Current: Week 7

0%

Week 7

Week 7 — Attention: read the relevant preceding context

Key questionWhen “like” appears in both [you,like] and [we,like], how does the model calculate Q/K/V, score visible positions, and produce different contextual representations and potentially different next-token logits?

Learning objectives

  • Calculate X → Q/K/V → raw scores → scaling → causal mask → attention weights → weighted values using one reproducible example.
  • Explain how attention scores are calculated and learned, and distinguish attention Softmax from vocabulary Softmax.
  • Read the axis meanings in [B,T,C], [B,T,d_head], [B,T,T], and [B,n_head,T,T].
  • Implement and inspect the fixed numerical head, a reusable causal head, and multi-head attention.
  • Explain the path from attention output to logits, the mechanism's limits and T² cost, and why Week 8 adds the rest of the Transformer block.

110 min estimated reading time

Week progress: 0 of 20 sections (0%)Course progress: 0 of 321 sections (0%)

Last week's Bigram used only the current token to look up one row of scores. Now compare the prefixes “you like” and “we like”: we want the final “like” to read different earlier information. Keep the same token IDs. To make the arithmetic manageable, this chapter explicitly uses simple four-feature input vectors and fixed projection matrices.

Scroll horizontally to view all columns.

Course data table
Study unitThe question we solve
1: What should be read?Start with the limitations of averaging and hand-picked weights. Then introduce a query as the current matching request, keys for scoring, and values as the content actually combined.
2: Calculate each stageQ/K/V → dot products → scaling → row-wise weights → weighted values. Trace every number back to the same inputs.
3: Why future information is forbiddenMatch input positions to next-token targets; explain mask direction, negative infinity, and the position axis.
4: How attention learnsConnect the last position's output to vocabulary scores and cross-entropy; inspect the gradient of one Wq parameter, then introduce multiple heads.

Run python week07_attention.py from the example directory. Observe different last-position weights for A and B, and explain why attention Softmax normalizes over positions while output Softmax normalizes over vocabulary candidates. Heads read in parallel; successive blocks transform representations in sequence.

Alternate reading, hand calculation, and code changes. Each unit can take several sessions. Existing section numbers are retained for links and reference; follow the displayed top-to-bottom learning order rather than jumping around to follow the old numbers.

Week 7 learning goal: let the same token read different prefixes

We keep Week 6's adapted words “you,” “we,” and “like,” their IDs, and the meanings of [B,T,C]. To calculate each Q/K/V entry by hand, however, we use this chapter's simple floating-point X and fixed projections—not embeddings copied from a particular Week 6 training step. Week 8 supplies a separate token-plus-position illustration; Weeks 10–12 use one shared trainable Mini GPT implementation.

Use two prompts throughout: A=[you,like] and B=[we,like]. Their final token is identical, but a contextual model should be able to let that final “like” read different earlier information. Attention provides this input-dependent information path.

Scroll horizontally to view all columns.

Fix B=2, T=2, C=4. The following matrices come from these two prompts.
Batch / PositionTensor SlotTokenMeaning
b=0, t=0x[0,0,:]youFirst token of Prompt A
b=0, t=1x[0,1,:]likeSecond token of Prompt A
b=1, t=0x[1,0,:]weFirst token of Prompt B
b=1, t=1x[1,1,:]likeSecond token of Prompt B
Concept sequence
  1. Input representations X [B,T,C] = [2,2,4]
  2. Each head projects Q / K / V with shape [2,2,2]
  3. QKᵀ produces raw scores [B,T,T] = [2,2,2]
  4. Divide by √d_k and apply the causal mask
  5. Softmax over key positions produces weights [2,2,2]
  6. Weights @ Values produces head output [2,2,2]
  7. Concatenate two heads and apply W_O to recover [2,2,4]
  8. Later Transformer computations and the LM head produce logits [2,2,V_vocab]
contextt=jtαt,jvj\operatorname{context}_t=\sum_{j\le t}\alpha_{t,j}v_j

Knowledge check

Why can two prompts ending in “like” behave identically in a Bigram but differently with attention?

1. From input X to simple context aggregation

Attention does not directly receive token IDs in this calculation. IDs are integer lookup indices; X contains embedding vectors or a previous layer's outputs. Our simplified values make each matrix multiplication manageable by hand. Actual model representations are usually learned dense floating-point vectors.

XA=[10000100],XB=[00100100]X_A=\begin{bmatrix}1&0&0&0\\0&1&0&0\end{bmatrix},\qquad X_B=\begin{bmatrix}0&0&1&0\\0&1&0&0\end{bmatrix}
XR[B,T,C]=R[2,2,4]X\in\mathbb{R}^{[B,T,C]}=\mathbb{R}^{[2,2,4]}

A simple baseline uniformly averages the visible prefix. At the final position t=1, the fixed weights are [0.5,0.5]. Different inputs can give different averages for A and B, but these reading weights do not change with the current query.

xˉA,1=[1,0,0,0]+[0,1,0,0]2=[0.5,0.5,0,0]\bar{x}_{A,1}=\frac{[1,0,0,0]+[0,1,0,0]}{2}=[0.5,0.5,0,0]
xˉB,1=[0,0,1,0]+[0,1,0,0]2=[0,0.5,0.5,0]\bar{x}_{B,1}=\frac{[0,0,1,0]+[0,1,0,0]}{2}=[0,0.5,0.5,0]

Scroll horizontally to view all columns.

Attention's advantage is not that it introduces aggregation for the first time, but that each query computes its own aggregation weights.
MechanismFinal-position weights for j=0 / j=1Can weights depend on the query?
Causal Uniform Mean[0.5, 0.5]No; the weights are fixed
Prompt A's teaching attention head[0.599, 0.401]Yes
Prompt B's teaching attention head[0.426, 0.574]Yes

Knowledge check

What is the key difference between uniform averaging and attention?

2. Understand queries, keys, and values through a search analogy

Scroll horizontally to view all columns.

Course data table
The question attention must answerNameIn the final “like” example
What is the current position looking for?Query (Q)“like” produces the current query
What makes each position matchable?Key (K)“you” or “we” provides key features to compare
What content is retrieved from a matched position?Value (V)That position's value contributes to the new representation

Think of a query as a search request, a key as searchable index features, and a value as record content. This analogy explains the roles only. The model is not searching literal strings, and no person assigns a coordinate the meaning “subject.”

Q=XWQ,K=XWK,V=XWVQ=XW_Q,\qquad K=XW_K,\qquad V=XW_V
X:[2,2,4],WQ,WK,WV:[4,2]Q,K,V:[2,2,2]X:[2,2,4],\qquad W_Q,W_K,W_V:[4,2]\quad\Longrightarrow\quad Q,K,V:[2,2,2]

Knowledge check

Once the attention weights are calculated, which of Q, K, and V is combined with those weights?

3. Why use three representations?

A position's key supplies features for matching, its value supplies content to combine, and its query determines its own matching request. All three come from the same x_t but use different parameters.

WQ,WK,WVR4×2,qt,kt,vtR2W_Q,W_K,W_V\in\mathbb{R}^{4\times2},\qquad q_t,k_t,v_t\in\mathbb{R}^{2}
xtR1×4,xtWR1×2x_t\in\mathbb{R}^{1\times4},\qquad x_tW\in\mathbb{R}^{1\times2}

Knowledge check

Why need a key not contain all the information in the value?

4. Calculate Q, K, and V from XW, then calculate raw scores

These parameters are deliberately chosen for readable arithmetic. Real models normally begin with initialized parameters and learn their values through training. Each W here is still shared across positions and prompts; we do not write a separate projection rule for every token.

WQ=[0.50.5110.50.500],WK=[0.8200.220.2200.1200],WV=[10011000]W_Q=\begin{bmatrix}0.5&0.5\\1&1\\-0.5&0.5\\0&0\end{bmatrix},\quad W_K=\begin{bmatrix}0.8\sqrt2&0\\0.2\sqrt2&0.2\sqrt2\\0&0.1\sqrt2\\0&0\end{bmatrix},\quad W_V=\begin{bmatrix}1&0\\0&1\\-1&0\\0&0\end{bmatrix}

For example, x_like=[0,1,0,0], so multiplication by each W selects its second row: q_like=[1,1], k_like=[0.2√2,0.2√2]≈[0.283,0.283], and v_like=[0,1].

QA=[0.50.511],KA=[1.13100.2830.283],VA=[1001]Q_A=\begin{bmatrix}0.5&0.5\\1&1\end{bmatrix},\quad K_A=\begin{bmatrix}1.131&0\\0.283&0.283\end{bmatrix},\quad V_A=\begin{bmatrix}1&0\\0&1\end{bmatrix}
QB=[0.50.511],KB=[00.1410.2830.283],VB=[1001]Q_B=\begin{bmatrix}-0.5&0.5\\1&1\end{bmatrix},\quad K_B=\begin{bmatrix}0&0.141\\0.283&0.283\end{bmatrix},\quad V_B=\begin{bmatrix}-1&0\\0&1\end{bmatrix}

The final “like” query is [1,1]. Its dot product with the “you” key is approximately 1×1.131+1×0=1.131. With its own “like” key, it is approximately 1×0.283+1×0.283=0.566.

For Prompt B, its dot product with the “we” key is approximately 1×0+1×0.141=0.141. The score for the “like” key remains approximately 0.566.

SA=QAKA=[0.5660.2831.1310.566],SB=QBKB=[0.07100.1410.566]S_A=Q_AK_A^\top=\begin{bmatrix}0.566&0.283\\1.131&0.566\end{bmatrix},\qquad S_B=Q_BK_B^\top=\begin{bmatrix}0.071&0\\0.141&0.566\end{bmatrix}

Knowledge check

Where does the approximately 1.131 raw score from Prompt A's final “like” to “you” come from?

5. From scores to attention weights

Divide scores by √d_k. Here d_k=2. Using the unrounded scores, Prompt A's final row becomes [0.8,0.4] and Prompt B's becomes [0.1,0.4]. The displayed raw scores [1.131,0.566] and [0.141,0.566] are rounded. The final position can read both columns, so this row has no future position to mask.

αt,j=exp(s^t,j)r=0T1exp(s^t,r)\alpha_{t,j}=\frac{\exp(\hat{s}_{t,j})}{\sum_{r=0}^{T-1}\exp(\hat{s}_{t,r})}
text
Prompt A, final query row
scaled scores = [0.8, 0.4]
exp values    = [2.226, 1.492]
sum           = 3.718
weights       = [0.599, 0.401]

Prompt B, final query row
scaled scores = [0.1, 0.4]
exp values    = [1.105, 1.492]
sum           = 2.597
weights       = [0.426, 0.574]

Scroll horizontally to view all columns.

The mathematical function is the same, but the inputs, axis, and meaning differ. An attention weight is not a next-token probability.
SoftmaxAxis being comparedQuestion answered
Attention SoftmaxT key positionsWhich positions should this query read, and with what weights?
Vocabulary SoftmaxV vocabulary tokensWhich token should come next?
P=softmax(S^,dim=1),S^,P:[B,T,T]=[2,2,2]P=\operatorname{softmax}(\widehat{S},\mathrm{dim}=-1),\qquad \widehat{S},P:[B,T,T]=[2,2,2]

Knowledge check

Why should each query row's attention weights sum to 1 before dropout?

6. Combine values using the weights

ot=j=0T1αt,jvjo_t=\sum_{j=0}^{T-1}\alpha_{t,j}v_j
text
Prompt A, final like
weights = [0.599, 0.401]
Values  = [[1,0], [0,1]]

o_A = 0.599 * [1,0] + 0.401 * [0,1]
    = [0.599, 0.401]

Prompt B, final like
weights = [0.426, 0.574]
Values  = [[-1,0], [0,1]]

o_B = 0.426 * [-1,0] + 0.574 * [0,1]
    = [-0.426, 0.574]

Both prompts end in “like,” yet their head outputs differ. The first-position keys produce different weights, and the “you” and “we” values also differ. Both differences affect the result.

P:[B,T,T] @ V:[B,T,dhead]O:[B,T,dhead]P:[B,T,T]\ @\ V:[B,T,d_{\mathrm{head}}]\longrightarrow O:[B,T,d_{\mathrm{head}}]

Knowledge check

Why do the final head outputs for A and B differ?

7. The complete scaled dot-product attention sequence

Attention(Q,K,V)=softmax ⁣(mask ⁣(QKdk))V\operatorname{Attention}(Q,K,V)=\operatorname{softmax}\!\left(\operatorname{mask}\!\left(\frac{QK^\top}{\sqrt{d_k}}\right)\right)V
Concept sequence
  1. X @ W_Q/W_K/W_V → Q、K、V
  2. Q @ Kᵀ → Raw Scores S
  3. S / √d_k → Scaled Scores
  4. Future Columns → −∞ → Masked Scores
  5. Row Softmax → Attention Weights P
  6. P @ V → Head Output O
Q:[B,T,dk] @ K:[B,dk,T]S:[B,T,T],P:[B,T,T] @ V:[B,T,dv]O:[B,T,dv]Q:[B,T,d_k]\ @\ K^\top:[B,d_k,T]\rightarrow S:[B,T,T],\qquad P:[B,T,T]\ @\ V:[B,T,d_v]\rightarrow O:[B,T,d_v]

During training, O affects later representations, the language-model head, logits, and cross-entropy. Gradients flow back from loss through O, with one path through P, Softmax, and scores to W_Q/W_K, and another through values to W_V. The model does not begin with a correct attention table; parameter updates can improve its matching and information flow while reducing prediction loss.

Scroll horizontally to view all columns.

Course data table
Backward from output O=PVIntermediate values on the pathParameters or inputs affected
Content pathO → VW_V and input X
Reading-weight pathO → P → Softmax → S → Q、KW_Q, W_K, and input X

Both paths contribute to the gradient of the shared input X, just as branches did in Week 4. V does not participate in the QKᵀ score calculation, so its direct gradient path from O does not pass through S. Identify the branches before tackling full matrix derivatives.

Knowledge check

What determines which positions receive higher scores?

8. Why divide by √d_k rather than d_k?

A dot product sums d_k products. Under a simplified model of independent zero-mean, unit-variance query/key components, each product has variance 1 and the sum has variance d_k. Its standard deviation is √d_k. Dividing by √d_k gives a typical score scale that does not grow with head width under these assumptions.

qk=i=1dkqiki,Var(qk)dk,Std(qk)dkq\cdot k=\sum_{i=1}^{d_k}q_i k_i,\qquad \operatorname{Var}(q\cdot k)\approx d_k,\qquad \operatorname{Std}(q\cdot k)\approx\sqrt{d_k}
text
Prompt A, final query
raw scores       = [1.131, 0.566]
sqrt(d_k)        = sqrt(2) ≈ 1.414
scaled scores    = [0.800, 0.400]

Prompt B, final query
raw scores       = [0.141, 0.566]
scaled scores    = [0.100, 0.400]

Large differences within a score row can make Softmax nearly one-hot, making some attention weights locally insensitive to score changes. Adding the same large constant to every score does not change Softmax. These local sensitivities also do not by themselves determine every final parameter gradient. Positive scaling preserves the row's ordering while changing its spread.

S~=S/dk,S,S~:[B,T,T]\widetilde{S}=S/\sqrt{d_k},\qquad S,\widetilde{S}:[B,T,T]

Knowledge check

What stays unchanged after dividing by √d_k, and what is moderated?

9. Self-attention shapes: what each axis means

Scroll horizontally to view all columns.

Equal dimension sizes do not imply equal axis meanings.
TensorShapeMeaning of the three axes
X[B,T,C] = [2,2,4]Batch, Token Position, Model Feature
Q / K / V[B,T,d_head] = [2,2,2]Batch, Token Position, Head Feature
Scores / Weights[B,T,T] = [2,2,2]Batch, Query Position, Key/Value Position
Head Output[B,T,d_head] = [2,2,2]Batch, Query Position, Head Feature
Q:[B,T,dk] @ K.transpose(2,1):[B,dk,T]S:[B,T,T]Q:[B,T,d_k]\ @\ K.\operatorname{transpose}(-2,-1):[B,d_k,T]\rightarrow S:[B,T,T]
P:[B,T,T] @ V:[B,T,dv]O:[B,T,dv]P:[B,T,T]\ @\ V:[B,T,d_v]\rightarrow O:[B,T,d_v]

In each [T,T]=[2,2] matrix, row t is the query position requesting information. Column j is the key position being compared and the corresponding value position. Neither axis is a feature axis.

Scroll horizontally to view all columns.

Course data table
IndexMeaning
weights[0,1,0]Weight from Prompt A's final “like” to “you”
weights[1,1,0]Weight from Prompt B's final “like” to “we”
output[1,1,:]Prompt B's final “like” head-output vector

Scroll horizontally to view all columns.

Course data table
Separate shape check: B=1, T=3, C=4, H=2Expected shape
Input[1,3,4]
Q/K/V after splitting into heads[1,2,3,2]
Scores: every query against every key[1,2,3,3]
Merge head features back together[1,3,4]

This separate axis-check example does not replace the earlier two-token arithmetic. Unequal axis sizes make mistakes such as exchanging head and time axes easier to detect.

Knowledge check

What does weights[1,1,0] mean?

10. Why GPT needs a causal mask

For a separate next-token illustration, use input [you,like] and target [like,AI]. Position t=0 must predict “like” using only “you.” Without a mask, it could read “like” directly at input position 1—its own answer. During generation, that future token has not yet been produced, so the shortcut is unavailable.

Mt,j={1,jt0,j>tM_{t,j}=\begin{cases}1,&j\le t\\0,&j>t\end{cases}
MT=2=[1011],MT=4=[1000110011101111]M_{T=2}=\begin{bmatrix}1&0\\1&1\end{bmatrix},\qquad M_{T=4}=\begin{bmatrix}1&0&0&0\\1&1&0&0\\1&1&1&0\\1&1&1&1\end{bmatrix}

Position t=3 can read positions 0,1,2,3—not only its immediate predecessor. The mask defines visibility; it does not determine the relative reading weights among visible positions.

M:[T,T] broadcast over Batch and Heads Scores:[B,nhead,T,T]M:[T,T]\ \xrightarrow{\text{broadcast over Batch and Heads}}\ \mathrm{Scores}:[B,n_{\mathrm{head}},T,T]

Knowledge check

Why does omitting the mask create a training/generation mismatch?

11. Why use −∞ before Softmax?

Scroll horizontally to view all columns.

Zero is a valid score, not a removal instruction. The exponential of −∞ is zero.
Scaled row at t=0Result
Original [0.2,0.9]The second column is in the future
Incorrect: replace the future score with 0Softmax([0.2,0]) = [0.550,0.450]
Correct: replace it with −∞Softmax([0.2,−∞]) = [1,0]
S^t,j={S~t,j,Mt,j=1,Mt,j=0,e=0\widehat{S}_{t,j}=\begin{cases}\widetilde{S}_{t,j},&M_{t,j}=1\\-\infty,&M_{t,j}=0\end{cases},\qquad e^{-\infty}=0
S^A=[0.40.80.4],S^B=[0.050.10.4]\widehat{S}_A=\begin{bmatrix}0.4&-\infty\\0.8&0.4\end{bmatrix},\qquad \widehat{S}_B=\begin{bmatrix}0.05&-\infty\\0.1&0.4\end{bmatrix}
PA=[100.5990.401],PB=[100.4260.574]P_A=\begin{bmatrix}1&0\\0.599&0.401\end{bmatrix},\qquad P_B=\begin{bmatrix}1&0\\0.426&0.574\end{bmatrix}

Knowledge check

Why not just replace a forbidden score with 0?

12. PyTorch code reproducing the teaching calculation

python
import math

import torch


torch.set_printoptions(precision=4, sci_mode=False)

sqrt_2 = math.sqrt(2)

# [B,T,C] = [2,2,4]
x = torch.tensor([
    [
        [1.0, 0.0, 0.0, 0.0],  # you
        [0.0, 1.0, 0.0, 0.0],  # like
    ],
    [
        [0.0, 0.0, 1.0, 0.0],  # we
        [0.0, 1.0, 0.0, 0.0],  # like
    ],
])

# Formula convention: [C,d_head] = [4,2]
w_q = torch.tensor([
    [0.5, 0.5],
    [1.0, 1.0],
    [-0.5, 0.5],
    [0.0, 0.0],
])

w_k = torch.tensor([
    [0.8 * sqrt_2, 0.0],
    [0.2 * sqrt_2, 0.2 * sqrt_2],
    [0.0, 0.1 * sqrt_2],
    [0.0, 0.0],
])

w_v = torch.tensor([
    [1.0, 0.0],
    [0.0, 1.0],
    [-1.0, 0.0],
    [0.0, 0.0],
])

q = x @ w_q
k = x @ w_k
v = x @ w_v

raw_scores = q @ k.transpose(-2, -1)
scaled_scores = raw_scores / math.sqrt(q.size(-1))

T = x.size(1)
causal_mask = torch.tril(torch.ones(T, T, dtype=torch.bool))
masked_scores = scaled_scores.masked_fill(~causal_mask, float("-inf"))

weights = torch.softmax(masked_scores, dim=-1)
output = weights @ v

print("Q:", q)
print("K:", k)
print("V:", v)
print("Raw scores:", raw_scores)
print("Scaled and masked scores:", masked_scores)
print("Attention weights:", weights)
print("Head output:", output)

text
Expected final rows
Prompt A weights: [0.5987, 0.4013]
Prompt B weights: [0.4256, 0.5744]

Prompt A output:  [ 0.5987, 0.4013]
Prompt B output:  [-0.4256, 0.5744]

This code uses x @ w_q, so W follows the mathematical [C,d_head] convention. nn.Linear(C,d_head) stores its weight as [d_head,C] and uses the corresponding transpose in the computation. The calculations are equivalent.

Knowledge check

Why does this example avoid a randomly initialized nn.Linear?

13. A reusable causal attention head

python
import math

import torch
import torch.nn as nn
import torch.nn.functional as F


class AttentionHead(nn.Module):
    def __init__(self, embed_dim: int, head_size: int, context_length: int):
        super().__init__()

        self.query = nn.Linear(embed_dim, head_size, bias=False)
        self.key = nn.Linear(embed_dim, head_size, bias=False)
        self.value = nn.Linear(embed_dim, head_size, bias=False)

        self.register_buffer(
            "causal_mask",
            torch.tril(
                torch.ones(
                    context_length,
                    context_length,
                    dtype=torch.bool,
                )
            ),
        )

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        # x: [B,T,C]
        _, T, _ = x.shape

        if T > self.causal_mask.size(0):
            raise ValueError("Sequence length exceeds context_length")

        q = self.query(x)  # [B,T,d_head]
        k = self.key(x)    # [B,T,d_head]
        v = self.value(x)  # [B,T,d_head]

        scores = q @ k.transpose(-2, -1)  # [B,T,T]
        scores = scores / math.sqrt(k.size(-1))

        mask = self.causal_mask[:T, :T]
        scores = scores.masked_fill(~mask, float("-inf"))

        weights = F.softmax(scores, dim=-1)
        return weights @ v  # [B,T,d_head]

Scroll horizontally to view all columns.

Course data table
Code stageFormulaShape
query/key/value(x)Q=XW_Q, K=XW_K, V=XW_V[B,T,C] → [B,T,d_head]
q @ k.transpose(-2,-1)S=QKᵀ[B,T,d_head] @ [B,d_head,T] → [B,T,T]
scores / sqrt(k.size(-1))S/√d_k[B,T,T] → [B,T,T]
masked_fillFuture → −∞[B,T,T] → [B,T,T]
softmax(dim=-1)Row Softmax[B,T,T] → [B,T,T]
weights @ vO=PV[B,T,T] @ [B,T,d_head] → [B,T,d_head]

register_buffer registers the mask so it moves with the model between devices without becoming a trainable parameter. [:T,:T] selects the relevant part of the maximum-length mask for the current sequence.

Knowledge check

Why can this AttentionHead not be added directly to input x in our residual path?

14. Why is it called self-attention?

“Self” means queries, keys, and values come from the same input sequence representations X. It does not restrict a token to itself. GPT's causal self-attention permits the current position and all earlier positions.

Q=XWQ,K=XWK,V=XWVQ=XW_Q,\qquad K=XW_K,\qquad V=XW_V

Scroll horizontally to view all columns.

Course data table
TypeQuery sourceKey/value source
Self-AttentionSequence XThe same sequence X
Cross-AttentionTarget-sequence representation or current stateAnother source sequence
S:[B,T,T][B,T,B,T]S:[B,T,T]\neq[B,T,B,T]

Prompt A's score slice contains only “you” and “like”; B's contains only “we” and “like.” Batching computes them together without concatenating them into one text.

Knowledge check

Can Prompt A's “like” read Prompt B's “we”?

15. Multi-head attention and its output projection

Each head has its own W_Q, W_K, and W_V, allowing different matching features and value representations. Heads may use different signals, but they are not guaranteed to become named grammatical specialists.

C=4,nhead=2,dhead=C/nhead=2C=4,\qquad n_{\mathrm{head}}=2,\qquad d_{\mathrm{head}}=C/n_{\mathrm{head}}=2
Concept sequence
  1. X [B,T,C]=[2,2,4]
  2. In parallel: Head 1(X) and Head 2(X), each [2,2,2]
  3. Concatenate along features to obtain [2,2,4]
  4. Multiply by W_O:[4,4] to produce [2,2,4]
MultiHead(X)=Concat(O(1),O(2))WO\operatorname{MultiHead}(X)=\operatorname{Concat}(O^{(1)},O^{(2)})W_O
python
class MultiHeadAttention(nn.Module):
    def __init__(self, embed_dim: int, num_heads: int, context_length: int):
        super().__init__()

        if embed_dim % num_heads != 0:
            raise ValueError("embed_dim must be divisible by num_heads")

        head_size = embed_dim // num_heads

        self.heads = nn.ModuleList([
            AttentionHead(embed_dim, head_size, context_length)
            for _ in range(num_heads)
        ])

        self.output_projection = nn.Linear(
            embed_dim,
            embed_dim,
            bias=False,
        )

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        head_outputs = [head(x) for head in self.heads]
        concatenated = torch.cat(head_outputs, dim=-1)
        return self.output_projection(concatenated)

The output projection does more than satisfy a shape requirement. After concatenation, features are merely side by side. W_O learns combinations across heads and supplies the [B,T,C] interface for the residual addition.

Head 2 receives the same X, not Head 1's output. Imagine two two-feature records at each position: place them together, then learn how to combine them. Successive Transformer blocks, in contrast, pass one block's output into the next.

Knowledge check

Why use W_O after concatenating the heads?

16. How attention output affects final logits

Concept sequence
  1. X [B,T,C]
  2. Multi-Head Attention [B,T,C]
  3. Later Transformer residual / FFN / layer computations [B,T,C]
  4. Language Model Head W_vocab:[C,V_vocab]
  5. Logits [B,T,V_vocab]
  6. Apply Softmax over vocabulary candidates when probabilities are needed
H:[B,T,C] @ Wvocab:[C,Vvocab]Logits:[B,T,Vvocab]H:[B,T,C]\ @\ W_{\mathrm{vocab}}:[C,V_{\mathrm{vocab}}]\rightarrow\mathrm{Logits}:[B,T,V_{\mathrm{vocab}}]

A and B already have different final-position head outputs. Multi-head mixing, W_O, and later Transformer computations can produce different H values, allowing the language-model head to produce different logits. Attention itself does not directly select a token.

Scroll horizontally to view all columns.

Course data table
ObjectTypical shapeMeaning
Attention Weights[B,n_head,T,T]Which key/value positions each query reads
Contextual Features H[B,T,C]A contextual representation at each position
Vocabulary Logits[B,T,V_vocab]A raw score for each vocabulary candidate
Vocabulary Probabilities[B,T,V_vocab]The distribution from Softmax over the logits' vocabulary axis

Knowledge check

What do attention weights and vocabulary probabilities answer?

17. The computational cost of attention

Each head's score matrix has T² entries. Across the batch and all heads there are B×n_head×T² score entries. In this explicit implementation, weights need the same number of entries, and training requires additional intermediate values and gradients.

Scroll horizontally to view all columns.

Course data table
Fix B=2 and n_head=2Score entries across the batch and headsRelative to T=2
T=22 × 2 × 2² = 16
T=10002 × 2 × 1000² = 4,000,000250,000×
Score Elements=BnheadT2\mathrm{Score\ Elements}=B\,n_{\mathrm{head}}\,T^2
Pairwise Attention Cost=O(BnheadT2dhead)=O(BT2C)\mathrm{Pairwise\ Attention\ Cost}=O(B\,n_{\mathrm{head}}\,T^2d_{\mathrm{head}})=O(BT^2C)

The causal mask forbids future reads, but this ordinary dense implementation still constructs a full T×T matrix. Optimized implementations can change materialization, memory use, and execution details; the underlying query/key axes remain important.

Knowledge check

With batch size, head count, and width fixed, what happens to score-entry count if T doubles?

18. Week 7 debugging checklist and ten essential ideas

Scroll horizontally to view all columns.

Course data table
Debugging stageWhat to check
1. XInput vectors have shape [B,T,C]; these are not token IDs
2. Q/K/VQ/K/V have shape [B,T,d_head] and come from their respective projections
3. ScoresK.transpose(-2,-1) gives scores of shape [B,T,T]
4. ScaleScores are divided by √d_k
5. MaskFuture-position scores are −∞ before Softmax
6. SoftmaxSoftmax uses dim=-1; each query row sums to 1 before dropout
7. RetrievalOutput uses Weights @ V, not @ K
8. Multi-HeadHeads concatenate along features and W_O returns width C
9. BatchDifferent batch examples do not read one another
10. LogitsThe LM head receives contextual features, not attention weights
  1. Attention allows the same current token to read information from different prefixes.
  2. X contains vector representations, not token IDs.
  3. The query supplies the current position's matching features.
  4. The key supplies learned features used for matching.
  5. The value supplies the content combined after matching.
  6. Each QKᵀ entry is one raw query–key dot-product score.
  7. Use scaled, causally masked scores before row-wise Softmax; do not treat a post-Softmax zeroing operation as the same normalized calculation.
  8. The causal mask forbids future positions while allowing history and the current position.
  9. Head concatenation and W_O produce an output with model width C.
  10. Attention outputs contextual features; the LM head produces vocabulary logits.
XQ,K,VQK/dkMaskSoftmaxPVMultiHeadHLogitsX\rightarrow Q,K,V\rightarrow QK^\top\rightarrow /\sqrt{d_k}\rightarrow\mathrm{Mask}\rightarrow\mathrm{Softmax}\rightarrow PV\rightarrow\mathrm{MultiHead}\rightarrow H\rightarrow\mathrm{Logits}

Knowledge check

If scores look correct but a future position has positive weight, which two steps should you inspect first?

19. Week 7 → Week 8: attention is not a complete Transformer block

Week 8 continues with the interface [B,T,C]=[2,2,4]. Adding token and position embeddings preserves [2,2,4]. Multi-head attention must also return [2,2,4] after concatenation and W_O so its result can be added to the residual stream.

Concept sequence
  1. Token Embedding + Position Embedding [2,2,4]
  2. Pre-Norm Multi-Head Attention [2,2,4]
  3. x + Attention(LN₁(x)) [2,2,4]
  4. Per-Position FFN [2,2,4]
  5. x₁ + FFN(LN₂(x₁)) [2,2,4]
  6. Language Model Head → Logits [2,2,V_vocab]
x1=x+Attention(LN1(x)),x2=x1+FFN(LN2(x1))x_1=x+\operatorname{Attention}(\operatorname{LN}_1(x)),\qquad x_2=x_1+\operatorname{FFN}(\operatorname{LN}_2(x_1))

Scroll horizontally to view all columns.

Course data table
ComponentMain responsibility
Position EmbeddingSupply explicit token-position information
Causal Multi-Head AttentionMix information across allowed token positions
FFNApply the same nonlinear transformation separately at every position
Residual ConnectionProvide an identity path that can help train deeper networks
Layer NormalizationControl feature scales within each position

Knowledge check

Which Week 8 component mixes positions, and which processes each position separately?