← ATTENTION & TRANSFORMERS / COURSE MAP
LESSON 03 / THE INTERACTION

Attention, from the ground up.

Derive the weighted mixture, work through actual numbers, and implement the same operation in NumPy and PyTorch.

On this page Explore the sections +
Before you begin Prerequisites & learning goal +

Builds on: Lessons 1–2: token vectors, matrix multiplication, and positional information.

Your goal: Explain every term in scaled dot-product attention, calculate a small example, and verify its implementation.

We have a vector for every input position. Each vector starts with token and position information, but it cannot yet incorporate the rest of the sequence. Attention gives a position a way to gather information from other positions, with weights that depend on the current input.

The central operation is a weighted mixture. We will build that mixture without learned projections, calculate the weights by hand, then put the matrix equation and code around it. If you arrived here directly, the course starts with tokens and vectors .

1. Start with values, not with the formula

Suppose three source positions offer these value vectors:

$$ v_1=[1,0],\qquad v_2=[0,2],\qquad v_3=[2,1]. $$
READ THE EQUATION

Define the content available at each of three source positions; each value has two feature coordinates.

TermWhat it is and doesWhat it controls
\(v_1,v_2,v_3\)Three value vectors; the subscript selects a source position.What each source can contribute to the eventual mixture.
\([a,b]\)An ordered vector with feature one followed by feature two.Keeps feature roles separate while combining sources.
1, 0, 2Hand-chosen feature values. They are not token IDs or probabilities.A zero feature contributes nothing on that coordinate; larger values have larger weighted effects.

Check: The second source has no first-feature contribution and a second-feature value of 2. With weight 0.3 it contributes [0, 0.6].

If the receiving position assigns weights \(a=[0.2,0.3,0.5]\), its output is:

$$ y=0.2v_1+0.3v_2+0.5v_3=[1.2,1.1]. $$
READ THE EQUATION

Scale every source value by its attention weight, then add corresponding features.

TermWhat it is and doesWhat it controls
\(y\)The receiving position’s two-feature output.The information produced by this mixture.
\(v_1,v_2,v_3\)The three value vectors defined above.Contents being combined, independent of how weights were selected.
0.2, 0.3, 0.5Nonnegative scalar weights that sum to one.Relative contributions; each scalar multiplies every feature of its vector.
\(+\), scalar multiplicationAdd coordinate by coordinate after scaling each vector.Preserves feature width while combining source positions.

Check: Feature one is 0.2×1 + 0.3×0 + 0.5×2 = 1.2. Feature two is 0.2×0 + 0.3×2 + 0.5×1 = 1.1. See foundations lesson 2 if this vector arithmetic is unfamiliar.

That is all the final multiplication does. Nonnegative weights summing to one make this a convex combination of the values. It usually does not select a single vector exactly. The output is a new mixture, not a copied token ID.

The remaining question is where the weights come from. A fixed average would use the same weights regardless of the query. Attention computes them from a compatibility function, so different receiving positions can gather different mixtures.

Earlier neural translation attention used a decoder state to score encoder annotations and construct a weighted context vector. Bahdanau et al. , §3, describe that learned alignment mechanism. Dot-product attention is a particular scoring rule, not the definition of every possible attention mechanism.

2. Queries ask, keys match, values contribute

Consider “The robot picked up the cup because it was empty.” Understanding a useful representation of “it” may require information from another position. A fixed average would combine every source equally; an input-dependent mixture can favor different sources in different contexts. This motivates attention, but it does not prove that a particular trained head performs coreference or that one weight explains the whole model.

The receiver needs a way to compare its current features with available sources. Giving matching and carried content separate projections lets the model use one feature space to choose a mixture and another to represent what is mixed. These projections are learned through the task loss, as in the foundations learning step , with many parameters instead of one.

A query is the vector used by a receiving position to score potential sources. A key is a source’s vector for matching. A value is the vector that source contributes to the mixture.

The words “ask” and “match” are analogies. These are learned numbers, not explicit natural-language questions, dictionary keys, or human-interpretable labels. A key is not a discrete identifier: several keys can receive substantial weight.

For self-attention, we obtain the three sets of vectors from the same input states X through different learned matrices:

