Chapter 4 · Section 4.2 · 50 min

Queries, keys, and values

In §4.1 the dictionary was given to us. The keys were apartment sizes, the values were rents, and the query came from outside. Inside a Transformer there is no outside. A sentence of TT tokens arrives as TT vectors, and every token needs to look things up in the other tokens. In "The animal didn't cross the street because it was too tired", the token "it" needs to find "animal", and the token "tired" needs to find whatever "it" turned out to be.

So where do the queries, keys, and values come from? The obvious answer is the token vectors themselves, which is what you did in Exercise 3.6d. This section shows why that obvious answer fails, why the fix is three separate learned projections, and exactly what each of the three is for. By the end you will have built a single attention head by hand, with numbers small enough to check on paper, and you will know the shape of every matrix involved, which is the first thing an interviewer will ask you to write down.

The naive version, where each token is its own query, key, and value

Stack the TT token vectors as the rows of a matrix X∈RT×d\mathbf{X} \in \R^{T \times d}, where dd is the model width (the embedding dimension of §1.2). Row ii is token ii's vector, which we write xi\mathbf{x}_i. (Throughout this chapter vectors belonging to tokens are stored as rows, exactly as they are in code.) The naive soft lookup of §3.6 uses xi\mathbf{x}_i as the query, every xj\mathbf{x}_j as a key, and every xj\mathbf{x}_j as a value,

sij=xi⊤xj,aij=softmax⁡j(si⋅),oi=∑jaijxj.s_{ij} = \mathbf{x}_i^\top \mathbf{x}_j, \qquad a_{ij} = \operatorname{softmax}_j(s_{i\cdot}), \qquad \mathbf{o}_i = \sum_j a_{ij}\mathbf{x}_j .

Three things go wrong.

Every token mostly finds itself. The score of a token with itself is ∥xi∥2\lVert\mathbf{x}_i\rVert^2. In high dimensions this is far larger than a typical score with another token. If the entries are roughly independent with unit variance, ∥xi∥2≈d\lVert\mathbf{x}_i\rVert^2 \approx d while xi⊤xj\mathbf{x}_i^\top\mathbf{x}_j is of order d\sqrt d (Fact 1 of §1.3). The lookup returns the token you started with, which is useless. Word2vec ran into the same problem and solved it the same way we are about to, by giving each word two different vectors (§1.5).

Relevance is forced to be symmetric. The score xi⊤xj\mathbf{x}_i^\top\mathbf{x}_j equals xj⊤xi\mathbf{x}_j^\top\mathbf{x}_i. But relevance is not symmetric. "It" needs "animal" to resolve what it refers to, while "animal" has no particular need for "it". A verb looks for its subject, and the subject is not looking for the verb in the same way.

One vector is doing three jobs. What a token is looking for, what it advertises about itself, and what it should hand over when someone finds it are different pieces of information. A pronoun is looking for a noun phrase, advertises that it is a pronoun, and should hand over, once resolved, whatever it refers to. Forcing a single vector to play all three roles means none is done well.

Here is the first problem, measured on random vectors.

Python · cell 1

With raw vectors, every token's best match is itself and it puts 99.5% of its weight there. With two different projections, even random ones, the diagonal stops being special (averaged over many draws, a token's weight on itself is about 1/T1/T), and the scores are no longer symmetric. Now we just have to make the projections learned.

Three projections, three roles

Give the layer three weight matrices and compute, for every token at once,

Q=XWQ,K=XWK,V=XWV,\mathbf{Q} = \mathbf{X}\mathbf{W}_Q, \qquad \mathbf{K} = \mathbf{X}\mathbf{W}_K, \qquad \mathbf{V} = \mathbf{X}\mathbf{W}_V,

