Concept units

Attention: queries, keys, values

Advanced

Read these first if they are new to you

The idea in one paragraph

Attention lets each token collect information from the other tokens. Each token makes three vectors from its current vector (in the first layer, its embedding): a query, which describes what it looks for; a key, which describes what it offers; and a value, the information it passes on. A token compares its query with every key, turns the match scores into weights with softmax, and adds up the values with those weights. The result is the token's new vector, made mostly of the values whose keys best match its query.

A dot product scores how well a query matches a key

A token is one item's vector, such as the embedding of a word or of a point. Attention needs a score for "how well does this query match that key". It uses the dot product: multiply the two vectors entry by entry and add the results.

q · k = q1k1 + q2k2 + … + qdkd

For example, [1, 2] · [1, 1] = 1 × 1 + 2 × 1 = 3. The dot product is large when the two vectors point the same way, zero when they are at right angles, and negative when they point apart. It also grows with the length of either vector. Each entry of a matrix times a vector is a dot product of one row with the vector, so you have met this sum before.

One token's turn: score, scale, softmax, weighted sum

Follow one token as it attends to three tokens. Its query is q = [1, 2]. The three tokens have the keys [1, 0], [0, 1] and [1, 1], and the values [10, 0], [0, 10] and [5, 5]. Each key has 2 entries, so the key size is dk = 2.

Predict first. Which key matches the query best? Which value will the result lean toward?

Worked example: one query, three keys
TokenKey kValue vScore q · kScore / √2Weight
1[1, 0][10, 0]10.7070.140
2[0, 1][0, 10]21.4140.284
3[1, 1][5, 5]32.1210.576
  1. Score. Take the dot product of the query with each key: 1, 2 and 3.
  2. Scale. Divide each score by √dk = √2 ≈ 1.414. A later section explains why.
  3. Softmax. Apply softmax to turn the scaled scores into weights: e0.707 = 2.028, e1.414 = 4.113 and e2.121 = 8.342. They add up to 14.483, and each divided by that sum gives 0.140, 0.284 and 0.576. The weights are positive and add up to 1.
  4. Weighted sum. Add the values with these weights: 0.140 × [10, 0] + 0.284 × [0, 10] + 0.576 × [5, 5] = [4.28, 5.72].

Token 3's key matches best, so its value [5, 5] gets the largest weight. The other tokens still contribute, which pulls the result to [4.28, 5.72].

A longer query picks more sharply

The figure below runs the same four steps for four tokens, A to D. The left panel shows their keys and the query. The right panel shows their values and the result, which is the weighted average of the values. Every dot is a 2D vector, drawn from the center.

One attention head: drag the query and watch the weights
Keys and the query (drag it) q Values and the result out
q = [1.0, 1.0]
TokenScore / √2Weight

Try it. Drag the query until token C gets more than 90% of the weight. Pointing the query at C is not enough. What else did you have to change? (You can also select the query and use the arrow keys.)

Answer

You had to make the query longer, by dragging it far from the center. Doubling the query's length doubles every score. Softmax turns bigger gaps between scores into more lopsided weights, so a long query concentrates the weight on the best match, and a short one spreads it.

What to notice. The result always lies inside the diamond whose corners are the four values, because the weights are positive and add up to 1. With the query at the center, every score is 0 and each token gets 0.25.

All tokens at once: one line of matrix algebra

In self-attention, every token computes its own query, key and value from its vector x, using three learned matrices:

q = WQx,   k = WKx,   v = WVx

Training sets these matrices, so the network decides what a query looks for and what a key offers. Every token then takes its turn, as in the worked example. Stack the n queries as the rows of a matrix Q, and the keys and values likewise as K and V. All the turns together are then exactly

Attention(Q, K, V) = softmax( QKT / √dk ) V

Here KT is K turned on its side (its transpose), so QKT is the table of every query's dot product with every key, with n rows and n columns. Softmax works on each row separately, so each token's weights add up to 1. Multiplying by V forms each token's weighted sum of values. The output has one row per token.

attention.py

import numpy as np

def softmax(s):
    # Subtracting the max first avoids overflow in exp.
    # axis=-1 works along each row. keepdims=True keeps the results in a column,
    # so that each row uses its own max and its own sum.
    e = np.exp(s - s.max(axis=-1, keepdims=True))
    return e / e.sum(axis=-1, keepdims=True)

def attention(Q, K, V):
    d_k = Q.shape[-1]
    weights = softmax(Q @ K.T / np.sqrt(d_k))       # one row of weights per query
    return weights @ V, weights

# The worked example: one query, three keys and values.
Q = np.array([[1.0, 2.0]])
K = np.array([[1.0, 0.0], [0.0, 1.0], [1.0, 1.0]])
V = np.array([[10.0, 0.0], [0.0, 10.0], [5.0, 5.0]])
out, w = attention(Q, K, V)
print(np.round(w, 3), np.round(out, 2))