$$ Q=XW_Q,\qquad K=XW_K,\qquad V=XW_V. $$
READ THE EQUATION

Apply three different learned feature projections to the same input representation.

TermWhat it is and doesWhat it controls
\(X\)Input matrix: one row per position, D features per row.The representation all three projections read in self-attention.
\(W_Q,W_K,W_V\)Learned matrices mapping model features to query, key, and value features.Which features are used for matching and which content is carried.
\(Q,K,V\)Query, key, and value activations produced for this input.Receiver matching features, source matching features, and source contents.
Juxtaposition, as in \(XW_Q\)Matrix multiplication: each row is projected with the same weights.Changes feature coordinates without mixing positions yet.

Check: If X is (4, 6) and W_Q is (6, 2), Q is (4, 2). The four positions remain four positions; each now has two query features.

Separate projections let the model learn different features for compatibility and for the information being transferred. Q and K need equal feature width because we take their dot products. V may have a different feature width; it only needs one value per source key.

The projections are parameters shared across positions. The resulting Q, K, V, and attention weights depend on the input and are recalculated for each sequence. Do not confuse the learned matrices \(W_Q,W_K,W_V\) with those temporary activations.

3. Calculate one query all the way through

Choose \(q=[1,0]\) and keys \(k_1=[1,0]\), \(k_2=[0,1]\), \(k_3=[1,1]\). Keep the three value vectors from the first section.

The unscaled dot products are [1, 0, 1]. The key dimension is two, so the scaled scores are approximately [0.7071, 0, 0.7071].

Softmax exponentiates the scores, then divides by their sum:

$$ a_j=\frac{\exp(s_j)}{\sum_r\exp(s_r)}. $$
READ THE EQUATION

Exponentiate a source score and divide by the total exponentiated scores for this query.

TermWhat it is and doesWhat it controls
\(a_j\)Attention weight assigned to source j.The share of that source’s value in the mixture.
\(s_j,s_r\)Scaled matching scores; j selects one source and r visits all sources.Relative preferences before normalization.
\(\exp\)Exponential: always positive and increasing.Turns signed scores into positive evidence.
\(\sum_r\), fraction barAdd evidence across all allowed sources, then divide by that total.Makes each query’s weights sum to one; changing one score changes the shared denominator.

Check: Scores [0, log(2), log(3)] give evidence [1, 2, 3] and weights [1/6, 2/6, 3/6]. Foundations lesson 3 explains exp, normalization, and numerical stability.

The weights are approximately [0.4011, 0.1978, 0.4011]. The output is therefore approximately [1.2033, 0.7967]. The middle source still contributes. Having the smallest score does not mean its weight is zero.

If we change only the values, we change the mixture while keeping the weights fixed. If we change a key, we can change its score and, through the shared denominator, every weight in that query’s row. If we change the query, we change the compatibility calculation for that receiving position.

4. Make every query a row

Stack T queries into Q and S source keys into K. We deliberately use different letters for the two lengths: cross-attention can have different numbers of receiving and source positions.

TensorShape, omitting batch and headsInterpretation
QT × dₖOne query per receiving position
KS × dₖOne key per source position
VS × dᵥOne value per source position
ScoresT × SEvery query–key dot product
WeightsT × SDistribution over sources for each query
OutputT × dᵥOne mixture per query
$$ Y=\operatorname{softmax}_{\text{source}}\!\left(\frac{QK^\top}{\sqrt{d_k}}\right)V. $$
READ THE EQUATION

For every query: match keys, scale the scores, normalize across sources, and use those weights to mix values.

TermWhat it is and doesWhat it controls
\(Y\)Output matrix, shape T×dᵥ.One value-feature vector for each receiving query.
\(Q,K\), \(K^\top\)Query matrix T×dₖ, key matrix S×dₖ, and its transpose dₖ×S.QKᵀ compares every query with every key, producing T×S scores.
\(d_k\), \(\sqrt{d_k}\)Key/query feature width and its square root.The divisor tempers growth in dot-product scale as width increases.
\(\operatorname{softmax}_{source}\)Normalize each row over its S source positions.Produces one distribution per query, not one distribution over the whole matrix.
\(V\), final multiplicationS×dᵥ value matrix; multiply the T×S weights by it.Combines source contents and leaves T×dᵥ outputs.

