Multi-Query Attention
Decoding is bound by the bandwidth needed to re-read the KV cache each step. Share one K/V head across all query heads and that cache shrinks by the head count.
At batch size 1 an autoregressive step performs a handful of matrix–vector products and re-reads the entire KV cache. Arithmetic intensity is therefore near 1, and the step time is a division: bytes read over memory bandwidth.
MQA sets ; grouped-query attention interpolates, sharing one K/V head per group of query heads. GQA at recovers essentially all of multi-head quality at an eighth of the cache, which is why it, rather than MQA, is what shipped.
import torch
from torch import Tensor
import torch.nn.functional as F
def gqa(q: Tensor, k: Tensor, v: Tensor, mask=None) -> Tensor:
"""q: [B, H, N, D] k, v: [B, H_kv, M, D] H % H_kv == 0"""
h, h_kv = q.size(1), k.size(1)
if h != h_kv:
repeat = h // h_kv
k = k.repeat_interleave(repeat, dim=1)
v = v.repeat_interleave(repeat, dim=1)
return F.scaled_dot_product_attention(q, k, v, attn_mask=mask)repeat_interleave here is expository — it materialises the expansion. In a real
kernel the grouping is expressed as a stride of zero over the head dimension, so
the shared K/V head is read once and broadcast, which is the entire point of the
technique.