Concepts / Self-attention with trainable weights

Self-attention with trainable weights

All context vectors are computed with one matrix multiplication: attn_weights @ inputs.

  • Programming

Why One Product Can Replace a Loop

Earlier in this chapter, a context vector was built for one input token at a time. That approach works, but it means repeating the same steps for every position in a sequence. This article shows how the attention weights you already computed can be applied to all the inputs in one operation, producing a full set of context vectors together.

The whole computation is a single line. It takes the attention weight matrix named attn_weights, multiplies it by the inputs with the @ operator, and stores the result in all_context_vecs. Before stepping through why that line works, read the tensor it produces and reason backward from the output to the operation that made it. This habit matters for debugging: when a result looks wrong, you need to know which operation produced which part of it.

From a Loop to a Single Product

The key idea is that a single matrix product replaces a loop over tokens. For each row of the attention weights, the product forms a weighted combination of the input vectors. Doing this for every row at once gives all context vectors together. The operands flow into the product, and the result lands in one tensor.

attn_weights3 by 3 weightsmatrix product @one step for all rowsall_context_vecs3 by 2 context vectorsinputs3 by 2 input vectorseach row weights the input rowsrows combinedone row per token
What does the single product attn_weights @ inputs consume from each operand, and where does the result land?

Each row of the output holds one context vector. Row one belongs to the first input token, row two belongs to the second, and so on. The rows are aligned with the rows of the attention weight matrix, because each row of attention weights describes how much one token attends to every input token. Reading the output therefore means reading one row per token.

Predicting the Shape of the Result

Shape reasoning lets you check a result before you inspect its values. Suppose attn_weights has shape $n \times n$, one row and one column per input token, and inputs has shape $n \times d$, one row per token with $d$ values each. The product consumes the shared dimension $n$: each row of attn_weights has $n$ entries that line up with the $n$ rows of inputs. The result therefore has one row per token and $d$ columns, giving shape $n \times d$.

attn_weightsn by n, one column perinput token@shared dimension n isconsumedinputsn by d, one row per inputtokenresultn by d, one context vectorper row
Which dimension of each operand is consumed by the multiplication, and which dimension survives in the result?
Worked example

Predicting the shape of a three-token result

attn_weights has shape 3 by 3 and inputs has shape 3 by 2. What shape should attn_weights @ inputs have, and which dimension is consumed?

  1. 1
    Identify the shared dimension

    The columns of attn_weights (3) must match the rows of inputs (3). That shared dimension of size 3 is consumed by the product.

  2. 2
    Keep the outer dimensions

    The surviving dimensions are the rows of attn_weights (3) and the columns of inputs (2).

  3. 3
    State the prediction

    The result should be a 3 by 2 tensor: one row per token, with 2 values per context vector.

  4. 4
    Check against the printed output

    Run the line and count rows and columns. If the printed tensor has three rows of two values, the prediction holds.

Result

The predicted shape is 3 by 2, which equals the number of tokens by the input dimension.

Tracing a Small Batched Calculation

The following toy example uses three tokens with two values each. The weights are chosen so the arithmetic can be checked by hand. Each row of attn_weights sums to one, and each row of the result is a weighted mix of the input rows.

python
Output (expected)
tensor([[2.0000, 3.0000],
        [3.0000, 4.0000],
        [3.5000, 4.5000]])
Notes on the code
  • attn_weights is 3 by 3 and inputs is 3 by 2, so the result is 3 by 2.
  • The @ operator performs the single matrix product that yields every context vector.
  • Row one of the result is 0.5 times [1, 2] plus 0.5 times [3, 4], which equals [2, 3].

Check the second row by hand. Row two of attn_weights is [0, 1, 0], so it selects the second input token alone, giving [3, 4]. Row three is [0.25, 0.25, 0.5], so it blends the three inputs: 0.25 times [1, 2] plus 0.25 times [3, 4] plus 0.5 times [5, 6] gives [3.5, 4.5]. Each printed row can be reproduced from one row of weights and the input rows.

Locating a Divergent Row

A useful check is to compare one row of the batched result with a context vector computed earlier by a different route. Earlier, the second context vector was computed one token at a time, so its values should match the second row of the batched result. Printing that earlier vector alongside the new tensor gives a direct comparison.

