Queries, keys and values, step by step

Attention is three matrix multiplications to make queries, keys and values, one more to compare every query with every key, a softmax to turn scores into weights, and a last multiplication to mix the values. Here is every number.

Key idea
Q = XW_Q, K = XW_K, V = XW_V; scores QKᵀ/√d; causal mask; softmax by row; output AV
Real world
A room of people at a networking event. Each person holds a card saying what they’re looking for (query) and wears a badge saying what they offer (key). Everyone walks towards the badges that match their card, and listens to those people (values) in proportion.
In an LLM
In “The cat sat on the”, the last “the” has a query that looks for a noun. The key of “cat” matches it best, so the output for “the” is mostly cat’s value: the model has carried information about the cat forward to the position that will predict the next word.

The recipe

The previous page described attention as a soft lookup. Here is the exact computation one attention head does. The input is a matrix XX with one row per token (5 tokens × 4 numbers in our toy model; thousands of numbers in a real one).

  1. Project. Multiply XX by three learned matrices to get three new matrices, each with one row per token:
    • Q=XWQQ = X W_Q: each token’s query, what it is looking for.
    • K=XWKK = X W_K: each token’s key, what it contains.
    • V=XWVV = X W_V: each token’s value, what it passes on.
  2. Score. S=QK⊤S = Q K^\top: the dot product of every query with every key. Entry (i,j)(i, j) says how well token ii’s query matches token jj’s key.
  3. Scale. Divide by d\sqrt{d}, where dd is the length of a query.
  4. Mask. Set every score where jj comes after ii to −∞-\infty, so tokens can’t see the future.
  5. Softmax each row, turning scores into weights AA that are positive and add up to 1.
  6. Mix. O=AVO = A V: each token’s output is the weighted average of the value rows.
Attention⁡(Q,K,V)=softmax⁡ ⁣(QK⊤d+M) V\operatorname{Attention}(Q, K, V) = \operatorname{softmax}\!\Big(\frac{Q K^\top}{\sqrt{d}} + M\Big)\, V

where MM is the mask: 0 where looking is allowed, −∞-\infty above the diagonal.

Try it

This is a real attention head with hand-picked weights so you can read it. The four embedding numbers mean thing, action, small word and first token. Press play, then click a token to follow its row, and hover any cell of a result to see exactly how it was computed.

X
follow:
X5 × 4
thingactsmallfirstThe0.10.01.01.0cat1.00.10.00.0sat0.21.00.00.0on0.00.30.80.0the0.10.01.00.0
−2.02.0
blue positive · red negative · purple probability
what’s happening
Each token is a row of 4 numbers (its embedding). Real models use thousands; here each column has a meaning you can read: thing, action, small word, first token.
Five tokens, four numbers each: a 5 × 4 matrix. Attention will let each token gather information from the others.
One attention head on a 5-token sentence, with every number shown. Play it, or step with the arrows. Click a token to follow its row; hover any cell in the result to see the arithmetic behind it.

Reading the matrices

Follow the last “the” through the steps:

  • Its embedding is mostly small word. WQW_Q maps small word onto the first query dimension, so its query says “I’m looking for a thing”.
  • WKW_K maps thing onto that same first dimension, so “cat” has a large first key number.
  • Their dot product is the largest score in the row. After scaling, masking and softmax, “the” gives about half of its attention to “cat”.
  • The output row for “the” is mostly cat’s value: a vector that says thing.

Nobody wrote these rules into a real model. WQW_Q, WKW_K and WVW_V start random and are learned by gradient descent, because moving the right information to the right place lowers the next-token loss. Real heads learn patterns like “look at the previous token”, “look at the subject of the sentence” or “look at what followed this token last time”: see real attention patterns.

example · a networking event

Everyone at the event has a card in their hand saying what they are looking for (“investors”), and a badge saying what they are (“founder”). Each person compares their card with every badge in the room, walks towards the best matches, and listens to them, more to the better matches. What they hear is the value: not the badge itself, but what that person has to say. The badge (key) decides who you listen to; the value decides what you learn.

example · “it” finding “the cat”

In “The cat sat on the mat because it was tired”, the token “it” needs to know what it refers to. In GPT-2 small, head 3 of layer 4 gives 84% of the attention from “it” to “cat”, more than to “mat” or anything else. Its query for “it” matches the key of “cat”, so cat’s value, information about the cat, is added at the position of “it”. Later layers can then predict words that suit a cat.

Every token at once

The widget follows one row, but the matrices compute every token’s attention in one go. That is why attention runs fast on GPUs, which are built for big matrix multiplications, and why training can process all positions of a sequence in parallel. The mask is what makes this safe: every row only uses the rows above it.

Shapes

With TT tokens, model width dmodeld_{\text{model}} and head size dd:

MatrixShapeIn the widgetGPT-2 small, 1 head
XXT×dmodelT \times d_{\text{model}}5 × 4T × 768
WQ,WK,WVW_Q, W_K, W_Vdmodel×dd_{\text{model}} \times d4 × 3768 × 64
Q,K,VQ, K, VT×dT \times d5 × 3T × 64
S,AS, AT×TT \times T5 × 5T × T
OOT×dT \times d5 × 3T × 64

The T×TT \times T score matrix is why attention gets expensive for long texts: double the length and it has four times as many entries. See what a forward pass costs.

going deeper

Per-row form

For a single query position ii:

oi=∑j≤iaij vj,aij=exp⁡ ⁣(qi⋅kj/d)∑j′≤iexp⁡ ⁣(qi⋅kj′/d)o_i = \sum_{j \le i} a_{ij}\, v_j, \qquad a_{ij} = \frac{\exp\!\big(q_i \cdot k_j / \sqrt{d}\big)}{\sum_{j' \le i} \exp\!\big(q_i \cdot k_{j'} / \sqrt{d}\big)}

In code

import torch, math
 
def attention(X, Wq, Wk, Wv):
    Q, K, V = X @ Wq, X @ Wk, X @ Wv          # (T, d) each
    S = Q @ K.T / math.sqrt(Q.shape[-1])      # (T, T)
    T = X.shape[0]
    mask = torch.triu(torch.ones(T, T, dtype=torch.bool), diagonal=1)
    S = S.masked_fill(mask, float("-inf"))
    A = S.softmax(dim=-1)                     # each row sums to 1
    return A @ V                              # (T, d)

In practice you call torch.nn.functional.scaled_dot_product_attention(Q, K, V, is_causal=True), which computes the same thing with a fused kernel (FlashAttention) that never stores the full T×TT \times T matrix.

In papers

Related

  • attention-intuition - Attention lets each token pull in information from other tokens. It works like a dictionary lookup that never fails: instead of returning the one entry whose key matches, it returns a weighted average of every entry, weighted by how well each key matches the question.
  • scaling - Dot products of long vectors are big numbers: their spread grows with the square root of the length. Big scores push softmax into giving one token everything, and a saturated softmax stops learning. Dividing the scores by √d keeps them at a sensible size whatever the head size.
  • causal-mask - A language model must predict each token without seeing it. The causal mask is a triangle of −∞ added to the attention scores so that every token can only look at itself and earlier tokens. It also lets training check a prediction at every position of a text in one pass.
  • multi-head - One attention head can only follow one pattern at a time. A transformer layer runs several heads side by side, each on its own slice of the vector, so different heads can track different relations at once. Their outputs are glued back together and mixed by one more matrix.