Speculative decoding : mon LLM local accéléré de 2,4x
Ma station locale avec 2× RTX 4090 (48 Go VRAM cumulés) faisait tourner Llama 3.1 70B quantifié en AWQ à 18 tokens/seconde. Acceptable pour du code, lent pour du chat. J'ai activé le speculative decoding sur cette même stack : 44 tokens/seconde. Même modèle, même GPU, même prompt. Facteur ×2,4 gratuit. Voici comment ça marche mécaniquement, comment l'activer en 20 lignes, et les 3 cas où ça ne donne rien.
Le principe : deviner à plusieurs, valider à un seul
Soutenez mon travail sur Patreon
Accès anticipé aux articles, contenu exclusif, et la satisfaction de soutenir un auteur indépendant.
Rejoindre — à partir de 3€/moisLe speculative decoding repose sur une asymétrie simple : générer un token coûte cher (un forward pass complet du modèle), mais valider si un token est correct est presque gratuit (on compare les probabilités). L'idée : utiliser un petit modèle rapide pour générer 4-8 tokens à l'avance, puis utiliser le grand modèle pour valider tous ces tokens en un seul forward pass. Si les tokens devinés sont bons, on les accepte tous d'un coup. Si l'un est rejeté, on corrige à cet endroit et on continue.
Le gain théorique : au lieu de faire N forward passes du gros modèle (coûteux), on fait N/K forward passes (où K = taux d'acceptation). Sur des prompts de code, K tourne autour de 3-5 — soit un speedup de 3× à 5×.
Le setup exact avec vLLM
Je suis passé à vLLM parce que c'est l'implémentation de référence en 2026. Installation :
pip install vllm==0.8.3Puis le serveur avec speculative decoding activé :
from vllm import LLM, SamplingParams
llm = LLM(
model="meta-llama/Llama-3.1-70B-Instruct-AWQ-INT4",
speculative_model="meta-llama/Llama-3.1-8B-Instruct",
num_speculative_tokens=5,
tensor_parallel_size=2, # 2× RTX 4090
quantization="awq",
max_model_len=8192,
)
sampling = SamplingParams(temperature=0.7, max_tokens=500)
output = llm.generate(["Écris un test Jest pour cette fonction..."], sampling)Le modèle « draft » (Llama 8B) devine les 5 prochains tokens. Le modèle target (Llama 70B) valide en bloc. Le reste est géré automatiquement par vLLM.
Les benchmarks sur 5 types de prompts
J'ai testé sur 5 types de prompts, chacun avec 20 runs, mesurant les tokens/seconde en output (pas de prefill inclus) :
| Type de prompt | Sans spec. | Avec spec. | Gain | Taux acceptation |
|---|---|---|---|---|
| Code Python (génération) | 18,2 t/s | 51,4 t/s | ×2,82 | 0,71 |
| Code TypeScript | 17,8 t/s | 48,1 t/s | ×2,70 | 0,68 |
| Résumé texte | 19,1 t/s | 35,6 t/s | ×1,86 | 0,47 |
| Dialogue créatif | 19,4 t/s | 26,9 t/s | ×1,39 | 0,31 |
| Traduction FR→EN | 18,7 t/s | 40,2 t/s | ×2,15 | 0,55 |
| Moyenne | 18,6 | 40,4 | ×2,17 | 0,54 |
Le code est la killer app : ×2,7 à ×2,8 de speedup parce que le draft model (8B) devine très bien la syntaxe et les mots-clés attendus. Le créatif est décevant — la génération est trop imprévisible, le draft se trompe souvent, et la correction coûte presque autant qu'un décodage classique.
Pourquoi le taux d'acceptation est le nerf de la guerre
Le taux d'acceptation mesure le pourcentage de tokens devinés par le draft qui sont acceptés par le target. Plus il est élevé, plus le speedup est important.
Règle empirique : si ton taux d'acceptation est sous 0,40, le speculative decoding te ralentit plus qu'il ne t'accélère (parce que le draft model consomme du temps pour rien). Si c'est entre 0,40 et 0,60, tu as un gain modéré. Au-delà de 0,60, c'est une killer feature.
Comment augmenter le taux d'acceptation
Trois leviers :
- 1.Un draft model bien aligné avec le target. Llama 8B devine bien pour Llama 70B parce qu'ils partagent la même distribution. Mixer un Qwen 1,5B avec un Llama 70B donne un taux de 0,28 — inutile.
- 2.Un
num_speculative_tokensadapté. Plus c'est haut, plus on essaie de deviner loin, plus c'est risqué. 5 est un bon défaut. Au-delà de 7, le taux chute. - 3.Une température basse. À
temperature=0, le draft est déterministe et matche mieux. Àtemperature=0.9, la variance explose et le taux chute de 15-20 points.
Les 3 cas où ça ne vaut rien
Cas 1 : tu n'as qu'un seul GPU
Speculative decoding nécessite d'avoir les deux modèles en VRAM simultanément (ou d'accepter de faire du CPU offload pour l'un). Sur un seul GPU 24 Go, loger un Llama 70B AWQ + un Llama 8B FP16 est impossible. Il faut au moins 2 GPUs, ou accepter le offload qui annule le gain.
Cas 2 : workloads de chat avec température élevée
Si ton assistant est configuré à temperature=0.9 (pour avoir de la variété), le speculative ne t'apportera pas grand-chose. Reste à température basse pour les tâches où c'est acceptable (code, résumé, extraction).
Cas 3 : modèles très petits
Sur un Llama 8B seul, activer le speculative avec un draft 1B ne donne presque rien (-5% à +10%) parce que l'overhead d'orchestrer les deux modèles dépasse le gain. Le speculative décoding brille avec target ≥ 30B, draft ≤ 10% de la taille du target.
Ce qu'il faut retenir
- 1.Speculative decoding = un petit modèle devine, un gros valide en bloc. Gain typique : ×2 à ×3 en latence.
- 2.vLLM le supporte nativement en 20 lignes de code.
- 3.Le code gagne le plus (×2,7) grâce à sa prédictibilité syntaxique.
- 4.Le créatif gagne peu (×1,4) à cause de la variance élevée.
- 5.Le taux d'acceptation doit être ≥ 0,40 pour que ça vaille le coup.
Pour aller plus loin sur les mécanismes d'inférence des LLM, la quantification, et toutes les techniques d'accélération qui permettent de faire tourner des modèles géants sur du matériel grand public, j'ai écrit un livre complet :
Soutenez mon travail sur Patreon
Accès anticipé aux articles, contenu exclusif, et la satisfaction de soutenir un auteur indépendant.
Rejoindre — à partir de 3€/mois