Inference-aware model design
Choices you make before training (how many key-value heads, what precision the cache uses, whether a draft model can ride along) decide how much memory each token costs and how fast the model can answer. This stage is about doing that arithmetic first.
Start here (no maths needed)
When a language model writes, it keeps notes on every word so far. Those notes take lots of memory. Smart model design makes the notes smaller, so the model answers faster and fits on smaller computers.
Think of it like this
Picture a class of 32 students reading a long story together. Each student keeps their own full set of notes about every page. That's a lot of paper. Now split the class into 4 groups of 8, and each group shares one set of notes. That's 4 sets instead of 32, and everyone can still look things up. If the whole class shares one set, it's even smaller, but students start missing details they cared about. Grouped sharing is the sweet spot.
How it works
- For every word it reads, the model writes a "key" (what this word is about) and a "value" (what it says) for each layer.
- It keeps these in a memory store called the KV cache, so it doesn't redo the work for every new word.
- Each layer has many "heads" that look for different things. Normally each head keeps its own keys and values.
- Grouped-query attention lets several heads share one set of keys and values, so the cache shrinks.
- Storing the notes in fewer bits (like writing with smaller handwriting) shrinks it again.
Why robots care
A robot's computer has much less memory than a data centre, and it must decide fast. If the model stops to think for half a second while the arm is moving, the arm keeps moving. Smaller notes mean the model fits on the robot and answers in time.
Try it
- Write a 10-line summary of a story you know, one line per page. Then try again with a friend, sharing one summary between you. Is the shared one still good enough for both of you?
- In the demo further down, move KV heads from 32 down to 1 and watch the memory fall. Then change KV precision from 16 to 4 bits, and stretch Context to see when the notes no longer fit in the Memory budget.
Words to know
- Inference
- Using a trained model to answer, as opposed to training it.
- KV cache
- The stored keys and values for every earlier token, so they aren't recomputed.
- Attention head
- One of several parallel "lookups" inside an attention layer.
- Quantisation
- Storing numbers with fewer bits, trading a little accuracy for memory.
- Latency
- How long you wait for an answer.
Going deeper: everything below is the full version, with some maths. On a first read, skim the headings and the "Why it matters for robotics" box, and come back for the rest later.
1. What this stage covers
TL;DR
During generation, each new token reads every model weight and the whole KV cache from GPU memory. At long contexts or large batches the cache, not the weights, dominates both memory and bandwidth. Its size is 2 × layers × KV heads × head dim × tokens × bytes. Multi-query attention (one shared KV head) and grouped-query attention (a few shared KV heads) cut the "KV heads" term by 4–32× at a small quality cost. KV-cache quantisation cuts the "bytes" term by 2–8×. Both must be decided with serving in mind, and both interact with paging and speculative decoding, which Module 05 covers.
- Learn: GQA, MQA, KV-cache quantisation, and what makes a model work well with speculative decoding.
- Practice: convert a standard transformer to GQA, measure the KV-cache memory saved, and benchmark latency and throughput in vLLM.
- Why (the post's claim): a model too slow or expensive to serve is useless, and architecture sets the inference costs. Largely true; see section 4.
2. Concepts, explained
First, the KV-cache arithmetic
In a decoder, each layer computes keys and values for every token. Generating token t+1 needs the keys and values of tokens 1…t, so servers keep them instead of recomputing. The size is exact and worth memorising:
KV bytes = 2 × L × n_kv × d_head × T × B × bytes_per_value
(K and V) (layers) (KV heads) (head dim) (tokens) (batch)
Llama-2-7B shape (L=32, n_kv=32, d_head=128), fp16:
per token = 2 × 32 × 32 × 128 × 2 B = 512 KiB
4,096 tokens = 2 GiB per sequence
Llama-3-8B shape (L=32, n_kv=8, d_head=128), fp16:
per token = 128 KiB → 8,192 tokens = 1 GiB per sequence
Why it matters for speed: decoding one token at batch 1 is memory-bandwidth bound. The GPU must stream all weights plus the cache from memory for very little arithmetic. A rough floor is (weight bytes + KV bytes) ÷ bandwidth. For a 7B fp16 model (about 14 GB of weights) on a GPU with 1 TB/s, that's about 14 ms per token before the cache. At short contexts the weights dominate. At long contexts or large batches the cache overtakes them, because weights are read once per step for the whole batch while each sequence brings its own cache. So shrinking the cache buys bigger batches (throughput) and longer contexts, and only buys per-token latency once the cache is a large share of the bytes read.
Multi-query attention (MQA)
Noam Shazeer's 2019 paper "Fast Transformer Decoding: One Write-Head is All You Need" kept many query heads but shared a single key head and value head across all of them. The cache shrinks by a factor of n_heads (32× for a 32-head model). The paper reports much faster decoding and only minor quality loss. In practice MQA can hurt quality and training stability at scale, and with tensor parallelism the single KV head has to be duplicated on every GPU, which gives back some of the saving.
Grouped-query attention (GQA)
GQA (Ainslie, Lee-Thorp, de Jong, Zemlyanskiy, Lebrón and Sanghai, 2023) is the middle ground: split the query heads into G groups, and give each group one shared KV head. G = n_heads is ordinary multi-head attention (MHA); G = 1 is MQA.
MHA: n_q = 32, n_kv = 32 cache × 1
GQA: n_q = 32, n_kv = 8 cache × 1/4 (each KV head serves 4 query heads)
MQA: n_q = 32, n_kv = 1 cache × 1/32
attention for query head h uses K, V of KV head ⌊h / (n_q / n_kv)⌋
The paper's second contribution is uptraining: you don't need to retrain from scratch. Mean-pool the key and value projection weights of the heads in each group into one head, then continue pre-training for about 5% of the original compute. Uptrained GQA came close to MHA quality at a speed close to MQA. Eight KV heads has become a common choice: Llama 2 70B, Llama 3 and Mistral 7B all use it.
A further step is to compress the cache rather than share it. DeepSeek's Multi-head Latent Attention (MLA) caches one small latent vector per token and reconstructs keys and values from it. Module 05 explains MLA and how Kimi K3 mixes it with linear-attention layers whose state doesn't grow at all; see the length section there.
KV-cache quantisation
Weights are fixed and can be quantised offline with care. The cache is created on the fly, so its quantisation must be cheap and must cope with whatever activations appear. Two findings shape the methods:
- Keys have outlier channels. A few channels of the key vectors carry much larger values than the rest, consistently across tokens. Quantising keys per channel isolates them. Values have no such pattern and are quantised per token. This is the core finding of KIVI (Liu et al., 2024), a tuning-free 2-bit scheme that also keeps the most recent tokens in full precision until a group fills.
- Rotary embeddings mix channels. KVQuant (Hooper et al., 2024) quantises keys before RoPE is applied, uses non-uniform per-layer data types, and stores outliers separately in a sparse format. It reports under 0.1 perplexity loss at 3 bits on Llama-family models.
q = round((x − min) / scale), scale = (max − min) / (2^bits − 1)
keys: min/max/scale per channel (over a group of tokens)
values: min/max/scale per token (over its channels)
memory ≈ bits/16 of fp16, plus a small overhead for the scales
In production the common, safe step is FP8 KV cache, which vLLM supports through --kv-cache-dtype. It halves the cache with usually small quality impact. Going to 4 or 2 bits needs the tricks above and careful evaluation, especially on long-context retrieval tasks, where small errors in old keys can make the model attend to the wrong place.
Speculative-decoding compatibility
Speculative decoding (Leviathan, Kalman and Matias, 2022) lets a cheap draft propose several tokens and the large model verify them in one forward pass, with an acceptance rule that leaves the output distribution exactly unchanged. Module 05 covers the mechanism and how K3 trained its draft for acceptance rate (serving section). The design questions for this stage are what make a model easy to speculate with:
- Same tokenizer. A separate draft model must share the target's vocabulary. Plan a small sibling model at training time.
- Built-in drafts. Extra prediction heads (Medusa, EAGLE) or multi-token prediction trained alongside the main model give a draft for free, but only if they're designed in.
- Cheap verification. Verifying k tokens is a short prefill that reads the whole cache. GQA and KV quantisation make that cheaper too.
- Memory for two caches. The draft has its own KV cache, which counts against the same budget.
- Predictable outputs. Acceptance is higher on predictable text (code, structured output) and lower at high sampling temperature, so the gain depends on the workload.
Interactive: KV-cache memory for MHA, GQA and MQA
Total KV cache (GiB, log scale) against context length, for the whole batch. Dashed: MHA (KV heads = query heads). Solid: your KV-head setting. Dotted: MQA (1 KV head). The horizontal line is your memory budget for the cache. The marker is your context length.
3. The practice project
(a) Laptop/Colab version: MHA vs GQA vs MQA on a tiny model
Train three tiny character-level GPTs that differ only in KV heads, then measure quality, cache size and decode speed. This runs on a laptop CPU in a few hours or a Colab GPU in under one.
- Start from nanoGPT (or your Stage 01 transformer) on Tiny Shakespeare: 6 layers, 8 query heads, embedding 256, context 256.
- Swap the attention for the GQA module below. Train three models with n_kv = 8, 2 and 1 for the same number of steps and seed.
- Measure quality: validation loss for each. Expect GQA-2 to be close to MHA and MQA a little worse, but check rather than assume; at this tiny scale the differences may be within seed noise, so run two seeds.
- Measure memory: compute KV bytes with the formula, then confirm by summing the sizes of the cached tensors during generation. They should agree exactly.
- Measure speed: tokens per second when generating 2,000 tokens at batch 1 and batch 32. Expect little gain at batch 1 on a tiny model, and a clearer gain at larger batch and context.
- Uptraining exercise: take the trained MHA model, mean-pool its KV heads into 2 groups, measure the loss jump, then fine-tune for 5% of the original steps and measure again.
import torch, torch.nn as nn, torch.nn.functional as F
class GQA(nn.Module):
def __init__(s, d, n_q, n_kv):
super().__init__()
s.n_q, s.n_kv, s.hd = n_q, n_kv, d // n_q
s.q = nn.Linear(d, n_q * s.hd, bias=False)
s.kv = nn.Linear(d, 2 * n_kv * s.hd, bias=False)
s.o = nn.Linear(d, d, bias=False)
def forward(s, x, cache=None): # x: [B, T, d]
B, T, _ = x.shape
q = s.q(x).view(B, T, s.n_q, s.hd).transpose(1, 2)
k, v = s.kv(x).view(B, T, 2, s.n_kv, s.hd).permute(2, 0, 3, 1, 4)
if cache is not None: # [B, n_kv, T_past, hd]
k, v = torch.cat([cache[0], k], 2), torch.cat([cache[1], v], 2)
new_cache = (k, v) # the part GQA shrinks
r = s.n_q // s.n_kv
k, v = k.repeat_interleave(r, 1), v.repeat_interleave(r, 1)
y = F.scaled_dot_product_attention(q, k, v, is_causal=cache is None)
return s.o(y.transpose(1, 2).reshape(B, T, -1)), new_cache
def pool_heads(W, n_q, n_kv): # W: [n_q*hd, d] → [n_kv*hd, d]
hd = W.shape[0] // n_q
return W.view(n_kv, n_q // n_kv, hd, -1).mean(1).reshape(n_kv * hd, -1)
Note that repeat_interleave copies data at compute time; only the cache is smaller. Real kernels (and recent PyTorch's enable_gqa option) index the shared heads without copying.
(b) The full version
The post asks for a GQA conversion benchmarked in vLLM. A realistic plan: take an open MHA checkpoint of about 1B parameters, convert it to 4 or 8 KV heads by mean-pooling, uptrain on a few billion tokens of similar data, and serve both versions in vLLM on one 24–80 GB GPU. Uptraining a 1B model on a few billion tokens is a multi-GPU-day job, so budget roughly tens to low hundreds of dollars of rented compute. Then:
- Record the KV-cache capacity vLLM reports at startup for each model. With the same memory setting, the GQA model should hold n_q/n_kv times more tokens.
- Benchmark with vLLM's benchmarking tools across concurrency levels and prompt lengths. Report time to first token, inter-token latency and output tokens per second.
- Repeat with
--kv-cache-dtype fp8on both. - Evaluate quality on the same tasks before and after, including at least one long-context retrieval test.
(c) Pitfalls
- Measuring VRAM with nvidia-smi. vLLM pre-allocates a fixed share of GPU memory, so total usage looks the same for both models. Compare KV-cache token capacity instead.
- Expecting latency wins at batch 1, short prompts. There the weights dominate the bytes read. GQA's gains show up as larger batches, longer contexts and higher throughput.
- "The exact drop". The cache saving is exactly n_q/n_kv by construction. The interesting measurements are the quality cost and the end-to-end throughput change.
- Skipping uptraining. Mean-pooled heads without further training lose noticeable quality. The GQA paper's result depends on uptraining.
- Tensor parallelism. With more GPUs than KV heads, KV heads are replicated, and the saving per GPU shrinks.
4. Reality check
The post says a model too slow or expensive to serve is useless, and that architecture dictates inference physics.
- True: serving cost is a design constraint, not an afterthought. GQA became near-universal in open models because it changes how many users fit on a GPU, and Module 05 shows Kimi K3's attention design was shaped by serving.
- Overstated: architecture is one lever among several. Quantisation of weights, batching, paged memory, prefix caching, speculative decoding and the kernel stack often matter as much, and they can be added after training. "Useless" is too strong: slow, accurate models are routinely distilled into fast ones (Module 04).
- Missing: the cache is not the bottleneck for every workload. Short chats at small batch are bound by weights; that's why weight quantisation and smaller models come first for edge and robot deployment.
5. In this course
- Module 05 · Serving: MLA, the paged KV pool shared by two cache types, and speculative decoding with a draft trained for acceptance rate. Read this before or after this page; it's the production version of these ideas.
- Module 05 · Length: linear-attention layers whose state doesn't grow with context.
- Module 04 · Latency is a safety number: how milliseconds turn into metres on a moving robot.
- Module 02 · TensorTonic: KV cache, quantisation and speculative decoding practice problems.
- 30u30 Day 13 · The Transformer: multi-head attention, the starting point for GQA.
- Stage 07 · Multimodal fusion: why multi-camera robot models produce so many tokens to cache.
6. Why it matters for robotics
- Robot computers are memory-poor. Embedded GPU modules share a modest memory pool between weights, cache, perception and everything else. Per-token cache cost decides how much camera history and instruction context a VLA can keep on board.
- Vision tokens fill the cache fast. Stage 07's demo shows a multi-camera setup reaching thousands of tokens per step. With the Llama-3-8B shape at fp16, 4,374 tokens is about 547 MiB of cache per control step, before any history.
- Reuse the prefix. The instruction and current images are the same for every action decoded in one step. The π0 paper (Black et al., 2024) describes this: its action expert attends to the VLM prefix, whose keys and values are computed once and reused across the action expert's integration steps. Designing so the expensive part is cached is an inference-aware choice.
- Batch 1 is normal on a robot. One robot runs one episode, so it sits in the weight-bound regime. Weight quantisation and smaller distilled models usually come first; GQA and KV quantisation matter most for long histories and for server-side fleets of robots.
- Latency is a control-rate budget. Autoregressive action tokens (Stage 07) multiply decode steps per action. Action chunking and parallel decoding exist largely to fit the control loop.
7. Self-check
A model has 80 layers, 8 KV heads and head dim 128. How big is its fp16 KV cache at 128k (131,072) tokens for one sequence?
Per token: 2 × 80 × 8 × 128 × 2 B = 327,680 B = 320 KiB. Times 131,072 tokens = 40 GiB.
What do G = n_heads and G = 1 correspond to in GQA?
G = n_heads is ordinary multi-head attention, one KV head per query head. G = 1 is multi-query attention, one KV head shared by all.
Why might switching to GQA barely change latency for a single short chat?
At batch 1 and short context, decode time is dominated by reading the weights from memory. The cache is a small share of the bytes, so shrinking it changes little. Gains appear at long contexts and large batches.
Why do KV quantisation methods treat keys and values differently?
Keys have a few consistent outlier channels, so they're quantised per channel. Values have no such channel pattern and are quantised per token. KIVI's analysis found this split works best.
How do you convert an MHA checkpoint to GQA without retraining from scratch?
Mean-pool the key and value projection heads within each group into one head, then uptrain for a small fraction of the original compute (about 5% in the GQA paper).
Name two design choices that make a model easier to use with speculative decoding.
Training a small sibling draft model with the same tokenizer, or building in extra prediction heads or multi-token prediction. Smaller KV caches also make the verification pass cheaper.
8. Courses & content
| Resource | Type | Use it for |
|---|---|---|
| Google DeepMind · How To Scale Your Model (inference chapters) | Free online book | KV-cache maths, batching and latency vs throughput |
| Hugging Face · KV cache explained | Blog guide | What the KV cache stores and why it dominates memory |
| vLLM docs | Official docs | Paged KV cache, quantisation, speculative decoding in a real server |
| DeepLearning.AI · Efficiently Serving LLMs | Short course | KV caching, batching, quantisation, LoRA serving |
| MIT 6.5940 · TinyML and Efficient Deep Learning Computing | University course (free lectures) | Quantisation, pruning, efficient attention, on-device and edge, which matters for robots too |
| Philip Kiely · Inference Engineering (book) | Book | Production inference from the Baseten side (see Module 05) |
Start with the inference chapters of How To Scale Your Model: they teach the bandwidth-versus-compute arithmetic this whole stage rests on, with worked examples. Then use the vLLM docs for the benchmark.
9. Papers & docs
- Fast Transformer Decoding: One Write-Head is All You Need (MQA) (Shazeer, 2019)
- GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints (Ainslie et al., 2023)
- Efficient Memory Management for LLM Serving with PagedAttention (vLLM) (Kwon et al., 2023)
- KIVI: A Tuning-Free Asymmetric 2bit Quantization for KV Cache (Liu et al., 2024)
- KVQuant: Towards 10 Million Context Length LLM Inference with KV Cache Quantization (Hooper et al., 2024)
- Fast Inference from Transformers via Speculative Decoding (Leviathan, Kalman, Matias, 2022)
- π0: A Vision-Language-Action Flow Model for General Robot Control (Black et al., 2024)
- Docs: vLLM documentation (quantised KV cache, speculative decoding, benchmarking)