This check is a debugging technique, not only a confirmation. If a single row disagrees, the problem lies in the batched path for that token, so inspect the attention weights in that row. If every row disagrees, the problem is more likely in the shared inputs or in how the weights were built. A mismatch in one row points to a specific row of attn_weights; a mismatch everywhere points to the shared setup.

What do you think happens?

If the earlier per-token vector for token two is [3, 4] but row two of the batched result is [3, 5], which part should you inspect first?

  • The shape of inputs

  • Row two of attn_weights

  • The printed label on the tensor

Reveal answer
Answer:

Row two of attn_weights

Only one row disagrees, so the problem is in the batched path for that token. Row two of attn_weights controls how token two's context vector is formed, so that row is the place to inspect.

Adding Trainable Weights

The mechanism you have just run is a simplified version of attention. The next step adds trainable weights, which lets the model learn from data and improve its performance on specific tasks. The extended mechanism is called self-attention with trainable weights, also known as scaled dot-product attention. It is the self-attention used in the original transformer architecture, in GPT models, and in most other popular large language models.

fixed attentionweightsattn_weights @ inputstrainable weightsattn_weights @ inputsone productone product
What changes between the fixed-weight computation and the trainable version, and what stays the same?

The batched product itself stays the same: a single matrix multiplication still yields a full set of context vectors. What changes is the origin of the weights. In the trainable version, the weights are learned rather than fixed, so the same product is applied with parameters the model adjusts during training.

The source describes four attention variants built in sequence. The simplified attention mechanism comes first, followed by self-attention with trainable weights. The later stages add causal attention and multi-head attention. The attention step is one stage in a longer pipeline for building a language model, not the whole model.

Mistakes When Checking Batched Results

  • Common mistake

    Expecting the output shape to be n by n

    What it looks like

    Predicting that attn_weights @ inputs is 3 by 3 because attn_weights is 3 by 3.

    Why it fails

    The product consumes the shared dimension. The surviving dimensions are the rows of attn_weights and the columns of inputs.

    Do this instead

    Keep the outer dimensions: rows of attn_weights by columns of inputs, giving n by d.

  • Common mistake

    Comparing a row against the wrong earlier vector

    What it looks like

    Comparing row one of the batched result against the vector for token two.

    Why it fails

    Rows are aligned with tokens, so a mismatch caused by an off-by-one comparison looks like a bug in the batched path.

    Do this instead

    Match row number to token number before comparing, and print both vectors together.

  • Common mistake

    Treating one mismatched row as a shared-input problem

    What it looks like

    Rebuilding the inputs after only row two disagrees.

    Why it fails

    A single disagreeing row points to the batched path for that token, not to the shared inputs.

    Do this instead

    Inspect the attention weights in that row first. Investigate the inputs only when every row disagrees.

  • Common mistake

    Reading the printed tensor without first predicting its shape makes it harder to notice a wrong result

Practice Tracing a New Result

EASY

Suppose attn_weights is a 4 by 4 matrix and inputs is a 4 by 3 matrix. Predict the shape of attn_weights @ inputs, then state which dimension of each operand the product consumes. Finally, explain how you would check a suspect row of the printed result against an earlier per-token vector.

Hints
  • Match the columns of the first operand with the rows of the second.
  • The result keeps the rows of attn_weights and the columns of inputs.
  • Compare the suspect row number with the token it belongs to, and check the attention weights in that row.

Key Takeaways

  • All context vectors are computed with one matrix multiplication, attn_weights @ inputs, which replaces a loop over tokens.
  • Each row of the result is one context vector, aligned with the matching row of attn_weights.
  • The shape of the result is predicted from the shared dimension: n by n times n by d gives n by d.
  • Comparing one batched row against an earlier per-token vector locates where a computation diverged.
  • Self-attention with trainable weights, also called scaled dot-product attention, extends this mechanism with weights learned from data.

At a glance

  • A single matrix product, attn_weights @ inputs, produces every context vector at once instead of looping over tokens.
  • Each row of the result corresponds to one token and is aligned with the matching row of attn_weights.
  • Predicting shape from the shared dimension lets you check the printed output before inspecting values.
  • When one batched row disagrees with an earlier per-token vector, inspect that row of attn_weights first.
  • Self-attention with trainable weights, also called scaled dot-product attention, extends this mechanism with learned weights.

Related concepts

More concepts starting with S