Start by outlining the overall computation flow and tensor shapes, then implement each component (linear projections, head splitting, scaled dot-product attention with optional mask, softmax stabilization, concatenation, and output projection) in a clear, modular way. Emphasize numerical stability by subtracting the row-wise maximum before exponentiation and using a large negative value for masked positions.
Pro tip: Mention that you would use `torch.nn.functional.scaled_dot_product_attention` in production for efficiency, but implement manually to demonstrate understanding. Also, note that masking should be applied before softmax to avoid NaNs.
Clarify input tensor shapes (batch_size, seq_len, d_model) and project to queries, keys, values using learned weight matrices to get (batch_size, seq_len, d_model) for each. Mention that d_model = num_heads * d_k.
Reshape Q, K, V from (batch_size, seq_len, d_model) to (batch_size, num_heads, seq_len, d_k) by viewing and transposing. Explain that this allows parallel attention computations per head.
Compute attention scores as Q @ K^T / sqrt(d_k), shape (batch_size, num_heads, seq_len, seq_len). Apply mask (if provided) by setting masked positions to a large negative value (e.g., -1e9) before softmax. Then compute softmax along the last dimension with numerical stability by subtracting the max per row.
Multiply attention weights (after softmax) with V to get (batch_size, num_heads, seq_len, d_k). Transpose and reshape back to (batch_size, seq_len, d_model) to concatenate heads.
Apply a final linear layer to the concatenated output to produce the final result of shape (batch_size, seq_len, d_model). Optionally mention dropout.
AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.