Hand-Write a Multi-Head Attention Layer: Projections, Head Splitting and Masking

Quick Overview

Write a multi-head attention layer from scratch: project queries, keys and values, split them into heads, apply scaled dot-product attention with optional padding or causal masks, and merge the heads. It tests tensor shape discipline, numerical stability, and the cost of attention as sequence length grows.

Hand-Write a Multi-Head Attention Layer: Projections, Head Splitting and Masking

Company: ByteDance

Role: Software Engineer

Category: Machine Learning

Difficulty: medium

Interview Round: Onsite

Implement a multi-head attention layer from scratch, as you would in a live coding round. The layer receives query, key and value inputs, projects each of them with learned weights, splits the projections into several heads, runs scaled dot-product attention independently in every head, then concatenates the heads and applies an output projection. It should accept an optional attention mask. ```hint Write down every shape Annotate the tensor shape after each line. Most bugs in this exercise are a reshape or a transpose that silently mixes heads with sequence positions. ``` ```hint Masks and the softmax Decide at which step the mask must be applied for the attention weights to remain a valid distribution, and check what your code returns for a query that is not allowed to attend to anything. ``` ### Constraints and Clarifications - Assume NumPy or PyTorch. Basic tensor operations (matrix multiplication, reshape, transpose, softmax, linear layers) are allowed; a library attention module such as `torch.nn.MultiheadAttention` or `torch.nn.functional.scaled_dot_product_attention` is not. - Inputs are batched: queries have shape `(batch, len_q, d_model)`, and keys and values have shape `(batch, len_k, d_model)`. - Assume `d_model` is divisible by the number of heads. ### Clarifying Questions - Is this self-attention only, or must the same layer handle cross-attention, where queries and keys have different lengths? - Which masks are needed: key padding, causal, or both? Does `True` in a boolean mask mean "may attend" or "blocked"? - Should the layer apply dropout to the attention weights, and should it return the weights along with the output? - Is a forward pass enough, or should the implementation also be checked for gradients and for numerical behavior in half precision? ### What a Strong Answer Covers - Correct projections, head splitting and head merging, with consistent shapes for self- and cross-attention - The right scaling factor, a softmax over the correct axis, and masking applied at the correct step - Numerical robustness: large scores, fully masked rows, reduced precision - Time and memory cost as functions of sequence length, model width and number of heads - How the implementation would be tested against a reference and for mask correctness ### Follow-up Questions - Why are the scores divided by $\sqrt{d_k}$, and what happens during training if that step is left out? - What are the time and memory costs in sequence length, and how would you reduce them for long sequences? - With the same `d_model`, what does multi-head attention gain over a single head, given that the number of parameters is the same? - How would you extend the layer for step-by-step autoregressive decoding without recomputing attention over the whole prefix at every step?

Overview: Write a multi-head attention layer from scratch: project queries, keys and values, split them into heads, apply scaled dot-product attention with optional padding or causal masks, and merge the heads. It tests tensor shape discipline, numerical stability, and the cost of attention as sequence length grows.

|Home/Machine Learning/ByteDance
ByteDance logo
ByteDance
Oct 10, 2026
mediumSoftware EngineerOnsiteMachine Learning
0
0

Implement a multi-head attention layer from scratch, as you would in a live coding round. The layer receives query, key and value inputs, projects each of them with learned weights, splits the projections into several heads, runs scaled dot-product attention independently in every head, then concatenates the heads and applies an output projection. It should accept an optional attention mask.

Constraints and Clarifications

  • Assume NumPy or PyTorch. Basic tensor operations (matrix multiplication, reshape, transpose, softmax, linear layers) are allowed; a library attention module such as torch.nn.MultiheadAttention or torch.nn.functional.scaled_dot_product_attention is not.
  • Inputs are batched: queries have shape (batch, len_q, d_model) , and keys and values have shape (batch, len_k, d_model) .
  • Assume d_model is divisible by the number of heads.

Clarifying Questions Guidance

  • Is this self-attention only, or must the same layer handle cross-attention, where queries and keys have different lengths?
  • Which masks are needed: key padding, causal, or both? Does True in a boolean mask mean "may attend" or "blocked"?
  • Should the layer apply dropout to the attention weights, and should it return the weights along with the output?
  • Is a forward pass enough, or should the implementation also be checked for gradients and for numerical behavior in half precision?

What a Strong Answer Covers Guidance

  • Correct projections, head splitting and head merging, with consistent shapes for self- and cross-attention
  • The right scaling factor, a softmax over the correct axis, and masking applied at the correct step
  • Numerical robustness: large scores, fully masked rows, reduced precision
  • Time and memory cost as functions of sequence length, model width and number of heads
  • How the implementation would be tested against a reference and for mask correctness

Follow-up Questions Guidance

  • Why are the scores divided by dk\sqrt{d_k} , and what happens during training if that step is left out?
  • What are the time and memory costs in sequence length, and how would you reduce them for long sequences?
  • With the same d_model , what does multi-head attention gain over a single head, given that the number of parameters is the same?
  • How would you extend the layer for step-by-step autoregressive decoding without recomputing attention over the whole prefix at every step?