with WQ,WK∈Rd×dk\mathbf{W}_Q, \mathbf{W}_K \in \R^{d \times d_k} and WV∈Rd×dv\mathbf{W}_V \in \R^{d \times d_v}. Row ii of Q\mathbf{Q} is token ii's query qi∈Rdk\mathbf{q}_i \in \R^{d_k}, row jj of K\mathbf{K} is token jj's key kj∈Rdk\mathbf{k}_j \in \R^{d_k}, and row jj of V\mathbf{V} is token jj's value vj∈Rdv\mathbf{v}_j \in \R^{d_v}. (Bold V\mathbf{V} is the value matrix. Italic VV remains the vocabulary size.) Then run the soft lookup of §4.1 with these,

sij=qi⊤kj,aij=esij∑lesil,oi=∑jaijvj.s_{ij} = \mathbf{q}_i^\top \mathbf{k}_j, \qquad a_{ij} = \frac{e^{s_{ij}}}{\sum_{l} e^{s_{il}}}, \qquad \mathbf{o}_i = \sum_j a_{ij}\mathbf{v}_j .

In matrix form, with the softmax applied to each row separately, S=QK⊤\mathbf{S} = \mathbf{Q}\mathbf{K}^\top, A=softmax⁡(S)\mathbf{A} = \operatorname{softmax}(\mathbf{S}), and O=AV\mathbf{O} = \mathbf{A}\mathbf{V}. (The real formula divides S\mathbf{S} by dk\sqrt{d_k}. We leave that out until §4.3, where it gets a section of its own.)

In plain words, each token asks a question (its query), every token answers how well it matches (its key), and each token collects a blend of everyone's information (their values), weighted by the match.

The three roles are worth stating precisely, because interviewers ask.

  • The query qi\mathbf{q}_i encodes what position ii is looking for.
  • The key kj\mathbf{k}_j encodes what position jj can be found by. It is an advertisement, used only to compute scores.
  • The value vj\mathbf{v}_j encodes what position jj contributes once it is found. It never affects who attends to whom.

A library is a decent analogy. You walk in with a question (the query). Each book has a catalog entry with subject headings (the key). What you take home is the book itself (the value). The catalog entry and the book are different objects, and they should be. A good catalog entry is short and easy to match. A good book is full of information.

The separation between keys and values is the less obvious one, so here is a concrete case. A previous-token head, one of the most common heads in trained language models, makes every position attend to the position just before it. To find the previous position, the keys must encode position. But what the head should deliver is the previous token's identity. With kj=vj\mathbf{k}_j = \mathbf{v}_j the head would have to deliver the position code it used for matching. With separate projections, the key reads the position part of xj\mathbf{x}_j and the value reads the token part. You will build exactly this head in Exercise 4.2b, and in §4.7 it becomes the first half of an induction head.

The similarity is learned, and it is low rank

Substitute the projections into the score,

sij=qi⊤kj=(WQ⊤xi)⊤(WK⊤xj)=xi⊤ WQWK⊤ xj=xi⊤WQK xj,s_{ij} = \mathbf{q}_i^\top\mathbf{k}_j = (\mathbf{W}_Q^\top\mathbf{x}_i)^\top(\mathbf{W}_K^\top\mathbf{x}_j) = \mathbf{x}_i^\top\, \mathbf{W}_Q\mathbf{W}_K^\top\, \mathbf{x}_j = \mathbf{x}_i^\top\mathbf{W}_{QK}\,\mathbf{x}_j,

with WQK=WQWK⊤∈Rd×d\mathbf{W}_{QK} = \mathbf{W}_Q\mathbf{W}_K^\top \in \R^{d\times d}.

In plain words, a query-key pair defines a learned bilinear similarity between token vectors, given by one d×dd \times d matrix.

