Concepts / Queries, Keys, and Values

Queries, Keys, and Values

The SelfAttention_v1 class stores three trainable matrices, W_query, W_key, and W_value, each mapping the input dimension to the output dimension.

  • Programming

Predicting the Output Before Reading the Code

A self-attention class can look correct line by line and still produce a result you did not expect. The fastest way to catch that problem is to form a prediction first and then check it against what the code actually does. This article builds that habit using a compact self-attention class written as a subclass of nn.Module.

The input to the class is a matrix with one row per token, and each row is an embedding vector. Before you read the internals, decide what the output should look like. Ask two questions: how many rows does the output have, and what determines how many columns it has?

What do you think happens?

The input has 6 tokens, each embedding has dimension d_in = 3, and the output dimension is d_out = 2. What shape should the context vector matrix have?

Reveal answer
Answer:

It has 6 rows, one per input token, and 2 columns, one per output dimension. The shape is 6 x 2.

The context vectors have one row per input token and d_out columns. The number of rows follows the number of tokens, and the number of columns follows d_out, not d_in. Notice that the input dimension does not appear in the output shape.

Three Trainable Matrices in the Constructor

The class SelfAttention_v1 inherits from nn.Module. The source describes this base class as a fundamental building block of PyTorch models that supplies the functionality needed to create and manage model layers. Inheriting from it is what lets the class hold trainable parameters in a way PyTorch can track.

Inside the constructor, three trainable weight matrices are created: W_query, W_key, and W_value. Each one is wrapped in nn.Parameter and sized so that it maps the input dimension d_in to the output dimension d_out. In shape terms, each matrix has d_in rows and d_out columns, so multiplying the input matrix by one of them produces a matrix with d_out columns.

The three matrices start from random values, and the same input vectors are multiplied by each one. That produces three different views of the same data: queries from W_query, keys from W_key, and values from W_value. The three matrices are separate parameters, so each view can be shaped differently during training even though they all start from the same input.

The constructor only sets up starting values. The source explains that as the model is exposed to more data during training, it adjusts these trainable weights. This is why the matrices are called trainable: the constructor creates them, and learning changes them later.

The Forward Method as a Five-Stage Pipeline

The forward method is where the input actually flows through the mechanism. It takes the input matrix x, projects it into keys, queries, and values, computes a score for every pair of tokens, converts those scores into weights, and then uses the weights to combine the values. Read the method as a pipeline with five stages, where each stage's output is the next stage's input.

Input X6 x 3 token embeddingsW_query3 x 2 trainableQueries6 x 2Scores6 x 6 token pairsAttention weights6 x 6 after scaled softmaxContext vectors Z6 x 2W_key3 x 2 trainableKeys6 x 2W_value3 x 2 trainableValues6 x 2multiplymultiplymultiplyX times W_queryX times W_keyX times W_valuequeries with keysscale then softmaxweights act on values
Which matrix operation produces each intermediate tensor, and which tensors feed the next step? Each arrow is one matrix operation.

The diagram follows the same sequence the forward method performs, written as a data flow rather than as code. The input matrix X is multiplied by three weight matrices to produce queries, keys, and values. Queries and keys are combined into the attention weight matrix, and those weights are then applied to the values to produce the context vectors Z.

Each of the three stages below corresponds to a group of lines in the forward method. Each is explained in its own subsection so you can locate the line that produces each kind of output.

Tracing Tensor Shapes Through a Six-Token Example

Shape tracing is the most reliable way to find a bug before you read any numbers. The example below was written for this article, not taken from the source. It uses six input tokens, an input dimension of 3, and an output dimension of 2. Trace each intermediate tensor and record which dimension changes at each step.

Worked example

Shapes through self-attention for 6 tokens

The input X has 6 rows (tokens) and 3 columns (d_in). Each trainable matrix W_query, W_key, and W_value is 3 x 2 because it maps d_in = 3 to d_out = 2. Determine the shape of every intermediate tensor and the final context matrix.

  1. 1
    Input X

    X has one row per token and one column per embedding dimension, so its shape is $6 \times 3$.

  2. 2
    Queries, keys, and values

    Each projection multiplies X by a $3 \times 2$ matrix. The inner dimensions match at 3, and the result has 6 rows and 2 columns. So Q, K, and V each have shape $6 \times 2$. The number of rows is unchanged, and the columns change from 3 to 2.

  3. 3
    Scores

    Scores compare every token with every token. Combining queries with keys requires lining up the shared dimension of size 2, which gives a result with 6 rows and 6 columns. The score matrix has shape $6 \times 6$.

  4. 4
    Attention weights

    Scaled softmax changes the values but not the shape, so the weights also have shape $6 \times 6$.

  5. 5
    Context vectors

    The weight matrix $6 \times 6$ is applied to the value matrix $6 \times 2$. The shared dimension of size 6 lines up, leaving 6 rows and 2 columns. The context matrix Z has shape $6 \times 2$.

Result

The output has shape $6 \times 2$: one row per token and d_out = 2 columns. The score and weight matrices are $6 \times 6$ and do not appear in the final output.

