Concepts / Multi-Head Attention

Multi-Head Attention

Two self-attention implementations can be made equivalent by assigning the weights from one instance to the other, with attention to the transposed storage form used by nn.Linear.

  • Programming

Two Attention Versions That Must Agree

When you write a self-attention module from scratch, you often end up with more than one version of it. An early version might store its trainable projection weights in one layout, while a later, cleaner version organizes the same mathematical operation differently. This article follows one practical question through the whole topic: if you have a trained or carefully set-up instance of one version, how do you move its weights into an instance of the other version so that both produce the same outputs?

The goal is not to make the two classes look alike. The goal is to make them behave alike. Once the weights are assigned correctly, feeding the same input into each instance should give the same output, and that shared output is the check that tells you the transfer worked. The rest of this article uses that check to build up to multi-head attention, the causal masking that a GPT-style model depends on, and the transformer blocks assembled into the full architecture.

What do you think happens?

In a sequence of several tokens, which tokens can the first token attend to once a causal mask is applied?

  • Every token in the sequence, including later ones

  • Only the tokens before it and itself

  • Only itself

Reveal answer
Answer:

Only itself

Causal attention masks out the attention weights above the diagonal, so the row for the first token has only one visible position, itself. After normalization that row receives the full weight of 1.0.

Copying Weights Between Two Attention Classes

The transfer runs from an instance of the second version, called SelfAttention_v2, into an instance of the first version, called SelfAttention_v1. Each class holds trainable weight matrices for its query, key, and value projections. Copying weights is therefore a matter of identifying which matrix in one class corresponds to which matrix in the other, and then assigning values pairwise: the query matrix to the query matrix, the key matrix to the key matrix, and the value matrix to the value matrix.

Pairing the matrices is the easy part. The difficulty is that a matrix you can see in one class may not be stored in the same orientation in the other, and that is where the transfer usually fails. The next section explains the storage layout that causes this.

The Transposed Storage Trap in nn.Linear

The step that most often causes trouble is the storage layout. nn.Linear stores its weight matrix in a transposed form. A matrix that you would write with rows for input features and columns for output features is held the other way around inside nn.Linear. If you assign a weight matrix directly without accounting for this, the shapes may still fit by accident in some cases. The values, however, end up arranged in the wrong orientation, and the two instances will disagree even though the code ran without error.

AspectMatrix as you would write itMatrix as nn.Linear holds it
Row meaningRows index input featuresRows index output features
Column meaningColumns index output featuresColumns index input features
OrientationInput by outputTransposed relative to the written form
Direct assignmentValues land in the wrong orientationMust transpose before assigning

The same weight matrix seen in its written form and in its nn.Linear storage form

Verifying the Transfer With One Input

Successful transfer is confirmed by running the same input through both instances and checking that they give the same outputs. You do not need to inspect the classes to decide whether the copy worked. If the outputs match for a shared input, the weights are behaving the same way, and that is the only standard the transfer needs to meet.

  1. 1
    Create an instance of SelfAttention_v2 and an instance of SelfAttention_v1.
  2. 2
    Pair each query, key, and value matrix in v2 with its counterpart in v1.
  3. 3
    Transpose each weight as needed to account for nn.Linear's storage form, then assign it.
  4. 4
    Feed the same input tensor into both instances.
  5. 5
    Compare the two output tensors. Matching outputs confirm the transfer.

Why Causal Attention Hides Future Tokens

The next enhancement adds a causal aspect to self-attention. Causal attention modifies the mechanism so that the model cannot access future information in the sequence. This matters for language modeling, where each prediction should depend only on the words that came before it. If a model could peek at upcoming words while it predicts the current one, it would learn a shortcut that does not exist at generation time.

In GPT-style models, each token is processed in turn. For the current token, the tokens that come after it in the input are masked out. Concretely, the attention weights above the diagonal of the attention matrix are masked out, and the remaining weights in each row are normalized so that they sum to 1. The row for a given token therefore only distributes its attention over itself and the tokens before it.

