PythiloLesson 26 of 30 · progress kept in this browser

Multi-head attention

One attention head mixes positions by one notion of similarity. Multi-head attention splits the d features of each position into h heads of d / h features, lets each head attend on its own, then joins them and mixes them with one more linear layer.

import torch
import torch.nn.functional as F

B, T, D, H = 2, 5, 8, 2
x = torch.randn(B, T, D)
heads = x.view(B, T, H, D // H)
heads = heads.transpose(1, 2)
print(heads.shape)  # (B, H, T, D // H)
out = F.scaled_dot_product_attention(
    heads, heads, heads)
merged = out.transpose(1, 2)
print(merged.reshape(B, T, D).shape)

The split is a reshape to (B, T, H, D // H) followed by a transpose. Reshaping straight to (B, H, T, D // H) has the same shape but cuts the numbers in the wrong places, mixing features of different positions into one head.

A padding mask stops every query from attending to padded keys: their scores become minus infinity before the softmax. For F.scaled_dot_product_attention, a boolean attn_mask is True where attending is allowed.

Your task

Write MultiHead(d, heads), an nn.Module with four nn.Linear(d, d, bias=False) layers q, k, v and o. forward(x, pad=None) takes (batch, positions, d) and an optional boolean pad of shape (batch, positions), True at padded positions no query may attend to, and returns o of the merged heads, shaped like x.

This lesson uses torch, which Python in this browser does not include. Read it here, and run and check it in the Pythilo app on iPhone or iPad.