I started with the math and worked forward, splitting Q/K/V into heads by reshaping, running softmax(QK^T / sqrt(d_k))V per head, then cat and project.
Start by clarifying the input shapes and the desired output, then implement the attention mechanism step-by-step: linear projections, splitting into heads, scaled dot-product attention with optional masking, concatenation, and output projection. Explain the purpose of scaling and masking, and discuss trade-offs like computational complexity and memory usage.
Pro tip: Mention that you would use `torch.einsum` or `torch.matmul` with broadcasting for efficient batched computation, and that you would verify correctness by comparing against a reference implementation like PyTorch's `nn.MultiheadAttention`.
Confirm the shapes of Q, K, V (batch_size, seq_len, d_model) and the number of heads. Determine the output shape and whether masking is needed.
Apply learned linear projections to Q, K, V to get d_model dimensions, then reshape and transpose to split into heads: (batch_size, num_heads, seq_len, d_k).
Compute attention scores as Q @ K^T / sqrt(d_k), apply optional masks (causal or padding), apply softmax, and multiply by V to get per-head outputs.
Transpose and reshape the per-head outputs back to (batch_size, seq_len, d_model), then apply a final linear projection.
Describe causal masking (preventing attention to future tokens) and padding masking (ignoring padding tokens), and justify scaling by 1/sqrt(d_k) to stabilize gradients.
AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.