Building the Masked Attention Weight Matrix

One way to obtain the masked attention weight matrix is to work in three stages. First, apply the softmax function to the attention scores. Second, zero out the elements above the diagonal so that future positions contribute nothing. Third, normalize the resulting matrix row by row so that each row again sums to 1. The result is a matrix in which every row describes a valid distribution over the tokens that are allowed to be seen.

Thesees only The (1.0)Yoursees The and Yourjourneysees The, Your, and journeyearlier tokenearlier token
Which tokens can each token attend to once the upper triangle is masked out? Each row lists only the positions up to and including itself.

The diagram above shows the row for the first word. Its only visible position is itself, so after normalization it receives the full weight of 1.0. Rows for later words see more positions. The second-row word, for example, keeps the weight for the word before it and for itself, and nothing beyond that.

Worked example

Masking and normalizing a three-token attention matrix

Starting from a softmax-normalized 3 by 3 matrix, build the masked attention weight matrix for a three-token sequence. The numbers below are illustrative and chosen only to show the arithmetic.

  1. 1
    Start with softmax output

    Row 1 is [0.50, 0.30, 0.20], row 2 is [0.25, 0.45, 0.30], and row 3 is [0.30, 0.30, 0.40]. Each row already sums to 1 after softmax.

  2. 2
    Zero the upper triangle

    Set the entries above the diagonal to zero. Row 1 becomes [0.50, 0, 0], row 2 becomes [0.25, 0.45, 0], and row 3 stays [0.30, 0.30, 0.40].

  3. 3
    Check the row sums

    After zeroing, row 1 sums to 0.50 and row 2 sums to 0.70. Row 3 still sums to 1.0. Rows 1 and 2 are no longer valid distributions.

  4. 4
    Normalize each row

    Divide each row by its own sum. Row 1 becomes [1.0, 0, 0]. Row 2 becomes approximately [0.36, 0.64, 0]. Row 3 is already normalized and stays [0.30, 0.30, 0.40].

Result

The first row gives all its weight to itself, the second row spreads its weight over the first two tokens only, and the third row sees all three tokens. Every row sums to 1 and no row uses a future position.

Why Masking Comes Before Normalization

The ordering in the three-stage method matters. Zeroing the upper triangle after the softmax leaves rows that no longer sum to 1, which is why the normalization stage follows the masking stage. The masked matrix is unnormalized right after the zeroing and becomes normalized only at the end.

Splitting Attention Into Several Heads

The third enhancement is the multi-head component. Here the attention mechanism is split into several heads. Each head learns different aspects of the data, and the heads together let the model attend to information from different representation subspaces at different positions at the same time. The source presents this as a way to improve performance on complex tasks.

Attention mechanismone input sequenceHead 1own weightsCombined outputheads recombinedHead 2own weightsHead 3own weightssplitsplitsplitcombinecombinecombine
How does one embedding dimension get divided into separate heads, each with its own weights and attention computation, and how are their outputs recombined?

Causal masking and multi-head splitting are presented together because a practical GPT-like model uses both. The mask controls which positions a token may see, while the heads let it look at those positions in several different ways. Each head in the diagram has its own weights and its own attention computation, and the heads' results are brought back together into one output.

Multi-Head Attention as Stacked Causal Attention

The attention modules in transformer-based LLMs involve multiple instances of causal attention, and this arrangement is called multi-head attention. A single causal attention module processes its input once. Multi-head attention runs several such instances side by side, so the model's attention is organized as a group of parallel causal attention units rather than one unit.

The implementation idea is equally direct. A multi-head attention module is created by stacking multiple instances of causal attention modules. You do not need a new kind of attention to get the multi-head version. You reuse the causal attention module you already built and stack several copies of it into one larger module, with each copy acting as one head.

Transformer Blocks in a GPT-Like Model

Multi-head attention is one of the core components of an LLM, but it is not the whole model. The next part of the work is to code the other building blocks of an LLM and assemble them into a GPT-like model. Those assembled units are transformer blocks. The source names transformer blocks as the building blocks used in LLM architecture, and implementing them is necessary for creating GPT models of various sizes.

