spec-verify / CARD.md
phanerozoic's picture
Card: standardized form with hero
0961b71 verified
|
Raw
History Blame
4.99 kB
---
library_name: kernels
license: apache-2.0
---
# spec-verify
Speculative-decoding verification ops for CPUs, aarch64 first, loadable
through `kernels`. The reference standard is the analytic law of rejection
sampling, matched to the sampling floor. Companions:
[bitnet-cpu](https://huggingface.co/kernels/phanerozoic/bitnet-cpu),
[quant-matmul](https://huggingface.co/kernels/phanerozoic/quant-matmul),
[gguf-gemv](https://huggingface.co/kernels/phanerozoic/gguf-gemv)
(linears),
[cpu-attn](https://huggingface.co/kernels/phanerozoic/cpu-attn),
[decode-ops](https://huggingface.co/kernels/phanerozoic/decode-ops).
On a small board the decode wall is memory: every token re-reads every
weight, so one token costs the same bandwidth as many. Speculative decoding
converts that into a win by having a draft model propose K tokens and the
target verify them in a single `[K+1, V]` forward, which on the companion
linear kernels is several times cheaper per token than K separate decodes.
These ops are the verification step: an accepted prefix plus one correction
token, distribution-preserving, at a cost far below the forwards they gate.
![The accepted-length histogram over 600 rounds converging on the analytic law](https://huggingface.co/kernels/phanerozoic/spec-verify/resolve/main/media/hero.gif)
*600 verification rounds at K = 4 measured on a Raspberry Pi with a
deliberately imperfect draft: the accepted-length histogram gives a mean of
1.84 against the analytic law's 1.86, at 33 us greedy and 117 us sampled per
round on a Pi 5 (126 and 657 us on a Pi 4), far below the model forwards
they gate.*
## Usage
```python
import torch
from kernels import get_kernel
sv = get_kernel("phanerozoic/spec-verify", version=1, trust_remote_code=True)
# Greedy: target_probs [K+1, V] (only the argmax is read), draft_tokens [K].
n_accepted, next_token = sv.verify_greedy(target_probs, draft_tokens)
# Keep draft_tokens[:n_accepted], append next_token.
# Or drive the whole draft/verify loop:
dec = sv.SpeculativeDecoder(draft_step, target_step, k=4, greedy=True)
out = dec.generate(prompt_ids, max_new=128)
```
`version` selects the release branch; `trust_remote_code` is required by
`kernels` for publishers without the trusted-publisher mark.
## API
| Symbol | Purpose |
|---|---|
| `verify_greedy(target_probs, draft_tokens)` | temperature-0 verify -> `(n_accepted, next_token)` |
| `accept(target_probs, draft_probs, draft_tokens, generator)` | speculative sampling -> `(n_accepted, next_token)` |
| `SpeculativeDecoder(draft_step, target_step, k, greedy, generator)` | draft/verify orchestration loop |
`draft_step(prefix, k) -> (tokens, probs)` and
`target_step(prefix, proposed) -> target_probs [k+1, V]` are user callbacks,
so any pair of models (including GGUF drafts and verifiers via the companion
kernels) composes.
## Method
Two verification modes:
- Greedy (temperature 0): accept draft token `t_i` while it equals
`argmax(target_i)`; at the first miss emit `argmax(target_i)` and stop;
all K matched emit `argmax(target_K)`. Deterministic, and the emitted
stream is identical to plain greedy target decoding.
- Speculative sampling (Leviathan et al. / Chen et al.): accept `t_i` with
probability `min(1, p_i(t_i) / q_i(t_i))`; on rejection resample from
`norm(max(0, p_i - q_i))`; all accepted samples the bonus from `p_K`.
Distribution-preserving when target and draft distributions are warped
identically (the caller's contract). Randomness enters only through
explicit uniform draws, so a fixed generator gives a fixed result.
## Measured
Raspberry Pi 5 (4x Cortex-A76 2.4 GHz) and Pi 4 Model B (4x Cortex-A72
1.8 GHz), torch 2.13 CPU, V = 32000, K = 4 (and V = 128256, K = 5 for the
published rows):
| op | Pi 5 | Pi 4 |
|---|---|---|
| `verify_greedy` | 33 us | 126 us |
| `accept` | 117 us | 657 us |
| `verify_greedy` (V=128256, K=5) | 59 us | 140 us |
| `accept` (V=128256, K=5) | 338 us | 754 us |
Over 600 rounds with a mismatched draft, the mean accepted length is 1.84
against the analytic `sum_i acceptance^i` of 1.86, where the per-token
acceptance probability `sum(min(p, q))` is 0.678.
## Correctness
The accepted-length distribution and its mean track the analytic law of
rejection sampling to the sampling floor at 600 rounds. Probabilities need
not be normalized; masses are summed exactly. Greedy verification is
deterministic and reproduces plain greedy target decoding exactly.
## Requirements and limits
- `target_probs` `[K+1, V]` f32; for `accept`, `draft_probs` `[K, V]`
warped identically to the target.
- x86-64 runs a scalar path; the O(V) reductions vectorize on aarch64.
- Distribution preservation depends on the caller warping both models'
distributions identically.
## References
Leviathan, Kalman, Matias, "Fast Inference from Transformers via
Speculative Decoding" (2023); Chen et al., "Accelerating Large Language
Model Decoding with Speculative Sampling" (2023).
## License
Apache-2.0.