Single-Head Attention Mechanism
The GPT-2 initialization in the source passes block_size, d_in, d_out, a dropout value of 0.0, and num_heads into MultiHeadAttention, and each argument should be checked by position.
Starting From the Smallest GPT-2 Setup
This article follows a single practical task: setting up an attention module for the smallest GPT-2 model and then checking that the result matches what you expected. Attention modules are easy to build and hard to debug, because a wrong configuration rarely fails with a loud error. A module can be built from the wrong settings, or from an incomplete set of weights, and still run. The only way to catch this is to know which values were supplied, in which order, and what each stage should produce.
The lesson is built around a before-you-theorize approach. Read the initialization for the smallest GPT-2 model, set up its values, construct the module, and watch what the trace reports at each step. The goal is to understand what you are looking at, so that when a result does not match your expectation you know which line to question first.
The Five Slots of the Initialization Call
The multi-head attention call supplies five values by position: the input dimension d_in, the output dimension d_out, the block size, a dropout value of 0.0, and the number of heads num_heads. The lesson does not give a full definition of each argument beyond these values. Treat the call as a contract: the input dimension, output dimension, block size, dropout rate, and head count must all be supplied together, and none of them can be left out or swapped silently.
Each slot should be checked one at a time. When you copy a configuration from one example to another, do not assume the positions line up. Read the call from left to right, name each value as you go, and confirm that it matches the parameter it is meant to fill. Two of these values, d_in and d_out, are the ones most often confused, because both are widths and both are plain integers.
Choosing d_out for a Target Output Width
A common assumption is that a module's output dimension always equals the d_out value you pass in. The lesson shows a different situation. It uses a wrapper that takes num_heads=2 and asks for an output dimension of 2, which matches the result that single-head attention produced earlier. To get that output width with two heads, the lesson changes the projection dimension d_out to 1. The value you pass for d_out is therefore not the same thing as the final width you see, and d_out must be reasoned about together with the head count.
Reaching an output width of 2 with two heads
The target output dimension is 2, and the wrapper uses num_heads=2. What value should the projection dimension d_out take?
- 1Identify the target
The learner wants a final output dimension of 2, matching the single-head result.
- 2Identify the head count
The wrapper uses
num_heads=2, so the output is produced from two heads. - 3Divide the target across heads
Two equal head slices must together reach a width of 2, so each head contributes a width of 1.
- 4Set d_out
The lesson changes the projection dimension
d_outto 1, which yields one unit per head across the two heads.
d_out is set to 1, not 2, so the combined output reaches the target width of 2.
The rule to keep in mind is that d_out is a total width that the heads share, not a per-head width you can set independently. If you change num_heads without revisiting d_out, the combined output width will change too. This is why the lesson treats the head count and d_out as a pair, and why a setup that passes the right d_out value for one head count can produce the wrong output width after the head count is changed.
Copying Query, Key, and Value Weights Between Modules
The second setup task is copying learned weights from one attention module into another. The lesson states that the correct assignment transposes the weights of the query, key, and value matrices taken from another attention mechanism. Each target attribute is wrapped in torch.nn.Parameter, so that the copied tensor is registered as a parameter of the new module rather than as a plain tensor.
Read the three assignments as one repeated operation applied to three different matrices. The same pattern covers the query, the key, and the value weights. Because the lesson presents them as a set, a missing line or a line written differently produces a module built from an incomplete set of weights. Nothing in that situation is a syntax error, so the problem stays hidden until the outputs are compared.
On the transpose, the lesson states that the copy is transposed before it is wrapped and assigned. This passage does not spell out the reasoning behind the transpose, so treat it as a required step of the transfer. The transposed matrix is what gets placed into the target attribute, so skipping the transpose or applying it to only one of the three matrices changes what the new module holds.
Mistakes That Make a Setup Diverge
Common mistake Assuming the output width always equals the d_out value passed in
What it looks likePassing d_out=2 with num_heads=2 and expecting the output to be width 2 because the number matches.
Why it failsThe lesson's two-head wrapper reaches an output width of 2 by setting
d_outto 1, sod_outand the head count together determine the final width.Do this insteadWork out the per-head width first, then check that the combined output reaches the target dimension.
Common mistake Reading the five positional values in one assumed order
What it looks likeCopying a configuration and assuming d_in and block_size sit in the same positions as in the earlier listing.
Why it failsThe lesson lists the five values in different orders in two places, so position alone is not a reliable guide.
Do this insteadName each positional value against the parameter in the exact call you are running.
Common mistake Transferring only some of the query, key, and value weights
What it looks likeCopying W_query and W_value but leaving W_key at its default values.
Why it failsThe module is built from an incomplete set of weights, and no syntax error signals the problem.
Do this insteadConfirm that all three assignments are present and follow the same transpose-and-wrap pattern.
Common mistake Leaving out the transpose during the weight copy
Practice: Locating the First Divergence
What do you think happens?
You build a two-head module that should produce an output of width 2, and the output width comes out as 4. Before checking anything else, which stage of the setup should you question first?
Reveal answer
The d_out choice, to confirm the per-head width was set correctly
The output width is wrong, which points to the d_out stage. A width of 4 from two heads means each head contributes 2 rather than 1, so d_out was likely left at 2 when it should have been 1. The arguments and the weight transfers are checked afterward if d_out is correct.
Write down what you expect for each of the three setup stages before you run the trace: the argument values, the d_out value for a two-head module targeting output width 2, and the presence of the transposed query, key, and value weights. Then run the setup and compare each stage's actual result to your note. Identify the first stage that differs.
Hints
- Divide the target output width by the number of heads to find the per-head contribution.
- Check the five positional values against their parameter names in the call you are running.
- Confirm that all three weight assignments exist, not just the first.
Key Takeaways
- The multi-head attention call takes five positional values,
d_in,d_out,block_size, a dropout value of 0.0, andnum_heads, and each must be checked by its position in the exact call you are running. d_outis a total width shared across heads, not the final output width by itself: a two-head module reaching output width 2 usesd_outset to 1.- Query, key, and value weights are copied from another attention module by transposing each matrix and wrapping it in torch.nn.Parameter, and all three assignments must be present.
- When a setup diverges, check the initialization arguments, then
d_out, then the weight transfers, and stop at the first stage whose result differs from your expectation.