Check: With one query, three keys, and two value features, shapes go (1,2)@(2,3) → (1,3), then (1,3)@(3,2) → (1,2). This is the exact hand calculation above.

Q @ K.T computes all pairwise scores. Softmax acts on the last axis, the S source keys. Then (T, S) @ (S, dᵥ) gives (T, dᵥ). The output length follows the queries, not the keys.

VISUAL WALKTHROUGHALL STEPS
Follow the attention calculation
1 / Compare each query with each key
Q [T × dₖ]K [S × dₖ]V [S × dᵥ]QKᵀ[T × S]÷ √dₖSCALED LOGITSSOFTMAXOVER S KEYSWEIGHTS × V[T × dᵥ]ONE ROW PER QUERY. ONE COLUMN PER SOURCE POSITION.A MASK, WHEN NEEDED, IS APPLIED BEFORE SOFTMAX.

The score at row i, column j is the dot product of query i and key j. We have not formed a probability distribution yet. Scores may be positive or negative.

2 / Control the scale
Q [T × dₖ]K [S × dₖ]V [S × dᵥ]QKᵀ[T × S]÷ √dₖSCALED LOGITSSOFTMAXOVER S KEYSWEIGHTS × V[T × dᵥ]ONE ROW PER QUERY. ONE COLUMN PER SOURCE POSITION.A MASK, WHEN NEEDED, IS APPLIED BEFORE SOFTMAX.

Divide the dot products by the square root of the key width. If a mask is needed, apply it to these logits before the next step. We derive the mask in lesson 4.

3 / Normalize over the source positions
Q [T × dₖ]K [S × dₖ]V [S × dᵥ]QKᵀ[T × S]÷ √dₖSCALED LOGITSSOFTMAXOVER S KEYSWEIGHTS × V[T × dᵥ]ONE ROW PER QUERY. ONE COLUMN PER SOURCE POSITION.A MASK, WHEN NEEDED, IS APPLIED BEFORE SOFTMAX.

Softmax independently normalizes each query’s row. Rows sum to one before attention dropout. Columns generally do not: many queries can gather from the same source.

4 / Mix the values
Q [T × dₖ]K [S × dₖ]V [S × dᵥ]QKᵀ[T × S]÷ √dₖSCALED LOGITSSOFTMAXOVER S KEYSWEIGHTS × V[T × dᵥ]ONE ROW PER QUERY. ONE COLUMN PER SOURCE POSITION.A MASK, WHEN NEEDED, IS APPLIED BEFORE SOFTMAX.

Every output row is a weighted sum of the source value vectors. The attention matrix is an intermediate result; it is not the final contextual representation.

One operation at a time. Use the arrows or step buttons; nothing advances automatically.

5. Why divide by the square root?

A mean is an average; variance is the average squared distance from that mean. For features [−1,1], the mean is zero and the variance is one. Variance describes scale here, not semantic certainty or the probability of a word. “Independent” in the following argument means the random coordinates do not influence one another; it is a simplifying initialization assumption, not a description of every trained activation.

Under a simple initialization model, assume the query and key coordinates are independent, have mean zero, and have variance one. Each coordinate product then has variance one, so a sum over dₖ independent products has variance dₖ. Dividing by \(\sqrt{d_k}\) brings that variance back to one.

This is a scale argument under assumptions, not a promise about every trained query and key. Without scaling, increasing the feature dimension can produce larger logits and very peaked softmax distributions. That can make gradients through softmax small in saturated regions. The derivation appears with the definition in §3.2.1 of Attention Is All You Need .

A dot product is not automatically cosine similarity. Cosine similarity explicitly divides by both vector norms. Standard scaled dot-product attention does not generally normalize Q and K that way.

6. Change the inputs and watch the result

This experiment adds a fourth key/value pair to our three-position calculation. The labels identify positions; these hand-chosen vectors do not represent learned word semantics. Select a query, adjust the extra temperature control, and optionally hide future positions.

TRY IT / ONE QUERY, FOUR KEYSLIVE CALCULATION

