LLM + RoboticsModule 08 · Stage 08

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.

PrerequisitesStage 01, self-attention and the KV cache at the level of 30u30 Day 13, basic GPU memory awareness
Time15–25 h: 5 h reading, 10–20 h for the GQA project and benchmarks
HardwareLaptop or free Colab GPU for the small version · one 24–80 GB GPU for the vLLM benchmark
Key papersMQA (Shazeer 2019), GQA (Ainslie et al. 2023), PagedAttention (Kwon et al. 2023), KIVI and KVQuant (2024)

Source: Suraj Sharma's roadmap post, stage 8 · saved in sources/08-suraj-ai-research-engineer.md

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

  1. For every word it reads, the model writes a "key" (what this word is about) and a "value" (what it says) for each layer.
  2. It keeps these in a memory store called the KV cache, so it doesn't redo the work for every new word.
  3. Each layer has many "heads" that look for different things. Normally each head keeps its own keys and values.
  4. Grouped-query attention lets several heads share one set of keys and values, so the cache shrinks.
  5. 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

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.

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:

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:

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.

KV per token
–
KV total
–
Smaller than MHA fp16
–
Tokens that fit in budget
–

Try this: the defaults are the Llama-3-8B shape (32 layers, 32 query heads, 8 KV heads, head dim 128) at 8,192 tokens: 128 KiB per token, 1 GiB total. Set KV heads to 32 for the Llama-2-7B shape: 4 GiB. Now set batch to 16 and KV precision to 4 bits: GQA fits in the 8 GiB budget at 4 GiB, while MHA at the same precision needs 16 GiB. Scale factors for quantisation ignore the small overhead of storing scales.

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.

  1. Start from nanoGPT (or your Stage 01 transformer) on Tiny Shakespeare: 6 layers, 8 query heads, embedding 256, context 256.
  2. 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.
  3. 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.
  4. Measure memory: compute KV bytes with the formula, then confirm by summing the sizes of the cached tensors during generation. They should agree exactly.
  5. 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.
  6. 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:

(c) Pitfalls

4. Reality check

The post says a model too slow or expensive to serve is useless, and that architecture dictates inference physics.

5. In this course

6. Why it matters for robotics

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

ResourceTypeUse it for
Google DeepMind · How To Scale Your Model (inference chapters)Free online bookKV-cache maths, batching and latency vs throughput
Hugging Face · KV cache explainedBlog guideWhat the KV cache stores and why it dominates memory
vLLM docsOfficial docsPaged KV cache, quantisation, speculative decoding in a real server
DeepLearning.AI · Efficiently Serving LLMsShort courseKV caching, batching, quantisation, LoRA serving
MIT 6.5940 · TinyML and Efficient Deep Learning ComputingUniversity course (free lectures)Quantisation, pruning, efficient attention, on-device and edge, which matters for robots too
Philip Kiely · Inference Engineering (book)BookProduction 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.

Full course map for all 12 stages →

9. Papers & docs