Concepts / Simplified Self-Attention with Dot Products

Simplified Self-Attention with Dot Products

Weight parameters are learned coefficients optimized during training, while attention weights are dynamic, context-specific values that determine how much a context vector depends on each part of the input.

  • Programming

Two Meanings of the Word Weight

Many learners first meet the word weight in a neural network course and come away with one meaning: a number that training adjusts until the model works. That meaning is reasonable, but it causes a real confusion once self-attention enters the picture. The word appears in two places that play very different roles. One place is the set of weight matrices that a self-attention layer learns. The other is the attention weights that the mechanism computes while it runs.

What do you think happens?

Suppose the second input token is the query. Which key does it pair with when the scores are computed?

  • keys[2]

  • keys[1]

  • keys[0]

Reveal answer
Answer:

keys[1]

Python starts indexing at 0, so the second key is keys[1], not keys[2]. The second token's query is paired with the key at index 1.

The rest of this article follows the path from input tokens to attention weights. Keep the two meanings of weight separate as you read, because each value in the computation is either fixed by training or produced fresh for each query.

What Training Fixes and What Each Query Changes

Weight parameters are the values of a neural network that get optimized during training. They are the fundamental, learned coefficients that define the network's connections. Once training has set them, they describe how the network connects its inputs to its outputs. In a self-attention layer, the weight matrices are these learned parameters.

Attention weights are a separate quantity. They determine the extent to which a context vector depends on the different parts of the input. In other words, they express how strongly the network focuses on each input element when it builds the context vector for one query. Because they are computed fresh for each query, they are dynamic and context-specific rather than fixed coefficients.

Key point

The clean way to hold on to the distinction is to ask when each value is determined. Weight parameters are settled by training and then used. Attention weights are produced during a forward computation, so the same weight matrix can yield different attention weights for different queries.

Learned coefficientsoptimized during trainingDynamic valuesdepend on contextSet before inferencethen used as fixed valuesComputed per queryproduced during the forwardcomputation
Weight parameters are fixed once training ends, while attention weights are recomputed for each query from the current input.

Suppose the goal is only to compute a single context vector for one input token. It is still necessary to have key and value vectors for every input element, because all of the keys take part in computing the attention weights with respect to the query. Keys and values for the full set of inputs can be obtained together by matrix multiplication with the key and value weight matrices. This projection maps each input token from its original embedding space into the space of keys and values. The source example projects six input tokens from a three-dimensional space onto a two-dimensional embedding space, so each token ends up with a two-dimensional key and a two-dimensional value.

Scoring Each Key With a Dot Product

The second step is to compute attention scores. An attention score is computed as a dot product between a query vector and a key vector. This is the same kind of dot-product computation used in simplified self-attention, with one new aspect: the query and the key are not the raw input elements. They are obtained by transforming the inputs through their respective weight matrices. The result is a raw, unnormalized number that measures how well the query matches that key.

The source computes the score for the second input token first. The query comes from that second token and is paired with the key at index 1. Because the second key is keys[1], an index-based mistake here would pair the query with the wrong key and produce a different score.

keys[0]score 1keys[1]score 2keys[2]score 3next keynext key
For the query [1, 2], each key's dot product fills one slot in the score row, in index order from 0.

Rather than repeating that dot product for every key, the same result can be obtained for all keys at once by multiplying the query by the transposed key matrix. The second entry of the resulting row matches the single score computed for index 1, which is a useful quick check when you write this code yourself.

python
Output (expected)
2.0
[1. 2. 3.]
Notes on the code
  • keys.T is the transposed key matrix, so the query dot-multiplies every key in one step
  • scores[1] is the entry for keys[1], matching a single dot product computed by hand
  • These are raw scores, not yet attention weights

Worked Example: Scores for One Query

Worked example

Scores and weights for the query [1, 2]

Keys are k0 = [1, 0], k1 = [0, 1], and k2 = [1, 1]. The query is q = [1, 2]. The key dimension is d_k = 2. Compute the unnormalized scores, scale them, and convert them into attention weights.

  1. 1
    Compute each dot product

    q dot k0 = 1 times 1 plus 2 times 0 = 1. q dot k1 = 1 times 0 plus 2 times 1 = 2. q dot k2 = 1 times 1 plus 2 times 1 = 3. The raw scores are 1, 2, 3.

  2. 2
    Scale by the square root of d_k

    The square root of 2 is about 1.414. Dividing each score gives about 0.707, 1.414, and 2.121.

  3. 3
    Apply softmax along the last dimension

    Exponentiating gives about 2.028, 4.113, and 8.343. Their sum is about 14.484. Dividing each value by that sum gives about 0.140, 0.284, and 0.576.

  4. 4
    Check the distribution

    The weights are positive and add up to 1.00, so they form a distribution over the three keys. The key k2 receives the largest weight because it has the largest score.

Result

