liveBook · Manning

Multi-head attention

As explained in Learn Generative AI with PyTorch

A self-attention variant that splits representations into multiple attention heads to capture different relationships in parallel.

Multi-head attention runs attention through several heads rather than one set of query, key, and value vectors. Each head can attend to different aspects of an input, giving the combined result a broader view of context. This is useful when a word has more than one possible meaning. section 9.1.1

Eight heads for a short sentence

For “How are you?”, query, key, and value each have shape (1, 6, 256): one sentence, six tokens including beginning- and end-of-sequence tokens, and 256 values per token. Split each into eight heads of 32 values, calculate attention in each head, then join the results into a (1, 6, 256) attention vector. section 9.2.1

To implement the operation:

  • Project the inputs into query, key, and value; require the model dimension to be divisible by the number of heads. section 9.2.1
  • Reshape and transpose the projected tensors to separate heads. Within each head, scale query–key scores, optionally mask them, apply softmax, and use the resulting weights to combine values. section 9.2.1
  • Join the head outputs back into one representation. section 9.2.1
Listing 9.2 Calculating multihead attention
from copy import deepcopy
class MultiHeadedAttention(nn.Module):
    def __init__(self, h, d_model, dropout=0.1):
        super().__init__()
        assert d_model % h == 0
        self.d_k = d_model // h
        self.h = h
        self.linears = nn.ModuleList([deepcopy(
            nn.Linear(d_model, d_model)) for i in range(4)])
        self.attn = None
        self.dropout = nn.Dropout(p=dropout)
  
    def forward(self, query, key, value, mask=None):
        if mask is not None:
            mask = mask.unsqueeze(1)
        nbatches = query.size(0)  
        query, key, value = [l(x).view(nbatches, -1, self.h,
           self.d_k).transpose(1, 2)    
         for l, x in zip(self.linears, (query, key, value))]    #1
        x, self.attn = attention(
            query, key, value, mask=mask, dropout=self.dropout) #2
        x = x.transpose(1, 2).contiguous().view(
            nbatches, -1, self.h * self.d_k)                    #3
        output = self.linears[-1](x)                            #4
        return output

The PyTorch `MultiHeadedAttention` listing shows the head-count argument `h`, the per-head dimension `d_k`, and the projection and reshape before calling `attention()`. section 9.2.1

In a Transformer decoder, masked multi-head self-attention prevents a position from attending to later tokens. A separate cross-attention sublayer then relates the decoder state to the encoder output. The same parallel-head idea also appears in a decoder-only GPT model for text generation and in attention over image features in a denoising U-Net. section 9.3.1 section 12.1.1 section 15.3.1

How Learn Generative AI with PyTorch covers it

How deep it goes
Works from query, key, and value through per-head shapes and a PyTorch implementation, then shows decoder masking.
Examples
Uses a six-token sentence with eight 32-dimensional heads, a wordplay example, and a masked translation decoder.
What it assumes
Starts from token embeddings and teaches query, key, value, and scaled dot-product attention before splitting them into heads.
What you can do after
Build it from scratch as a PyTorch attention module and use it in Transformer encoder and decoder layers.
How it connects
Connects scaled dot-product attention with Transformer encoders, masked self-attention, cross-attention, and feed-forward networks.

Also explained in

Compare how 14 books explain multi-head attention →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Learn Generative AI with PyTorch. The section links point to the parts of the book it draws on.