Skip to content
DeepForgeby svx
All articles
Deep Learning8 min read · 1 interactive demo

Attention Is a Heatmap

Strip away the code and attention is a table of dot products, a softmax on every row, and a weighted average of values.

Attention begins with a sequence of vectors, one per token — a token being a word, a piece of a word, or any other unit the model works with. Each vector describes what that position means so far. The question attention answers is relational: for this position, which other positions carry information I need?

The answer is a weighted average of the other positions' vectors, where the weights are learned from data rather than fixed by distance. Each position gets its own private set of weights. That is the whole idea. Everything else in this article exists to make that average expressive and stable.

Each token's vector is projected three ways, 'projected' meaning multiplied by a matrix the model learned during training.

- The query q = x·W_Q is what this position is looking for. - The key k = x·W_K is what this position offers to others. - The value v = x·W_V is the content that gets passed along if this position is chosen.

Here x is the token's vector, and W_Q, W_K, and W_V are the three learned matrices. A query and a key are compared with a dot product: multiply matching entries and add the products. A large positive q·k means the query is looking for something this key advertises.

Gather all the queries into a matrix Q, all the keys into K, and all the values into V. Then every comparison at once becomes one matrix multiplication, Q·Kᵀ, where the small T means rows and columns are swapped. Entry (i, j) of the result says how much query i likes key j.

The scores are divided by √d_k, where d_k is the number of entries in each query and key vector. The square root keeps the numbers from growing with dimension.

Suppose the entries of q and k are independent random numbers with average zero and spread one. Their dot product is then a sum of d_k terms, so it gets bigger as d_k grows. Feed huge scores into the step that turns them into weights and it saturates: one weight becomes 1 and the rest become 0. The slope that learning needs dies, and training stalls. Dividing by √d_k holds the scores at a workable scale no matter how wide the vectors are.

The step that turns scores into weights is called softmax. It is applied to each row of scores separately, and each row becomes weights between 0 and 1 that add up to exactly 1 — a budget of attention spent across the keys.

A peaked row means the query is locked onto one position. A flat row means it is spreading its attention widely. The entropy of the row, a number that measures how spread out the weights are, summarizes which case you are in.

QKᵀ / √d, one softmax per row
j1thej2catj3satj4onj5thej6mati1thei2cati3sati4oni5thei6mat20%17%16%16%20%12%13%24%22%9%13%19%13%23%28%8%13%15%21%12%10%23%21%14%20%17%16%16%20%12%13%20%18%12%13%25%every row sums to 100% · darker = more attention
q(sat) · k(sat) = 1.920 → /√4 = 0.960 → softmax row = 27.7%
  • sat 28%
  • cat 23%
  • mat 15%

View: softmax weights. Focused cell: query "sat" (row 3) against key "sat" (column 3). q·k = 1.920; scaled by 1/√d = 0.960; softmax weight = 27.7%. Row "sat" attends most to "sat" 28%, "cat" 23%, "mat" 15%.

In the demo, flip between the raw scaled scores and the attention weights, and hover a cell to see one query-key pair. Then follow the output. It is the weighted average of the values: softmax(QKᵀ/√d_k)·V. Row i of the output mixes all the value vectors, using the weights from row i of the attention table.

Notice what the shapes do: one output vector per input token, each of the same length as the values. That is what lets attention slot into a larger network. Around it sit residual connections (which add a layer's input back to its output), normalization steps, and a small feed-forward network, but the mixing itself is exactly this one multiplication.

Scaled dot-product attention pipelineA pipeline: input tokens are projected into queries, keys, and values. Queries and keys produce an n by n score matrix, which is divided by the square root of the key dimension and softmaxed one row at a time. The resulting weights multiply the values into one output vector per token.scaled dot-product attention · one headd_k = dimension of one keyinput tokensx₁ … xₙQ · K · Vx·W_Q x·W_K x·W_VS = Q·Kᵀn×n dot productsS / √d_kkeeps scores sanerow softmaxevery row sums to 1output = A·Vweighted averageoutput tokensy₁ … yₙ · same dim as vVsoftmax(Q·Kᵀ / √d_k) · Vevery token leaves as a weighted mixture of the values it selected
The whole mechanism in one pass: project every token into a query, a key, and a value; score every query against every key; soften each row; then average the values.

A real transformer runs several attention operations side by side, each with its own W_Q, W_K, and W_V. These parallel copies are called heads.

One head might track grammar, another might track which pronoun refers to which noun, another might just look at nearby words. Each head has its own learned subspace and can develop its own notion of similarity.

The heads' outputs are stitched together and passed through one more learned matrix, W_O, so the model can combine what the heads found. Splitting the model's width d_model across h heads keeps the parameter count and the arithmetic roughly constant while giving the model several ways to look at the same sequence.

Attention weights per row and a causal-mask variantA six by six attention weight matrix. Every row sums to one, shown in the right column. Row q3 is peaked, with 0.90 on a single key, while row q5 is nearly flat at about 0.17 on every key. To the right, the causal mask variant keeps the upper triangle at zero so each row can only attend to itself and earlier tokens.A = softmax(Q·Kᵀ / √d_k)every row sums to 1k1k2k3k4k5k6Σq1q2q3q4q5q60.901.001.001.001.001.001.00q3 peaked — 0.90 lands on one keyq5 flat — attention spread at ≈0.17 across keyscausal maskfuture scores → −∞, weight 0k1k2k3k4k5k6q1q2q3q4q5q6maskedeach row renormalizes over the past only
Softmax turns every row into weights that add up to 1 — peaked when one key wins, flat when attention spreads — and the mask keeps forbidden positions at zero.

Two practical facts complete the picture. First, masks. If a position must not be looked at — say the future in a language model, or padding at the end of a batch — add a very large negative number (negative infinity in theory) to its score before the softmax. The exponential of that number is zero, so the forbidden position receives no attention.

Second, cost. For a sequence of length n, the table of scores has n² entries. Doubling the sequence length quadruples the table. That single fact is why long context windows are expensive and why so much engineering goes into sparse, sliding-window, and memory-saving variants.

The problems below ask you to scale the scores, soften the rows, take the weighted sum, and reproduce the whole computation end to end.

Practice this

Open a problem and implement the idea from scratch. Progress saves to this browser.

Try it hands-on

Run this topic on real data in a lab, or beat a baseline in a research challenge.