That single observation explains most of the design.

  • It generalizes the dot product. With WQK=I\mathbf{W}_{QK} = \mathbf{I} we recover the naive xi⊤xj\mathbf{x}_i^\top\mathbf{x}_j. Learning WQK\mathbf{W}_{QK} lets the head decide which features matter for matching, and how features of the query should line up with features of the key.
  • It can be asymmetric. sij≠sjis_{ij} \ne s_{ji} unless WQK\mathbf{W}_{QK} is symmetric, so "it" can care about "animal" without the reverse.
  • It is low rank. rank⁡(WQK)≤dk\operatorname{rank}(\mathbf{W}_{QK}) \le d_k, because it is a product through a dkd_k-dimensional space. The head can only compare tokens along dkd_k directions. That is a restriction, but also a bargain, since the factored form costs 2ddk2dd_k parameters instead of d2d^2, and §4.5 will spend the savings on several heads that each look along different directions.
  • Only the product matters. For any invertible R∈Rdk×dk\mathbf{R} \in \R^{d_k\times d_k}, replacing WQ\mathbf{W}_Q by WQR\mathbf{W}_Q\mathbf{R} and WK\mathbf{W}_K by WKR−⊤\mathbf{W}_K\mathbf{R}^{-\top} leaves WQK\mathbf{W}_{QK} unchanged. The individual query and key vectors therefore have no meaning on their own, only their pairing does. This is why interpretability work studies WQK\mathbf{W}_{QK} directly, and it is a good answer to "what do the query vectors mean?".