X6 x 3 inputQueries6 x 2 columns changed 3 to2Keys6 x 2Values6 x 2Scores6 x 6 token pairsWeights6 x 6 same shapeContext Z6 x 2 output
For 6 tokens, d_in = 3, and d_out = 2, what shape does each intermediate tensor have, and which dimension changes at each step?

Reading the shapes this way gives you a check you can perform without running any numbers. If your output has 6 rows and 2 columns, the row count and the output dimension are correct. If it has 3 columns, a projection probably used d_in where it should have used d_out. If it has 6 rows but a wrong column count, the context stage is the first place to look.

Why Queries and Keys Decide the Weights

The three projections play different roles even though they all come from the same input. The queries and keys are compared with each other to produce scores and then weights. The values are not used when computing the weights. They are only used in the final stage, where the weights combine them into context vectors.

Key point

The weights come from queries and keys. The weights then act on the values. If you swap the roles of the projections in your head, you will predict the wrong output shape or the wrong dependency between lines.

This dependency explains a useful debugging fact. A mistake in how queries or keys are built will change the attention weights, and therefore change every context vector. A mistake that only affects the values changes the context vectors directly, but leaves the weights alone.

Locating Where a Result Goes Wrong

Suppose the context vectors look wrong. The source offers a verification method: in the output matrix, the second row should match the context vector computed for the second input token in the previous single-token computation. Matching a row like this is a strong check, because it confirms that the batched matrix version and the single-token computation agree.

If the rows do not match, the mismatch tells you which part of the pipeline to examine. Because the forward method is a fixed sequence of matrix operations, you can check output at each stage rather than only at the end. Work backward from the context vectors, or forward from the projections, until you find the first stage where the values diverge from what you predicted.

Unexpected outputcontext vectors look wrongOutput shapeexpect 6 x 2Row 2 matchcompare with single-tokencontextQueries, keys, valuescheck each projection shapeScorescheck 6 x 6 pair matrixAttention weightscheck rows after softmaxContext vectorscheck weighted valuesFirst divergencefix the line that producesitcheck firstshape correctyes: check other rowsno: inspect upstreamprojections correctscores correctweights correctoutput diverges here
After the output looks wrong, which checkpoint should you inspect first, and how do you move through the forward sequence to find where the output first diverges from your prediction?

Mistakes When Tracing Self-Attention

  • Common mistake

    Predicting the output width from the input dimension

    What it looks like

    Assuming a 6 x 3 input gives a 6 x 3 context matrix.

    Why it fails

    The projections map d_in to d_out, so the context matrix takes its columns from d_out.

    Do this instead

    Set the column count from d_out, which is 2 in the example, and keep the row count equal to the number of tokens.

  • Common mistake

    Treating the constructor as where the outputs are computed

    Why it fails

    The constructor only creates the trainable matrices and sets their starting values. Outputs are computed when forward runs on an input.

    Do this instead

    Trace outputs through the forward method and treat the W matrices as inputs to that method.

  • Common mistake

    Assuming the attention weights depend on the values

    What it looks like

    Looking for a multiplication involving the values when computing the score matrix.

    Why it fails

    The scores come from queries compared with keys. The values only enter in the final context stage.

    Do this instead

    Locate the score line first, since it compares queries and keys, and then find the line that applies the weights to the values.

  • Common mistake

    Reading the figure's weight numbers as real results

    Why it fails

    The source states that the weight values in its figure were placed for visual consistency.

    Do this instead

    Use the figure to understand the structure of the computation, not the specific numbers.

  • Common mistake

    Checking only the final output, which makes it impossible to tell which stage introduced the error

Practice: Predict Then Trace

MEDIUM

A self-attention class receives an input with 8 tokens, each embedding of dimension d_in = 4, and it uses d_out = 5. Before tracing, predict the shape of each of these: the three projection outputs, the score matrix, the attention weight matrix, and the context matrix. Then trace the forward method stage by stage and write the shape after each stage.

Hints
  • Each W matrix has shape d_in by d_out, so here it is 4 x 5.
  • The number of rows in the projections and in the context matrix equals the number of tokens.
  • The score and weight matrices compare every token with every token.
MEDIUM

After tracing the shapes, your colleague says the second row of the output does not match the single-token context vector for the second input. Name the first stage you would check, and explain why the score and weight stages should be checked before the context stage if the projections look correct.

Hints
  • Think about which stage the context vectors depend on directly.
  • The weights depend on queries and keys, so an error there changes every context row.

Key Takeaways

  • The SelfAttention_v1 class, a subclass of nn.Module, creates three trainable matrices, W_query, W_key, and W_value, each mapping d_in to d_out. Their values start random and are adjusted during training.
  • The forward method runs five stages: projecting the input into keys, queries, and values; computing scores for every token pair; converting scores into weights with scaled softmax; and combining values using the weights to produce context vectors.
  • For an input with one row per token, the context matrix has one row per token and d_out columns. Shape tracing lets you predict this output and catch the first dimension that goes wrong.
  • Weights come from queries compared with keys, and then act on the values. A bug in the score or weight stages changes every context vector, so check intermediate stages in forward order.
  • To locate a divergence, verify the output shape, compare a row such as row 2 with the single-token computation, and then inspect each stage in turn until the first mismatch appears.

Related concepts

More concepts starting with Q