Self-Attention Mechanism
The SelfAttention_v1 class stores three trainable matrices, W_query, W_key, and W_value, each mapping the input dimension to the output dimension.
Try it: Classes, Instances and Attributes
How a class creates instances with __init__ and self, why an attribute read checks the instance's __dict__ before the class, why assigning through an instance never changes the class, how a class-level list is shared by every instance, and why obj.method(x) is Class.method(obj, x).
How it works
- Dog(...) makes a new, empty instance and calls Dog.__init__(new_object, ...); self.name = ... stores into that object's __dict__.
- Reading obj.attr looks in obj.__dict__ first, then in the class.
- Assigning obj.attr = value always writes obj.__dict__, so it shadows a class attribute for that one object only.
- Mutating a class-level list (self.tricks.append) changes the one list every instance shares.
- obj.method is found on the class and bound to obj, so obj.method(x) runs Class.method(obj, x).
Default run (30 steps): Ready: class Dog and the statements you picked. Watch where each attribute is found and where each assignment writes. … Finished. Printed: ['sit'] / Wolf / Canis familiaris / Fido is 5.
Simplified: The lab's own small Python interpreter runs a fixed, hand-written script (you choose the variant and the values, never the code); nothing is executed as real Python. Every script was run in CPython 3.12 with the same inputs and the lab reproduces its output and error messages. Object numbers are illustrative, not real id() values.
Loading the simulation…
Why a Word Needs Its Neighbours
Read a sentence and you constantly decide which other words matter for understanding a given word. A pronoun such as 'it' is meaningless until you connect it to something earlier in the text. A word near the end of a long passage may depend on a phrase introduced many words before it. Language is full of these links, and they do not always sit next to each other.
Transformers and large language models are built around a component called the self-attention mechanism, which is designed to handle exactly this problem. Its job is to let the model weigh the importance of different words or tokens in a sequence relative to each other. Because the weighing happens between the words of the input itself, the mechanism is described as self-attention.
This article follows one concrete implementation, a compact class called SelfAttention_v1, and traces it from its input to its output. Before you read the internals, you will practise forming a prediction about what the output should look like. The source notes that a self-attention class can look correct line by line and still produce a result you did not expect, and that the fastest way to catch this is to predict first and then check against what the code actually does.
Predicting the Output Shape First
The input is a matrix with one row per token, and each row is an embedding vector. Suppose the input has 6 tokens and each embedding has 3 values, so the input is 6 rows by 3 columns. The three weight matrices each map the input dimension d_in to the output dimension d_out. Use d_in equal to 3 and d_out equal to 2.
What do you think happens?
Before reading on: with 6 input tokens, d_in equal to 3, and d_out equal to 2, how many rows and columns does the output context matrix have?
Reveal answer
6 rows and 2 columns, written as 6 by 2
The context vectors have one row per input token and d_out columns. The number of rows comes from the number of tokens, and the number of columns comes from the output dimension d_out, which is set by the weight matrices. The input dimension d_in does not appear in the final output shape.
The Class and Its Three Trainable Matrices
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 is sized so that it maps the input dimension d_in to the output dimension d_out. The three matrices start from random values, and the same input vectors are multiplied by each one to produce three different views of the data.
- The constructor creates the three trainable matrices; their shapes map
d_intod_out - nn.Parameter marks each matrix as a trainable parameter the model can learn
- The forward method takes the input matrix x, one row per token
- The scores come from comparing queries with keys
- The context vectors are weights applied to the values
The sketch above is an illustrative reconstruction consistent with the description in the source, which explains each stage in prose rather than printing the full class. The line that divides by the square root of the key dimension is the scaling step that the source calls scaled softmax.
The constructor only sets up the starting values. The source explains that as the model is exposed to more data during training, it adjusts these trainable weights. Learning changes them later, which is why they are called trainable. This distinction matters when you debug: the matrices you see at construction time are not the ones the model ends up with.
Tracing the forward Method
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: receive the input, project it into queries, keys, and values, score every pair of tokens, turn the scores into weights, and combine the values using those weights.
The diagram traces the same flow the forward method performs, but as data flow instead of 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 the weights are then applied to the values to produce the context vectors. Notice that the scores and weights sit between the projections and the final output. When something goes wrong, you can check the output of each stage rather than only the final result.
Weighing One Token Against Every Other
Take a single token, say token 2. Its query is compared against the key of every token in the sequence, including itself. Each comparison yields a score, and the scores become weights. The context vector for token 2 is then a weighted blend of the value vectors of all tokens, so tokens that score highly contribute more to it. Each token therefore gets its own context vector, shaped by its surroundings.
The example above shows the kind of judgement self-attention is meant to make: deciding which parts of the input bear on which other parts. It does not show how the model computes those judgements in detail. The source introduces the mechanism at this conceptual level and says nothing more specific about the arithmetic inside it.
Shape Propagation Through the Pipeline
Shape tracing is the most reliable way to find a bug before reading any numbers. The example below is an illustrative case 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. Follow each intermediate tensor from the input to the final output.
Predicting the Final Shape from the Inputs
Input x has 6 rows and 3 columns. Each weight matrix is 3 by 2. What are the shapes of the queries, keys, values, scores, weights, and context vectors?
- 1Projections
Multiplying a 6 by 3 input by a 3 by 2 weight matrix gives a 6 by 2 result. The inner dimensions 3 and 3 match, and the outer dimensions give 6 by 2. Queries, keys, and values are all 6 by 2.
- 2Scores
Queries multiplied by the transpose of keys is a 6 by 2 times a 2 by 6 product, giving 6 by 6. There is one score for every pair of tokens.
- 3Weights
Applying softmax row by row to the scores keeps the shape, so the attention weights are also 6 by 6.
- 4Context vectors
Weights (6 by 6) multiplied by values (6 by 2) gives 6 by 2. One row per token, with
d_outcolumns.
Queries, keys, values: 6 by 2. Scores and weights: 6 by 6. Context vectors: 6 by 2. The final output has one row per input token and d_out columns.
Checking the Output Row by Row
The source offers a simple way to verify the implementation. In the output matrix, the second row should match the context vector computed for the second input token in the 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 its output at each stage rather than only at the end. Compare the shapes first, then the projections, then the scores, then the weights, then the context vectors. The first stage that disagrees with your prediction is where the unexpected result begins.
Your forward method returns a 6 by 6 matrix when you expected 6 by 2. Using the stage-by-stage check, which intermediate tensor would you inspect first, and why?
Hints
- Compare the shapes of the queries, keys, and values first. Are they 6 by 2?
- A 6 by 6 result is exactly the shape of the scores and the weights, not the context vectors.
- If the context vectors are 6 by 6, check whether the final multiplication used the weights against the values, not against another matrix.
Mistakes Beginners Make with Self-Attention Shapes
Common mistake Expecting the output to have one column per input dimension
What it looks likeAssuming a 6 by 3 input produces a 6 by 3 context matrix because the input has 3 columns
Why it failsThe context vectors have
d_outcolumns, which is set by the output dimension of the weight matrices, not byd_in.Do this insteadRead the number of columns from d_out. Read the number of rows from the number of tokens.
Common mistake Treating the constructor's random matrices as the final learned weights
Why it failsThe constructor only sets up starting values. Training adjusts the trainable weights over time.
Do this insteadCheck shapes at construction time, but do not expect the numbers to match a trained model.
Common mistake Reading the numbers in the source's figure as real attention weights
Why it failsThe source states those values were placed for visual consistency.
Do this insteadUse the figure to understand structure: weights come from queries and keys, and they act on values.
Common mistake Debugging only the final output instead of checking each stage
Why Self-Attention Captures Long-Range Dependencies
Self-attention is what allows a model to relate every position in an input to other positions, rather than reading each word in isolation. The source identifies two closely linked benefits. First, the mechanism enables the model to capture long-range dependencies, meaning relationships between words that sit far apart. Second, it captures contextual relationships within the input data, so that a token's role is shaped by its surroundings.
The reason this works is that a token's query is compared directly with the key of every other token in one step. A distant token is therefore no harder to reach than a neighbouring one. This is the contrast with a model that passes information only from one position to the next, where a distant clue would have to travel through every intermediate position.
The source connects these capabilities to output quality. By capturing these relationships, the mechanism enhances the model's ability to generate output that is coherent and contextually relevant. The reason self-attention matters is not only that it sees many words at once, but that it uses those words to judge what each one means in its setting.
How Encoder and Decoder Divide the Translation Work
The self-attention mechanism appears within a larger structure. A transformer is a deep learning model architecture for language translation. In the simplified depiction the source gives, it is made of two parts: an encoder and a decoder. In the original transformer architecture, the encoder processes the complete input text and produces an embedding representation, and the decoder uses that representation to generate output one word at a time.
Consider one translation. The input text is the sentence 'This is an example', and the partially translated sentence is 'Das ist ein'. The final stage is where the decoder has to generate only the last word, 'Beispiel', to complete the translation. The encoder has already read the whole input, so the decoder's job at this point is to produce the remaining output word using the representation it was handed.
Keeping these jobs separate makes it easier to see where the attention-based weighing of words does its work. The encoder is where the input is read as a whole, and the decoder depends on the representation that results. The source's introductory treatment places self-attention within this two-part structure without describing the internal arithmetic of either part in detail.
Where the Full Explanation Is Deferred
When you reach the detailed treatment, keep the structure from this article in mind. The shapes you predicted, the five stages of the forward method, and the row-by-row check will still apply. The later material supplies the mathematics behind each stage.
Key Takeaways
- Self-attention lets a model weigh the importance of different tokens in a sequence relative to each other, which helps capture long-range dependencies and context.
- SelfAttention_v1 inherits from nn.Module and holds three trainable matrices, W_query, W_key, and W_value, each mapping
d_intod_out. - The forward method projects the input into queries, keys, and values, scores token pairs, converts scores into weights, and combines values into context vectors.
- Predicting shapes first makes the output checkable: with 6 tokens and
d_outof 2, the context vectors are 6 by 2, and the scores and weights are 6 by 6. - Checking the output row by row and stage by stage locates where an unexpected result first diverges. In the transformer, the encoder reads the input and the decoder generates the output one word at a time.