Choose a query token. Watch its scores become weights, then a weighted mixture of values.

thescore —
smallscore —
robotscore —
movesscore —
Query vector: —Output: —Weight sum: —

Hand-chosen vectors, not a trained language model. Token labels identify positions. Temperature is an extra exploration control: scores are divided by √dₖ × temperature. The code below uses temperature 1.

Lowering temperature makes the largest visible scores more dominant. Raising it makes the distribution flatter. A causal mask disallows positions entirely. Changing temperature cannot recover a masked source because its weight remains zero.

Try selecting the first query and enabling the causal mask. Only its own position remains visible, so its weight must be one and its output must equal its own value, [1, 0]. This is a structural consequence of the mask, not a learned behavior.

7. The same operation in two libraries

These functions accept leading batch and head axes as well as simpler two-dimensional matrices. The last two axes always mean position and feature. allowed uses a single course-wide convention: True means a source may participate.

PYTHON
NumPy / inspect the operations
def attention(q: FloatArray, k: FloatArray, v: FloatArray, allowed: BoolArray | None = None) -> tuple[FloatArray, FloatArray]:
    """(..., T, dk), (..., S, dk), (..., S, dv). True means visible."""
    if q.shape[-1] != k.shape[-1] or k.shape[-2] != v.shape[-2]:
        raise ValueError("Q/K feature widths and K/V source lengths must match")
    scores = (q @ np.swapaxes(k, -2, -1)) / np.sqrt(q.shape[-1])
    if allowed is not None:
        visible = np.broadcast_to(np.asarray(allowed, dtype=bool), scores.shape)
        if not visible.any(axis=-1).all():
            raise ValueError("Every query needs at least one visible key")
        scores = np.where(visible, scores, -np.inf)
    shifted = scores - scores.max(axis=-1, keepdims=True)
    weights = np.exp(shifted)
    weights /= weights.sum(axis=-1, keepdims=True)
    return weights @ v, weights

Typed Python · Shapes are documented alongside the code. Open the full file for imports and dependencies, or step through a concrete example.

NumPy subtracts the largest score in each row before exponentiation. This preserves softmax because multiplying every exponential in a row by the same factor cancels between numerator and denominator. It prevents overflow when the original scores are large. PyTorch’s softmax performs the stable calculation internally.

We reject a row with no visible keys. Otherwise, all its logits would be negative infinity and subtracting their maximum would give an undefined result. Our later decoder always has an initial BOS token visible to each valid query.

Run the three-position calculation using the complete NumPy file:

python
import numpy as np
from numpy_core import attention

q = np.array([[1., 0.]])
k = np.array([[1., 0.], [0., 1.], [1., 1.]])
v = np.array([[1., 0.], [0., 2.], [2., 1.]])
output, weights = attention(q, k, v)

np.testing.assert_allclose(weights.sum(-1), 1)
np.testing.assert_allclose(output, [[1.203336, 0.796664]], atol=1e-5)
print(np.round(weights, 4))
print(np.round(output, 4))

8. What this operation does not tell us

The weights are distributions over source positions, not over the vocabulary. An attention weight of 0.4 is not a 40% probability that a word is the correct next token. A later readout layer makes vocabulary predictions.

A high attention weight is not a complete explanation of a model’s decision. The associated value vector, output projection, residual path, later layers, and prediction head all affect the result. Treat a heatmap as one intermediate calculation to inspect rather than a direct report of the model’s reasoning.

Finally, random or hand-chosen projections demonstrate an operation. Useful context-dependent behavior has to be learned from a training objective. The last lesson will connect output scores to such an objective.

CHECK YOUR UNDERSTANDINGIf the attention matrix has shape (7, 11), how many output vectors are produced?Think first. Open to check your reasoning.

Seven: one per query. There are eleven source positions. If the values have shape (11, 5), the output has shape (7, 5). The rows of the attention matrix sum to one; its columns need not.

The next missing piece

We can now mix information across positions, but we have not specified which positions are allowed to communicate. In the next lesson, that choice separates bidirectional self-attention, causal self-attention, and cross-attention. The weighted-mixture operation stays the same.

END OF LESSON 03