Quant Memo
Core

Queries, Keys and Values

Attention lets a model look up relevant information from anywhere in a sequence, and it does that lookup with three learned projections of the same input: a query describing what you're looking for, a key describing what each item offers, and a value carrying what gets returned when there's a match.

Prerequisites: Sequence-to-Sequence Encoder-Decoder Models, Softmax and the Log-Sum-Exp Trick

A plain Sequence-to-Sequence Encoder-Decoder Models model compresses an entire input sequence into one fixed-size context vector, and that bottleneck degrades badly on long sequences because everything has to be squeezed through one summary. The fix seems obvious in hindsight: instead of forcing the decoder to work from one compressed summary, let it look back at the original input sequence directly, at every step, and decide for itself which parts of the input are relevant right now. That "which parts are relevant" decision is exactly what attention computes, and queries, keys and values are the mechanism that makes the decision learnable rather than hand-coded.

The analogy: a librarian search

Imagine searching a library. You arrive with a query — what you're actually looking for, phrased in your own words ("books about mean reversion in pairs trading"). Every book on the shelf has a key — a short descriptor, like a catalog tag, that summarizes what that book is about, independent of what anyone happens to be searching for. You compare your query against every book's key and get a relevance score for each — high for a close match, low for an unrelated book. Then you don't walk away with the keys — you walk away with the actual value: the book's contents. Attention runs this exact search, numerically, at every position of a sequence, simultaneously: every position emits a query (what it's looking for), every position (including itself) offers a key (what it has to offer) and a value (what gets returned if selected), and the output is a blend of values weighted by how well each key matched the query.

The maths

Given a set of input vectors x1,,xnx_1, \dots, x_n (each, say, a token or timestep's embedding), three learned weight matrices project each input into a query, key, and value:

qi=xiWQ,ki=xiWK,vi=xiWVq_i = x_i W_Q, \qquad k_i = x_i W_K, \qquad v_i = x_i W_V

In words: the same input vector xix_i produces three different roles by being multiplied by three different learned matrices — WQW_Q, WKW_K, WVW_V. These are what the network actually learns during training; nothing about "query" or "key" is hand-designed, only the mechanism that uses them is fixed.

For a given query qiq_i, its attention score against every key kjk_j in the sequence is a scaled dot product:

score(qi,kj)=qikjdk\text{score}(q_i, k_j) = \frac{q_i \cdot k_j}{\sqrt{d_k}}

In words: measure how well query ii and key jj align via a dot product (large when the two vectors point in a similar direction, i.e. when they "match"), then divide by dk\sqrt{d_k} — the square root of the key dimension — purely to keep the scores from growing too large as dkd_k increases, which would otherwise push the next step's softmax into a regime with vanishing gradients.

These scores across all jj are turned into weights that sum to 1 via softmax, then used to blend the values:

Attention(qi)=j=1nsoftmaxj ⁣(qikjdk)vj\text{Attention}(q_i) = \sum_{j=1}^{n} \text{softmax}_j\!\left(\frac{q_i \cdot k_j}{\sqrt{d_k}}\right) v_j

In words: normalize the scores into a probability-like distribution over all nn positions, then take a weighted average of every position's value vector using those weights. Position ii's output is a blend of everyone's values, leaning heavily on whichever positions its query matched best. Unlike a fixed-size context vector, this readout can pull directly from any position in the sequence, however far away, with a weight the model computes fresh for every query — nothing forces distant information to decay the way it does in a recurrent chain.

query keys softmax weights 0.05 0.70 0.05 0.20 values weighted sum out
The query is compared against all four keys (line thickness = match strength), converted to weights via softmax, and those weights blend the four values into a single output vector.

Worked example 1: computing attention weights by hand

Take n=3n = 3 positions with 2-dimensional keys and a query q=(1,0)q = (1, 0). Keys: k1=(1,0)k_1 = (1, 0), k2=(0,1)k_2 = (0, 1), k3=(0.7,0.7)k_3 = (0.7, 0.7). Use dk=2d_k = 2, so dk1.414\sqrt{d_k} \approx 1.414.

Dot products: qk1=1q \cdot k_1 = 1, qk2=0q \cdot k_2 = 0, qk3=0.7q \cdot k_3 = 0.7. Scaled scores:

11.4140.707,01.414=0,0.71.4140.495\frac{1}{1.414} \approx 0.707, \qquad \frac{0}{1.414} = 0, \qquad \frac{0.7}{1.414} \approx 0.495

Softmax over (0.707,0,0.495)(0.707, 0, 0.495): exponentiate, e0.7072.028e^{0.707} \approx 2.028, e0=1e^{0} = 1, e0.4951.640e^{0.495} \approx 1.640, sum 4.668\approx 4.668.

weights(0.434, 0.214, 0.351)\text{weights} \approx (0.434,\ 0.214,\ 0.351)

Position 1's key matched the query best (raw dot product 1, the highest), so it gets the largest weight, 0.434 — but position 3's key, only a partial match, still earns a meaningful 0.351, not zero. This is the whole point of softmax here: it's a soft, graded lookup, not a hard "pick the single best match and ignore everything else."

Worked example 2: blending values with those weights

Continue the example with values v1=(2,0)v_1 = (2, 0), v2=(0,2)v_2 = (0, 2), v3=(1,1)v_3 = (1, 1). The output is the weighted sum using the weights just computed, (0.434,0.214,0.351)(0.434, 0.214, 0.351):

out=0.434(2,0)+0.214(0,2)+0.351(1,1)\text{out} = 0.434(2,0) + 0.214(0,2) + 0.351(1,1) =(0.868,0)+(0,0.428)+(0.351,0.351)=(1.219, 0.779)= (0.868, 0) + (0, 0.428) + (0.351, 0.351) = (1.219,\ 0.779)

The output vector (1.219,0.779)(1.219, 0.779) is not any single input's value — it's a genuine blend, dominated by v1v_1 (since it got the largest weight) but pulled toward v2v_2 and v3v_3 proportionally. Change the query and every weight recomputes fresh; there's no fixed routing, which is exactly what lets the same mechanism serve completely different lookups at every position, at every layer, without re-architecting anything.

Matrix explorer
dashed = before · solid = after
det 1.25trace 2.50λ 1.81, 0.69area scales by 1.25×

WQW_Q, WKW_K, WVW_V are each just a learned linear map applied to the input, the same kind of matrix transform this explorer visualizes as a stretch and rotation of a shape. Attention's expressive power comes from three differently-trained transforms of the same input feeding into a similarity comparison, not from any one of them individually.

Query, key and value are three different learned linear projections of the same input. The query asks "what am I looking for," the key answers "what do I offer," and the dot product between them measures the match — but what actually gets returned and blended into the output is the value, a separate projection carrying the content itself, not the descriptor used to find it.

What this means in practice

This query-key-value lookup is the mechanism underneath every transformer, and by extension underneath most modern large language models and increasingly, sequence models used for price and order-flow forecasting. Its practical draw over a recurrent architecture is that any position can attend directly to any other position in one step, with no decay over distance — useful when a relevant piece of context (a macro print, a large block trade) could be arbitrarily far back in the window and needs to be found on its merits, not on its recency. Its cost is that comparing every query against every key is O(n2)O(n^2) in sequence length, which is exactly the scaling problem that motivated the state-space alternatives in Mamba and Selective State Spaces.

The trap: forgetting the dk\sqrt{d_k} scaling term, or misunderstanding why it's there. Without it, dot products between high-dimensional query and key vectors can grow large in magnitude purely as a side effect of dimensionality (more terms summed in the dot product means larger typical magnitudes), which pushes softmax into a saturated regime where one weight goes to nearly 1 and the rest to nearly 0 — the "soft," graded lookup degenerates into a near hard-max, and gradients through that near-saturated softmax vanish, stalling training. The scaling isn't a minor numerical nicety; skipping it is a common, hard-to-diagnose source of training instability in from-scratch attention implementations.

Related concepts

Practice in interviews

Further reading

  • Vaswani et al., Attention Is All You Need (2017)
  • Bahdanau, Cho & Bengio, Neural Machine Translation by Jointly Learning to Align and Translate (2014)
ShareTwitterLinkedIn