The attention weights for the query [1, 2] are approximately 0.140, 0.284, and 0.576 for keys k0, k1, and k2.

Scaling and Softmax Turn Scores Into Weights

Attention scores are not yet attention weights. The next step converts the scores by scaling them and then applying the softmax function. Scaling divides each score by the square root of the embedding dimension of the keys. Taking the square root is the same as raising to the power 0.5, so the relationship can be written as follows.

Rule

Formula, step by step
  1. 1
    omega_i is the unnormalized attention score for key i, the dot product of the query and that key
  2. 2
    d_k is the embedding dimension of the keys, read from the shape of the key matrix
  3. 3
    Dividing by the square root of d_k scales every score before normalization
  4. 4
    softmax converts the scaled scores into positive values that add up to one along the last dimension
  5. 5
    w_i is the resulting attention weight for key i
Raw scores1, 2, 3Divide by square rootof d_ksquare root of 2, about1.41Scaled scores0.71, 1.41, 2.12Softmax over lastdimensionpositive values that sum to1Attention weights0.14, 0.28, 0.58scaledividenormalizeresult
Each raw score is divided by the square root of the key dimension, and softmax then normalizes the scaled values across the last dimension.
python
Output (expected)
[0.14  0.284 0.576]
1.0
Notes on the code
  • d_k is read from the shape of the keys, not typed in by hand
  • The division happens before softmax, so the scores are scaled first
  • axis=-1 is the last dimension, where the normalization is applied

Softmax is what converts the scaled scores into the weights. Its output values are positive and add up to one across the dimension it is applied to. Larger weights mean the context vector leans more heavily on those tokens. Each attention weight routes a fraction of its input embedding into the output context vector for that query, so the context vector is a weighted combination of the input embeddings.

Mistakes Beginners Make with Scores and Weights

  • Common mistake

    Treating attention weights as if they were learned parameters

    What it looks like

    Assuming the weights for the query [1, 2] will be the same as the weights for a different query.

    Why it fails

    Weight parameters are fixed by training, but attention weights are recomputed for each query from the current input.

    Do this instead

    Ask when each value is determined. Parameters are set by training; attention weights are produced during the forward computation.

  • Common mistake

    Pairing the query with the wrong key index

    What it looks like

    Using keys[2] when the second input token's query should be paired with keys[1].

    Why it fails

    Python starts indexing at 0, so the second key is at index 1.

    Do this instead

    Check the index in code before comparing a score with the expected value.

  • Common mistake

    Forgetting the scaling step

    What it looks like

    Passing the raw scores 1, 2, 3 directly into softmax.

    Why it fails

    The scores are meant to be divided by the square root of the key embedding dimension before softmax.

    Do this instead

    Read d_k from the key shape and divide the scores by its square root before normalizing.

  • Common mistake

    Applying softmax along the wrong dimension

    Why it fails

    Softmax has to normalize across the input tokens so that the weights for one query sum to one.

    Do this instead

    Confirm the softmax axis is the last dimension of the score matrix.

  • Common mistake

    Confusing scores with weights, so that a raw dot product is reported as if it were a probability-like attention weight

Locating Where a Result Goes Wrong

When a computed score or weight does not match the expected value, the source recommends checking four things in a fixed order. Working through them in sequence narrows down the stage where the divergence starts.

  1. 1
    Check the query source: confirm which input token produced the query vector, and that the query was computed from the right weight matrix.
  2. 2
    Check the key index: confirm that the key paired with the query is the intended one, remembering that Python indexes from 0 so the second key is keys[1].
  3. 3
    Check the scaling step: confirm the scores were divided by the square root of the key embedding dimension, taken from the shape of the keys.
  4. 4
    Check the softmax axis: confirm softmax was applied along the last dimension, so the weights for one query sum to one across the input tokens.
MEDIUM

A query [1, 2] is paired with keys[1] = [0, 1]. Your code prints a raw score of 1 for this pair. Using the four-step order above, which check should you make first, and what value should the raw score be?

Hints
  • Start with the query source before looking at the scaling step.
  • Compute the dot product of [1, 2] with [0, 1] by hand.

What do you think happens?

After dividing the raw scores 1, 2, 3 by the square root of 2 and applying softmax, will the weights for one query sum to 1?

  • Yes

  • No, only the largest weight sums to 1

Reveal answer
Answer:

Yes

Softmax outputs are positive and add up to one across the dimension it is applied to. Here that dimension is the last one, across the three keys.

Key Takeaways

  • Weight parameters are learned during training and then fixed, while attention weights are dynamic values computed fresh for each query.
  • An attention score is an unnormalized dot product between a query vector and a key vector, and the key at index 1 is keys[1] in Python.
  • Attention weights come from dividing the scores by the square root of the key embedding dimension and then applying softmax along the last dimension.
  • When a result looks wrong, check the query source, the key index, the scaling step, and the softmax axis, in that order.

Related concepts

More concepts starting with S