llm-inference-explained
← /learn · 07

Speculative decoding

Draft, verify, and the expected tokens per step.

Concept

Decode is memory-bound: a step spends its time reading the weights, and the arithmetic units mostly wait. So a step that checks several positions costs about the same as one that produces a single token. Speculative decoding exploits that.

  1. A small, fast draft model (or a cheap extra head on the big model) guesses the next γ\gamma tokens, one after another.
  2. The large target model runs one forward pass over all γ\gamma guesses at once, as if they were a short prompt, which gives its own distribution at every one of those positions.
  3. A verify rule walks the guesses in order, accepting each with probability min⁡(1,p(x)/q(x))\min(1, p(x)/q(x)), where pp is the target's probability of the guessed token and qq the draft's. At the first rejection it draws a replacement from the leftover distribution and stops. If all γ\gamma are accepted, the target's pass also gives one more token for free.

Each target pass therefore yields between 1 and γ+1\gamma + 1 tokens. And the rule is built so that the tokens come out with exactly the target model's distribution. Speculative decoding changes the speed, never the output distribution: the draft only decides how much of the target's work is reused. (Under greedy decoding this reduces to "keep the guesses that match the target's argmax".)

What it costs: the draft's own steps, and some wasted target work on rejected guesses. It pays when the draft agrees with the target often (acceptance α\alpha high) and is cheap (cost ratio cc low). It helps most at small batch sizes; at large batches the target step is no longer so memory-bound, and the verified positions compete with other users'.

Variants. Medusa adds several extra decoding heads to the target itself, each predicting a token further ahead, and verifies a tree of candidates in one pass. EAGLE drafts at the level of the target's hidden features rather than tokens, with a light head. Both avoid running a separate draft model.

Interactive

Draft γ tokens, verify them in one pass

Top: Leviathan et al.'s closed forms (i.i.d. acceptance α; one draft step costs c of a target step). Bottom: 4,000 rounds of the real accept/reject rule on an 8-token toy vocabulary.

Tokens / target pass
3.36
Speed-up
2.80×
Best γ here
8
3.09×
Tokens if all accepted
5
1×123456789101112γ
α = Σ min(p, q)
0.639
Measured acceptance
0.642
Tokens / pass
2.49
formula 2.48
γ
4
tok 041.6 / 41.5%
tok 122.8 / 22.6%
tok 213.8 / 13.9%
tok 39.3 / 9.1%
tok 45.6 / 5.6%
tok 53.4 / 3.6%
tok 62.1 / 2.2%
tok 71.4 / 1.3%

Grey: the target distribution p. Blue: what speculative decoding actually emitted. They match whatever the draft is: only the speed changes.

Maths

Let the target be pp and the draft qq. A drafted x∼qx \sim q is accepted with probability min⁡(1,p(x)/q(x))\min(1, p(x)/q(x)), so the per-token acceptance is

β=∑xq(x)min⁡ ⁣(1,p(x)q(x))=∑xmin⁡(p(x),q(x)).\beta = \sum_x q(x)\min\!\left(1, \frac{p(x)}{q(x)}\right) = \sum_x \min(p(x), q(x)).

On rejection the replacement comes from r(x)=max⁡(0,p(x)−q(x))1−βr(x) = \frac{\max(0, p(x) - q(x))}{1 - \beta}. The emitted token's distribution is then

min⁡(p(x),q(x))+(1−β) r(x)=p(x),\min(p(x), q(x)) + (1 - \beta)\,r(x) = p(x),

exactly the target's (Leviathan et al., §3; Chen et al.). With i.i.d. acceptance α=E[β]\alpha = \mathbb{E}[\beta] and γ\gamma drafts, the expected tokens per target pass are

E[tokens]=1−αγ+11−α,\mathbb{E}[\text{tokens}] = \frac{1 - \alpha^{\gamma+1}}{1 - \alpha},

and if a draft step costs cc target steps, the expected speed-up is 1−αγ+1(1−α)(γc+1)\frac{1 - \alpha^{\gamma+1}}{(1-\alpha)(\gamma c + 1)} (their Theorem 3.8). For example α=0.8\alpha = 0.8, γ=4\gamma = 4 gives 3.36 tokens per pass, and with c=0.05c = 0.05 a 2.80× speed-up.

Code

// src/lib/inference/speculative.ts (excerpt)
export function verify(
  p: Vector,
  q: Vector,
  x: number,
  u: number,
  rng: () => number,
) {
  if (u < Math.min(1, p[x]! / q[x]!)) return { accepted: true, token: x };
  return { accepted: false, token: sampleFromProbs(residual(p, q), rng) };
}

The tests check the emitted distribution equals pp to 10−1410^{-14}, and run 40,000 Monte Carlo rounds whose acceptance, tokens per pass and token histogram match the formulas.