# Self-attention: 5 tokens with 4 numbers each, as the rows of X.
rng = np.random.default_rng(0)
X = rng.normal(size=(5, 4))
Wq, Wk, Wv = rng.normal(size=(3, 4, 4))         # three random 4 x 4 matrices
def self_attention(X):
    return attention(X @ Wq, X @ Wk, X @ Wv)[0]  # [0]: the output, not the weights

perm = [3, 0, 4, 1, 2]                          # a new order for the tokens
# [perm] reorders the rows. np.allclose is True if two arrays match up to rounding.
print(np.allclose(self_attention(X)[perm], self_attention(X[perm])))
[[0.14  0.284 0.576]] [[4.28 5.72]]
True

In code, tokens are the rows of a matrix X, so the queries are X @ Wq, where Wq is WQ turned on its side (its transpose). Each row of X @ Wq is then one token's query WQx. The last line is explained two sections below.

Dividing by √dk keeps the softmax from saturating

Real models use keys of 32 to 128 numbers, not 2. A dot product adds up dk products, so its typical size grows with dk. If the entries of q and k are independent random numbers with mean 0 and spread (standard deviation) 1, their dot product has mean 0 and spread √dk. Dividing by √dk brings the spread back to 1, whatever the key size.

scaling.py

import numpy as np

rng = np.random.default_rng(0)
q = rng.normal(size=(10000, 64))     # 10,000 random queries, d_k = 64
k = rng.normal(size=(10000, 64))
scores = (q * k).sum(axis=1)         # 10,000 dot products
print(round(scores.std(), 2), round((scores / 8).std(), 2))
8.08 1.01

Why does the spread matter? Softmax of the scores [8, 0, −8] is [0.99966, 0.00034, 0.0000001]: almost all the weight goes to one token. Softmax of [1, 0, −1] is [0.665, 0.245, 0.090]. When softmax is that lopsided we say it saturates. Small changes to the scores then barely change the weights, so the training signal that should adjust the queries and keys becomes tiny, and learning stalls.

Without positions, attention treats its tokens as a set

Nothing in the formula looks at where a token sits in the list. Shuffle the input tokens, and each token still meets the same keys and values, so it gets the same output. The outputs come out shuffled in the same way. The last line of attention.py checks this. In the terms of the permutation invariance unit, self-attention is permutation equivariant.

For a point cloud, this is what we want: the points have no natural order, and each point's coordinates already say where it is. For a sentence, the order of the words matters. So language models give each token information about its position, for example by adding Fourier features of the word's position to its embedding.

Multi-head attention runs several attentions side by side

One set of weights per token can follow only one notion of "match". Multi-head attention runs h attentions, called heads, in parallel. Each head has its own WQ, WK and WV, which make vectors with fewer entries: usually dk = d / h, where d is the number of entries in a token. Each head computes its own weights and its own weighted sum. The h results are joined end to end into one vector of d entries and multiplied by one more learned matrix, WO.

For example, the original transformer used tokens of 512 numbers and 8 heads with keys of 64 numbers. The total work is about that of one head with keys of 512 numbers. In a model of points, one head may learn to weight nearby points and another to weight points with similar features. Nothing forces a head to specialize; training finds whatever helps.

What this page leaves out

Language models that write one word at a time add a mask: they set the scores for later words to −∞ before the softmax, so a word cannot attend to words that come after it. Libraries also add a bias vector to each projection, and often randomly drop some weights during training (dropout). The names "query", "key" and "value" describe the roles in the formula; the network learns what the vectors contain.

Check yourself

  1. A query has dot product 0 with every one of four keys. What are the weights, and what is the output?

    Answer

    All scaled scores are 0, and softmax of equal scores gives equal weights: 0.25 each. The output is the plain average of the four values.

  2. Work a similar problem. The query is [2, 0]. The keys are [1, 0] and [0, 1], with values [4, 0] and [0, 4]. What is the output?

    Answer

    Scores: 2 and 0. Divided by √2: 1.414 and 0. Softmax: e1.414 = 4.113 and e0 = 1, so the weights are 4.113 / 5.113 = 0.804 and 0.196. Output: 0.804 × [4, 0] + 0.196 × [0, 4] = [3.22, 0.78].

  3. A model uses keys of 256 numbers but forgets to divide by √dk. What goes wrong?

    Answer

    With random entries the scores have a spread of about √256 = 16 instead of 1. Softmax then puts nearly all the weight on one token, the weights hardly respond to small changes, and training of the queries and keys slows to a crawl.

  4. Self-attention without position information reads five points in the order A, B, C, D, E and gives the outputs oA … oE. What does it give for the order E, D, C, B, A?

    Answer

    The same five outputs, in the order oE, oD, oC, oB, oA. Each point meets the same keys and values either way.

Go further

Optional background, for readers who want more than this page.

Where this shows up

Units that build on this one