GPT-like modelfull architectureTransformer blockfirst copyTransformer blocksecond copyTransformer blockrepeated as neededcontainscontainscontains
What does a transformer block contain, and how are multiple identical blocks stacked to form the full GPT-like model? The stacked block structure is drawn here; the contents of each block are described in the text.

The phrase of various sizes is worth taking seriously. A transformer block is a reusable unit, so the same kind of block can be repeated or configured to produce models of different scales. The learning objective here is to understand how to implement transformer blocks, and the source treats that implementation as a required step rather than an optional refinement.

Where the Architecture Step Sits in the Roadmap

The LLM architecture is introduced top-down as several building blocks before the individual components are examined. The roadmap that places this chapter inside a larger process has three stages. Read it as a sequence rather than a checklist of unrelated tasks.

  • Stage 1: building an LLM. Within this stage, step 1 and step 2 prepare the data and the attention machinery.
  • Stage 1, step 3: implementing the LLM architecture, which assembles attention and the other pieces into a GPT-like model. This is the step this article covers.
  • Stage 1, later steps: a training loop, pretraining the model, and evaluating it.
  • Stage 2: turning the built model into a foundation model.
  • Stage 3: turning it into a personal assistant by adapting it with labeled or instruction data.

The chapter ends at the architecture. The source states that the next chapter adds the training loop and pretrains the LLM, so the model assembled here is not yet trained. Its purpose at this point is to exist as a complete, correctly structured GPT-like model that can later be trained to generate human-like text.

Mistakes When Moving Weights and Stacking Heads

  • Common mistake

    Assigning an nn.Linear weight matrix directly without accounting for its transposed storage

    What it looks like

    Copying a matrix written as input-by-output straight into nn.Linear's weight slot without transposing it

    Why it fails

    The shapes can still fit, so no error appears, but the values end up in the wrong orientation and the outputs disagree

    Do this instead

    Transpose the matrix so its orientation matches nn.Linear's storage, then confirm the copy by comparing outputs on the same input

  • Common mistake

    Judging a weight transfer by whether the code runs or the shapes match

    Why it fails

    Matching shapes and error-free execution do not show that the two instances compute the same thing

    Do this instead

    Feed the same input into both instances and check that the outputs are identical

  • Common mistake

    Zeroing the upper triangle and skipping the final normalization

    Why it fails

    Rows no longer sum to 1, so they are not valid distributions over the visible tokens

    Do this instead

    Apply softmax, zero the elements above the diagonal, then normalize each row so it sums to 1

  • Common mistake

    Treating multi-head attention as a new kind of attention mechanism

    Why it fails

    The source describes it as multiple instances of causal attention stacked into one module

    Do this instead

    Reuse the causal attention module and stack several copies, one per head

Checking Your Understanding of the Masked Matrix

MEDIUM

A four-token sequence has a softmax-normalized attention matrix. You zero the entries above the diagonal, and the row for token 2 sums to 0.6 after zeroing. What must you do to that row, and why does the row for token 4 need no change beyond the masking itself?

Hints
  • Recall the three stages: softmax, zero the upper triangle, then normalize each row.
  • Token 4 is the last position, so its row already has every position visible.
  • Normalizing divides each element by the row's sum so the row totals 1.

Key Takeaways

  • Two self-attention implementations can produce identical outputs when their trainable query, key, and value weights are copied pairwise, and matching outputs on a shared input confirm the transfer.
  • nn.Linear stores its weight transposed relative to the written form, so direct assignment can put values in the wrong orientation even when shapes fit.
  • Causal attention masks the upper triangle of the attention matrix, then normalizes each row; the masked matrix is built by softmax, zeroing, and normalizing, in that order.
  • Multi-head attention splits attention into several heads and is built by stacking multiple causal attention instances, and transformer blocks are assembled into a GPT-like model within stage 1, step 3 of building an LLM.

Related concepts

More concepts starting with M