Grouped Query Attention › Choosing the Right Number of KV Heads

Michael BrenndoerferUpdated August 6, 20254 min read
Language AI HandbookAll chapters
Part 29 of 67Modern Decoder Models
Chapter 209Grouped Query Attention › Choosing the Right Number of KV Heads
Reading section 8 of 14 · Choosing the Right Number of KV Heads · 4 minSection 8 of 14 · 4 min

Book contents

Section 8 of 14 · 4 min read

Grouped Query Attention › Choosing the Right Number of KV HeadsOne section · Sections in this chapter8 / 14 ↓
Reading tools Definitions & inline links

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

The optimal number of KV heads depends on your specific constraints and requirements. Let's develop a framework for making this decision.

In[19]:
Code
def analyze_gqa_tradeoffs(
    num_query_heads: int,
    d_model: int,
    num_layers: int,
    target_memory_gb: float,
    seq_length: int,
    batch_size: int,
):
    """Find GQA configurations that fit within a memory budget."""
    bytes_per_element = 2  # fp16
    head_dim = d_model // num_query_heads

    viable_configs = []

    for num_kv_heads in [1, 2, 4, 8, 16, num_query_heads]:
        if num_query_heads % num_kv_heads != 0:
            continue

        # KV cache memory
        cache_bytes = (
            2
            * num_layers
            * num_kv_heads
            * head_dim
            * seq_length
            * batch_size
            * bytes_per_element
        )
        cache_gb = cache_bytes / 1e9

        # KV projection parameters
        kv_params = 2 * d_model * num_kv_heads * head_dim

        # Estimated quality (simplified model based on research)
        quality_factor = min(
            1.0, 0.92 + 0.08 * (num_kv_heads / num_query_heads) ** 0.5
        )

        config = {
            "kv_heads": num_kv_heads,
            "cache_gb": cache_gb,
            "kv_params_m": kv_params / 1e6,
            "quality": quality_factor * 100,
            "fits_budget": cache_gb <= target_memory_gb,
            "memory_reduction": num_query_heads / num_kv_heads,
        }
        viable_configs.append(config)

    return viable_configs


# Example: 70B model on 80GB GPU with 8K context
configs = analyze_gqa_tradeoffs(
    num_query_heads=64,
    d_model=8192,
    num_layers=80,
    target_memory_gb=20.0,  # Reserve 60GB for model weights
    seq_length=8192,
    batch_size=8,
)
Out[20]:
Console
GQA Configuration Analysis (70B model, 8K context, batch=8)
Memory budget for KV cache: 20 GB
======================================================================

KV Heads   Cache        Quality      Reduction    Fits?
----------------------------------------------------------------------
1          2.7 GB      93.0%       64x         ✓
2          5.4 GB      93.4%       32x         ✓
4          10.7 GB      94.0%       16x         ✓
8          21.5 GB      94.8%       8x         ✗
16         42.9 GB      96.0%       4x         ✗
64         171.8 GB      100.0%       1x         ✗

Recommended: GQA-4 (highest quality that fits budget)
Out[21]:
Visualization
Scatter plot with quality on y-axis and memory on x-axis, showing viable GQA configurations.
Decision framework for choosing GQA configuration. The plot shows the quality-memory trade-off curve, with the green region showing configurations that fit within the 20 GB memory budget. GQA-8 is often the sweet spot, giving good quality retention with significant memory savings.

The framework reveals several insights:

  • MHA is only viable for small batches or short contexts
  • GQA-8 fits most practical deployment scenarios while retaining near-MHA quality
  • GQA-4 or GQA-2 may be necessary for very long contexts or large batches
  • MQA should be reserved for extreme memory constraints