

Think of attention as a team of researchers trying to answer questions using a library: Queries are the questions asked, Keys are book index topics, and Values are the actual textbook contents.


Multi-Head Attention (MHA)
Multi-Query Attention (MQA)
Grouped-Query Attention (GQA)
| Feature | Multi-Head (MHA) | Grouped-Query (GQA) | Multi-Query (MQA) |
|---|---|---|---|
| Analogy Setup | 1 tutor per student | 1 tutor per small group | 1 tutor for the whole class |
| Memory Usage | Very High | Low–Medium | Extremely Low |
| Speed | Slow | Fast | Ultra Fast |
| Model Quality | Maximum | Near-Maximum | Reduced |
Whiteboard Memory Trick Use a quick visual formula to show how memory scales during generation:
5-Minute Classroom Activity Give students the sentence: “The chef cooked the soup because it was cold.” Ask them to map out what two distinct “heads” are looking at simultaneously:
This bridges the gap between human intuition and why models need multiple projections before jumping into code.
import torch
# Common dimensions
batch_size = 1
seq_len = 128
num_q_heads = 8
head_dim = 64
print("=== 1. MULTI-HEAD ATTENTION (MHA) ===")
num_kv_heads_mha = 8 # 1:1 ratio (8 Q heads, 8 KV heads)
q_mha = torch.randn(batch_size, num_q_heads, seq_len, head_dim)
k_mha = torch.randn(batch_size, num_kv_heads_mha, seq_len, head_dim)
print(f"Q shape: {list(q_mha.shape)}")
print(f"K shape: {list(k_mha.shape)} <-- Full memory footprint (8 heads)\n")
print("=== 2. GROUPED-QUERY ATTENTION (GQA) ===")
num_kv_heads_gqa = 2 # 4:1 ratio (8 Q heads share 2 KV heads)
group_size = num_q_heads // num_kv_heads_gqa
q_gqa = torch.randn(batch_size, num_q_heads, seq_len, head_dim)
k_gqa = torch.randn(batch_size, num_kv_heads_gqa, seq_len, head_dim)
print(f"Q shape: {list(q_gqa.shape)}")
print(f"K shape (Cache): {list(k_gqa.shape)} <-- 75% smaller memory footprint!")
# Expand Key heads to match Query heads right before matrix multiplication
k_gqa_expanded = k_gqa.repeat_interleave(group_size, dim=1)
print(f"K shape (Math): {list(k_gqa_expanded.shape)} <-- Expanded for dot-product")
Tracking the head dimension ($N_{\text{heads}}$) directly reveals how Grouped-Query Attention reduces GPU memory overhead during generation compared to Multi-Head Attention.
Key Takeaways to Point Out to Students
[1, 8, 128, 64] tensors in GPU VRAM, whereas GQA only stores [1, 2, 128, 64]. This 4x reduction in KV cache size allows LLMs to process much larger context windows.repeat_interleave): GQA saves memory in VRAM, but briefly duplicates the Key/Value heads along the head dimension right before computing dot-product attention so matrix shapes still align: $(1, 8, 128, 64)$.