Module 2 — Query, key, value: attention step by step
Module 1 promised that attention is a weighted average of values, with weights that come from a match between a query and a set of keys. This module makes that promise precise, computes it by hand on three tokens, and lands on the first block of the Transformer we build across the course.
The three roles, in plain English
For every token in the sequence, attention builds three projections.
- The query carries the question "what am I looking for?". It represents the current token as a searcher.
- The key carries the offer "here is what I contain". It represents each token as an index.
- The value carries the content that will actually be read once the match is found. Nothing forbids and being different: modules 6 and 7 exploit that separation.
The three vectors come from three different learned linear projections of the same input:
The distinction between roles is entirely produced by these three matrices. At initialisation they are random and the three roles are indistinguishable; after training, each has learned its specialty.
The formula, then the arithmetic
The 2017 paper gives the operation in one line:
Five steps hide inside it. Read them in order:
- Compute the similarity between each query and each key: , a matrix of dot products.
- Scale by so that variance stays under control as grows.
- Apply softmax along each row so that the coefficients become non-negative weights that sum to one.
- Multiply those weights by to obtain a weighted average of values.
- Return one output vector per query.
The output has the same length as the input: attention transforms a sequence into another sequence of the same length, where each position now depends on all the others.
By hand, on three tokens
Take three tokens with . Suppose the queries, keys and values are already computed:
Step 1 — similarities. gives every dot product between a query and a key:
Step 2 — scaling. Divide by :
Step 3 — softmax on each row. Exponentiating and normalising row by row gives approximately:
Each row sums to one. Read them as "attention distributions": for token 1, most weight sits on itself (0.38) and on token 3 (0.42); token 3 pays some attention to everyone but leans on itself (0.48).
Step 4 — weighted average. gives the output. For row 1, .
Step 5 — output. The three output rows are the new representations of the three tokens. Each mixes information from all three, in proportion to how much they matched the current query.
That is the entire operation. Everything else in this course is variations on this pattern.
Why the scaling by
The scaling is not decoration. Without it, the mechanism silently breaks when grows.
Take two random vectors of dimension with unit-variance components. Their dot product has variance : the numbers grow with dimension. Feeding a large number into softmax pushes the distribution towards a one-hot, which is close to argmax. Gradients through such a softmax are near zero everywhere except on the winning coordinate, and training stalls.
Dividing by brings the variance back to one, whatever the head size. The softmax then stays soft enough for gradients to flow. This is one of those single-line design choices that seem cosmetic and are, in fact, load-bearing.
In PyTorch, once and for all
import torch
import torch.nn.functional as F
def attention(q, k, v):
d_k = q.size(-1)
scores = q @ k.transpose(-2, -1) / d_k ** 0.5
weights = F.softmax(scores, dim=-1)
return weights @ v, weights
# Toy run with three tokens, d_k = d_v = 2
q = torch.tensor([[1.0, 0.0], [0.0, 1.0], [1.0, 1.0]])
k = q.clone()
v = torch.tensor([[10.0, 0.0], [0.0, 10.0], [5.0, 5.0]])
out, w = attention(q, k, v)
print(out)
print(w.sum(dim=-1)) # each row sums to 1
Save this function under attention.py: it is the first block of the Transformer we assemble at module 10. Later modules add batching, multiple heads and masking on top, without touching this core.
It is tempting, on toy examples, to set . That works arithmetically and hides the whole point. The reason attention works is that the three projections specialise during training — a query learns to look for what a key learns to expose, and both differ from the value. Skip the projections and you have removed the learning surface.
Complexity — a taste of module 9
Computing produces a matrix of size for a sequence of length . That is quadratic in the sequence length, in both compute and memory. For it is fine, for it is a hundred million entries per head per layer. Module 9 shows how the community pushes that ceiling; for now, just note that the price of "each token looks at every other token" is .
Choosing around 64 matches the effective hidden size of a strong LSTM. It is a sensible default that keeps arithmetic readable in printouts and stays within a laptop's memory in module 10.
In summary
- Attention builds three learned projections of the input — query, key, value — then reads a weighted average of the values, weighted by the softmax of the scaled dot product between queries and keys.
- The formula hides five steps: similarity, scaling, softmax, weighted sum, per-query output.
- The scaling by is essential: without it, softmax saturates as dimension grows and gradients vanish.
- The cost is in the sequence length, which module 9 will revisit; the code fits in five lines of PyTorch.
Next module: multi-head attention, which runs several of these operations in parallel and lets each head specialise on a different pattern.