[01-scaled-dot-product-attention] computes one single attention pattern per call. But a single pattern can only capture one type of relationship per layer (e.g. "attend to the previous token"). Multi-Head Attention (MHA) splits a model-dimension input into n_heads independent attention heads, runs scaled dot-product attention in each head in parallel, then concatenates the head outputs and projects them back to the model dimension — so each head can learn a different attention pattern cheaply.
Implement multi_head_attention(X, W_Q, W_K, W_V, W_O, n_heads, mask=None):
Q_h = X @ W_Q[h], K_h = X @ W_K[h], V_h = X @ W_V[h] for h = 1..n_heads
head_h = softmax(Q_h K_h^T / sqrt(d_head)) @ V_h
output = concat(head_1, ..., head_H) @ W_O
d_model must be evenly divisible by n_heads; d_head = d_model // n_heads.(d_model, d_model) matrices W_Q, W_K, W_V.sqrt(d_head)) independently per head.W_O of shape (d_model, d_model).Reshape (seq_len, d_model) into (seq_len, n_heads, d_head) by splitting the last axis, then transpose to (n_heads, seq_len, d_head). This is exactly equivalent to slicing n_heads separate weight matrices out of one big projection, and lets you do the whole thing as one batched matmul instead of a Python loop over heads.
After computing (n_heads, seq_len, d_head) head outputs, transpose back to (seq_len, n_heads, d_head) and reshape to (seq_len, d_model) before the final W_O projection — this must invert exactly the split you did going in, in head order.
Click "Run Tests" to test your implementation