Implement Masked Multi-Head Self-Attention
AppleMachine Learning Engineer · Technical ScreenLast updated
This question tests the ability to implement the core mechanics of a Transformer's multi-head attention mechanism, specifically focusing on linear projections, attention weight scaling, and causal masking.
Problem Statement
Implement a MultiHeadAttention class in Python using a framework like PyTorch or NumPy. Your implementation must support scaled dot-product attention with causal masking to prevent tokens from attending to future positions.
Requirements
- Linear Projections: Given an input embedding tensor
xof shape(batch_size, seq_len, d_model), create projections forQuery,Key, andValueusing linear layers to reach a dimension ofd_model. - Multi-Head Split: Split the projected tensors into
n_heads. Each head will have a dimension ofd_k = d_model // n_heads. - Scaled Dot-Product Attention: Compute
Attention(Q, K, V) = softmax((Q @ K.transpose) / sqrt(d_k) + mask) @ V. - Causal Masking: Apply a lower-triangular mask (filled with
-infor a very large negative number) to the attention scores before the softmax to ensure positionionly attends to positions<= i. - Concatenation: Concatenate the outputs of all heads and project them back to
d_model.
Signature
class MultiHeadAttention:
def __init__(self, d_model, n_heads):
# Initialize linear layers for Q, K, V and output projection
pass
def forward(self, x):
# Return the attended output tensor of shape (batch_size, seq_len, d_model)
pass
Example
Input x has shape (1, 3, 8) (batch size 1, sequence length 3, embedding size 8). With n_heads=2, each head processes a dimension of 4. The causal mask ensures the third token cannot attend to the first or second token's future information.
Constraints
d_modelis divisible byn_heads.- The output shape must match the input shape.
Hints
Hint 1
Before computing the softmax, create a `(seq_len, seq_len)` matrix where the upper triangle is filled with negative infinity.
Hint 2
Ensure you reshape the tensors from `(batch, seq_len, d_model)` to `(batch, n_heads, seq_len, d_k)` to allow parallel computation across heads.
Hint 3
Remember to scale the dot product of Q and K by `1 / sqrt(d_k)` to maintain stable gradients during training.
Follow-up questions
- How would you modify this implementation to support padding masks for variable-length sequences in a batch?
- Why is the scaling factor `1 / sqrt(d_k)` critical when the dimensionality of the embeddings increases?
- If this model were used for inference in a large language model, how would you optimize the Key-Value (KV) cache to avoid redundant computation?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.