Spot the GQA configuration error: num_attention_heads=32, num_key_value_heads=7.
Click any words you think contain an error. Click again to unmark.
GQA requires num_attention_heads % num_key_value_heads == 0. 32 mod 7 = 4, so the reshape fails at load time and 7 was almost certainly a typo for 8.
Picture 32 students in a classroom that need to be split into equal teams sharing one shared textbook per team. If you ask for 8 teams, every team gets 4 students; clean and fair. If you ask for 7 teams, the math fails: you cannot hand the same 32 students to 7 teams of equal size, because 32 does not divide cleanly into 7 parts. GQA works the same way. The 32 query heads are the students, the KV heads are the shared textbooks, and each KV head must serve the same number of query heads. Pick a divisor of 32 (1, 2, 4, 8, 16, 32) and life is easy. Pick 7 and the reshape blows up the moment the model tries to run.
Detailed answer & concept explanation~6 min readEverything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example.
Everything important, quickly.
Walk through the GQA reshape mechanics, explain why divisibility is a kernel constraint not an algorithmic one, identify where the error surfaces (forward pass, not config load), name 8 as the likely intended value, and end with a CI unit test that prevents future repeats.
# Validation that should live in your model config loader.
def validate_gqa_config(n_heads: int, n_kv_heads: int) -> None:
if n_heads % n_kv_heads != 0:
raise ValueError(
f"GQA requires n_heads ({n_heads}) divisible by "
f"n_kv_heads ({n_kv_heads}). "
f"For {n_heads} query heads, legal n_kv_heads values are "
f"divisors of {n_heads}: "
f"{[d for d in range(1, n_heads + 1) if n_heads % d == 0]}"
)
# This catches the bug at PR review, not at the first forward pass.
validate_gqa_config(n_heads=32, n_kv_heads=7) # raises ValueError
validate_gqa_config(n_heads=32, n_kv_heads=8) # passes (group_size=4)Real products, models, and research that use this idea.
- Llama-3 70B (2024): 64 query heads, 8 KV heads, group size 8.
- Mistral 7B: 32 query heads, 8 KV heads, group size 4.
- Mixtral 8x7B: 32 query heads, 8 KV heads per expert, group size 4.
- DeepSeek V4 (2025): uses MLA, a generalization of GQA that compresses KV via a low-rank latent rather than via head sharing.
- Qwen 3.5 (2026): family of GQA configurations across model sizes, all preserving the head divisibility constraint.
What an interviewer would ask next. Try answering before peeking at the approach.
QWhy did Llama-2 70B pick exactly 8 KV heads for 64 query heads rather than 4 or 16?
QHow does MLA in DeepSeek V4 sidestep the head-divisibility constraint entirely?
Don't say thisRed flags and common mistakes that signal junior thinking. Click to expand.
Red flags and common mistakes that signal junior thinking. Click to expand.
Assuming the config loaded successfully because the model file opened, missing that the shape error only fires when the attention reshape executes during the first real forward pass.
The night-before-the-interview bullets. Scan these on the way to the call.
Primary sources. Skim if you want the original framing.
Same topic, related formats. Practice these next.