The values have a matching story. In a full layer, the output of the head is multiplied by an output matrix WO∈Rdv×d\mathbf{W}_O \in \R^{d_v\times d} (with several heads, this head's block of rows of a larger matrix) that maps it back to width dd (§4.5). What a head moves from token jj into token ii is then xj⊤WVWO\mathbf{x}_j^\top\mathbf{W}_V\mathbf{W}_O, governed by the product WOV=WVWO∈Rd×d\mathbf{W}_{OV} = \mathbf{W}_V\mathbf{W}_O \in \R^{d\times d} of rank at most dvd_v. Elhage et al. (2021) call these the QK circuit, which decides where to look, and the OV circuit, which decides what to move. A head is fully described by these two d×dd\times d low-rank matrices, a point of view that pays off when we build an induction head in §4.7 and when circuit analysis reverse-engineers trained models.

Python · cell 2

Shapes, written out

Here is every shape in one head, for a batch of BB sequences. The queries may come from a sequence of length TqT_q and the keys and values from a sequence of length TkT_k. In self-attention both are the same sequence and Tq=Tk=TT_q = T_k = T. In cross-attention they differ (§4.6).

objectshapemeaning
X\mathbf{X} (query side)(B,Tq,d)(B, T_q, d)token vectors that ask
X\mathbf{X} (key side)(B,Tk,d)(B, T_k, d)token vectors that answer (the same tensor in self-attention)
WQ\mathbf{W}_Q, WK\mathbf{W}_K(d,dk)(d, d_k)query and key projections, shared by all positions
WV\mathbf{W}_V(d,dv)(d, d_v)value projection
Q\mathbf{Q}(B,Tq,dk)(B, T_q, d_k)one query per asking position
K\mathbf{K}(B,Tk,dk)(B, T_k, d_k)one key per answering position
V\mathbf{V}(B,Tk,dv)(B, T_k, d_v)one value per answering position
S=QK⊤\mathbf{S} = \mathbf{Q}\mathbf{K}^\top(B,Tq,Tk)(B, T_q, T_k)row ii holds query ii's scores against every key
A=softmax⁡(S)\mathbf{A} = \operatorname{softmax}(\mathbf{S})(B,Tq,Tk)(B, T_q, T_k)each row sums to 1, over keys
O=AV\mathbf{O} = \mathbf{A}\mathbf{V}(B,Tq,dv)(B, T_q, d_v)one output per asking position

Three facts are worth memorizing. Queries and keys must have the same width dkd_k, because they meet in a dot product. Values can have any width dvd_v, because they are only averaged. And the output has one row per query, with the value width, so the key length TkT_k disappears from the result. Notice also that the weights WQ,WK,WV\mathbf{W}_Q, \mathbf{W}_K, \mathbf{W}_V do not depend on TT. The same three matrices serve a sequence of 5 tokens or 100,000, which is what lets one model handle inputs of any length.

Warm-up

Write down every shape

Implement attention_shapes(B, T_q, T_k, d, d_k, d_v) returning a dictionary that maps each of the names "W_Q", "W_K", "W_V", "Q", "K", "V", "S", "A", "O" to its shape as a tuple, for one attention head whose queries come from a (B, T_q, d) tensor and whose keys and values come from a (B, T_k, d) tensor. This is the table above, from memory. It is also the first five minutes of many interviews.

Hidden tests, hints, and AI help, from $1.99 a month.

A worked example you can check by hand

Let's build a head that does something recognizable, with numbers small enough to verify with a pencil. The sentence is "the cat sat". Each token is a 4-dimensional vector whose coordinates are hand-made features, (is a determiner, is a noun, is a verb, is an animal),

X=(100001010010)←the←cat←sat\mathbf{X} = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 1 \\ 0 & 0 & 1 & 0 \end{pmatrix} \begin{matrix} \leftarrow \text{the} \\ \leftarrow \text{cat} \\ \leftarrow \text{sat} \end{matrix}

We want two behaviors from one head. A verb should look for a noun (its subject), and a noun should look for a determiner. And whatever is found should report whether it is an animal, plus whether it is a determiner. With dk=dv=2d_k = d_v = 2 we can write the weights directly. Rows of each weight matrix correspond to the four input features.

WQ=(00033000),WK=(01100000),WV=(01000010).\mathbf{W}_Q = \begin{pmatrix} 0 & 0 \\ 0 & 3 \\ 3 & 0 \\ 0 & 0 \end{pmatrix}, \quad \mathbf{W}_K = \begin{pmatrix} 0 & 1 \\ 1 & 0 \\ 0 & 0 \\ 0 & 0 \end{pmatrix}, \quad \mathbf{W}_V = \begin{pmatrix} 0 & 1 \\ 0 & 0 \\ 0 & 0 \\ 1 & 0 \end{pmatrix}.

Read them like this. In WQ\mathbf{W}_Q, the verb feature produces the query (3,0)(3, 0), meaning "I am looking for a noun", and the noun feature produces (0,3)(0, 3), meaning "I am looking for a determiner". In WK\mathbf{W}_K, the noun feature produces the key (1,0)(1, 0), "I am a noun", and the determiner feature produces (0,1)(0, 1), "I am a determiner". In WV\mathbf{W}_V, the value reports (is an animal, is a determiner). The factor 3 in the queries sets how sharp the lookup is, which is the temperature of §4.1 in disguise.

Multiply out. Each row of Q=XWQ\mathbf{Q} = \mathbf{X}\mathbf{W}_Q picks out the rows of WQ\mathbf{W}_Q for the features that token has,

Q=(000330),K=(011000),V=(011000),S=QK⊤=(000300030).\mathbf{Q} = \begin{pmatrix} 0 & 0 \\ 0 & 3 \\ 3 & 0 \end{pmatrix}, \quad \mathbf{K} = \begin{pmatrix} 0 & 1 \\ 1 & 0 \\ 0 & 0 \end{pmatrix}, \quad \mathbf{V} = \begin{pmatrix} 0 & 1 \\ 1 & 0 \\ 0 & 0 \end{pmatrix}, \quad \mathbf{S} = \mathbf{Q}\mathbf{K}^\top = \begin{pmatrix} 0 & 0 & 0 \\ 3 & 0 & 0 \\ 0 & 3 & 0 \end{pmatrix}.

Row by row, "the" has an all-zero query, so all its scores are 0. "cat" scores 3 against "the" (noun looking for determiner). "sat" scores 3 against "cat" (verb looking for noun). Apply the softmax to each row. A row with one score of 3 and two of 0 gives e3/(e3+2)=0.909e^3/(e^3 + 2) = 0.909 on the winner and 1/(e3+2)=0.0451/(e^3+2) = 0.045 on each of the others, so

A=(0.3330.3330.3330.9090.0450.0450.0450.9090.045),O=AV=(0.3330.3330.0450.9090.9090.045).\mathbf{A} = \begin{pmatrix} 0.333 & 0.333 & 0.333 \\ 0.909 & 0.045 & 0.045 \\ 0.045 & 0.909 & 0.045 \end{pmatrix}, \qquad \mathbf{O} = \mathbf{A}\mathbf{V} = \begin{pmatrix} 0.333 & 0.333 \\ 0.045 & 0.909 \\ 0.909 & 0.045 \end{pmatrix}.

Read the outputs. "sat" now carries (0.909,0.045)(0.909, 0.045), meaning "my subject is an animal". The verb has learned something about its subject, which is exactly the information it needed. "cat" carries (0.045,0.909)(0.045, 0.909), meaning "I have a determiner". And "the", which had nothing to ask, gets the plain average of everyone's values. That last behavior is typical. A query that matches nothing spreads its weight uniformly, because softmax has to put its weight somewhere. Trained models often handle this by parking the weight on a harmless token, the attention sink we meet in §4.4.

Notice too that "cat" attends to "the", which comes before it, and "sat" attends to "cat", also before it. Nothing in this head stops a token from looking ahead. In a language model it would have to be stopped, which is the job of the causal mask in §4.4.

Python · cell 3

Now drag the vectors yourself. The widget below has two queries and four keys in a 2-D plane, and four values in a second plane. The heatmap is the 2×42\times 4 attention matrix, and each output is drawn as the weighted average of the values.

What do queries, keys, and values each control?
Left, two queries and four keys. Right, the four values and the two outputs (dashed), each the weighted average of the values. Drag any arrow tip.
Loading interactive figure…
Core

Build a previous-token head by hand

Each token is the concatenation of a one-hot token id (vocabulary size n_vocab) and a one-hot position (sequence length T), so d=d = n_vocab + T and xt=(ewt,et)\mathbf{x}_t = (\mathbf{e}_{w_t}, \mathbf{e}_t). Write previous_token_head(n_vocab, T) returning (W_Q, W_K, W_V) such that the unmasked, unscaled head softmax⁡(QK⊤)V\operatorname{softmax}(\mathbf{Q}\mathbf{K}^\top)\mathbf{V} makes every position t≥1t \ge 1 put more than 0.9 of its weight on position t−1t-1, and outputs (approximately) the one-hot token id of the previous token, so O has shape (T, n_vocab). You choose dkd_k. The head must work for any token sequence, including repeated tokens.

Hidden tests, hints, and AI help, from $1.99 a month.

The whole head, batched

Real code handles a batch of sequences, and usually computes the three projections with one matrix multiply. Concatenate the three weight matrices side by side into WQKV=[WQ∣WK∣WV]∈Rd×(2dk+dv)\mathbf{W}_{QKV} = [\mathbf{W}_Q \mid \mathbf{W}_K \mid \mathbf{W}_V] \in \R^{d \times (2d_k + d_v)}, multiply once, and split the result along the last axis. One large matmul runs faster on a GPU than three small ones, which is why GPT-2's code has a single c_attn layer producing all three. The batch dimension rides along untouched, because every operation either acts on the last axis or is a batched matrix product over the last two.

Python · cell 4

One head with GPT-2's sizes (d=768d = 768, dk=dv=64d_k = d_v = 64) has 3×768×64=147,4563 \times 768 \times 64 = 147{,}456 weights in its three projections. GPT-2 has 12 such heads per layer, and §4.5 counts everything, including the output projection.

Interview

Batched single-head self-attention with a fused projection

Implement fused_self_attention(X, W_qkv, scale) where X has shape (B, T, d) and W_qkv has shape (d, 3 * d_k), holding [WQ∣WK∣WV][\mathbf{W}_Q \mid \mathbf{W}_K \mid \mathbf{W}_V] in that order along its columns (so dv=dkd_v = d_k). Return (O, A) with shapes (B, T, d_k) and (B, T, T), where the scores are scale * Q @ K^T. No loops. Sequences in the batch must never see each other.

Hidden tests, hints, and AI help, from $1.99 a month.

Biases, and the one that does nothing

Many implementations add a bias to each projection, qi=WQ⊤xi+bQ\mathbf{q}_i = \mathbf{W}_Q^\top\mathbf{x}_i + \mathbf{b}_Q and so on. GPT-2 and BERT do. LLaMA drops all of them, while Qwen2 keeps the query, key, and value biases and drops the rest. Here is a fact that surprises people in interviews. The key bias has no effect whatsoever on the output. Expand the score,

qi⊤(kj+bK)=qi⊤kj+qi⊤bK.\mathbf{q}_i^\top(\mathbf{k}_j + \mathbf{b}_K) = \mathbf{q}_i^\top\mathbf{k}_j + \mathbf{q}_i^\top\mathbf{b}_K .

The second term depends on the query ii but not on the key jj. It adds the same constant to every score in row ii, and softmax is invariant to that (§4.1). So the weights, the output, and therefore the loss do not depend on bK\mathbf{b}_K, and its gradient is exactly zero. (One caveat matters for modern models. With rotary position embeddings, §5.5, each key is rotated by an angle that depends on its position after the bias is added, so the bias term differs from key to key and is no longer redundant.) The query bias is different, since bQ⊤kj\mathbf{b}_Q^\top\mathbf{k}_j varies with jj and acts as a learned, query-independent preference for certain keys. The value bias passes straight through, because the weights sum to 1, so ∑jaij(vj+bV)=oi+bV\sum_j a_{ij}(\mathbf{v}_j + \mathbf{b}_V) = \mathbf{o}_i + \mathbf{b}_V. It is equivalent to a bias added after attention, which is why it can be folded into the output projection's bias.

Python · cell 5

In PyTorch

A production single head is a fused linear layer and a call to the attention kernel. We write the attention by hand here, and switch to torch.nn.functional.scaled_dot_product_attention in §4.3.

🔥 In PyTorchfor a machine with PyTorch

Note that nn.Linear stores its weight as (out_features, in_features) and computes x @ W.T. So head.qkv.weight has shape (3 d_k, d), the transpose of our WQKV\mathbf{W}_{QKV}. Mixing up the two conventions is a frequent source of shape errors when porting weights between frameworks.

Read more

  • Vaswani et al. (2017). Attention Is All You Need. Section 3.2 defines queries, keys, and values and the projections used throughout this chapter.
  • Elhage et al. (2021). A Mathematical Framework for Transformer Circuits. The QK and OV circuits, the non-uniqueness of the factorization, and why heads are best studied through WQWK⊤\mathbf{W}_Q\mathbf{W}_K^\top and WVWO\mathbf{W}_V\mathbf{W}_O.
  • Phuong & Hutter (2022). Formal Algorithms for Transformers. Precise pseudocode for every attention variant, with all shapes stated. The reference to check your notation against.
  • Sukhbaatar et al. (2015). End-To-End Memory Networks. Separate input (key) and output (value) memory embeddings, an early statement of why keys and values should differ.
  • Alammar. The Illustrated Transformer. Step-by-step pictures of the query, key, and value computation for a single token.
  • Bloem (2019). Transformers from Scratch. Builds self-attention from the naive version up, the same route as this section.

Watch next

🎬 Attention in transformers, step-by-step | Deep Learning Chapter 6 · 3Blue1BrownThe best visual explanation of queries, keys, and values as questions, advertisements, and the information that moves.
🎬 Let's build GPT: from scratch, in code, spelled out. · Andrej Karpathy · starts at 62:00Karpathy writes a single self-attention head in PyTorch, with the query, key, and value projections explained as he types.