<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Speculative-Decoding on lo0 — Blog Técnico</title><link>https://blog.lo0.es/tags/speculative-decoding/</link><description>Recent content in Speculative-Decoding on lo0 — Blog Técnico</description><generator>Hugo -- gohugo.io</generator><language>es</language><lastBuildDate>Fri, 29 May 2026 11:00:00 +0200</lastBuildDate><atom:link href="https://blog.lo0.es/tags/speculative-decoding/index.xml" rel="self" type="application/rss+xml"/><item><title>Speculative decoding: el secretario que adelanta lo que va a decir el jefe — fundamentos, matemáticas y estado mayo 2026</title><link>https://blog.lo0.es/posts/speculative-decoding-fundamentos/</link><pubDate>Fri, 29 May 2026 11:00:00 +0200</pubDate><guid>https://blog.lo0.es/posts/speculative-decoding-fundamentos/</guid><description>&lt;blockquote>
&lt;p>Este post complementa los de &lt;a href="https://blog.lo0.es/posts/kv-cache-fundamentos/">KV cache: la memoria de trabajo&lt;/a> y &lt;a href="https://blog.lo0.es/posts/quantization-fundamentos-inferencia/">Quantization para inferencia LLM&lt;/a>. Ambos son palancas de aceleración &lt;strong>dentro de un único forward pass&lt;/strong>. Speculative decoding es la palanca complementaria: en lugar de hacer cada forward pass más barato, intenta &lt;strong>producir más tokens por forward pass&lt;/strong>. Es ortogonal a quantization y al KV cache, y se acumula multiplicativamente con ambos.&lt;/p>
&lt;/blockquote>
&lt;h2 id="estás-aquí-deploy">Estás aquí: DEPLOY&lt;/h2>
&lt;div class="diagram" style="max-width:780px;margin:1rem auto;">
&lt;svg viewBox="0 0 780 90" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="estás aquí: Deploy">
&lt;style>.box{stroke:#444;stroke-width:1.4;rx:6}.active{fill:#7ad88f;stroke-width:3}.idle{fill:#f4f4f4}.lbl{font:600 12px sans-serif;fill:#222}.arr{stroke:#666;stroke-width:1.4;fill:none;marker-end:url(#spm)}.cyc{stroke:#888;stroke-width:1.2;fill:none;stroke-dasharray:4 2;marker-end:url(#spm)}&lt;/style>
&lt;defs>&lt;marker id="spm" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto">&lt;path d="M0,0 L10,5 L0,10 z" fill="#666"/>&lt;/marker>&lt;/defs>
&lt;text x="390" y="20" text-anchor="middle" class="lbl">Estás aquí: DEPLOY · más tokens por forward pass sin tocar calidad&lt;/text>
&lt;rect x="30" y="35" width="110" height="35" class="box idle"/>&lt;text x="85" y="58" text-anchor="middle" class="lbl">1 · Data&lt;/text>
&lt;rect x="155" y="35" width="110" height="35" class="box idle"/>&lt;text x="210" y="58" text-anchor="middle" class="lbl">2 · Tune&lt;/text>
&lt;rect x="280" y="35" width="110" height="35" class="box idle"/>&lt;text x="335" y="58" text-anchor="middle" class="lbl">3 · Eval&lt;/text>
&lt;rect x="405" y="35" width="110" height="35" class="box active"/>&lt;text x="460" y="58" text-anchor="middle" class="lbl">4 · Deploy&lt;/text>
&lt;rect x="530" y="35" width="110" height="35" class="box idle"/>&lt;text x="585" y="58" text-anchor="middle" class="lbl">5 · Observe&lt;/text>
&lt;rect x="655" y="35" width="110" height="35" class="box idle"/>&lt;text x="710" y="58" text-anchor="middle" class="lbl">6 · Retrain&lt;/text>
&lt;path class="arr" d="M140,52 L155,52"/>&lt;path class="arr" d="M265,52 L280,52"/>&lt;path class="arr" d="M390,52 L405,52"/>&lt;path class="arr" d="M515,52 L530,52"/>&lt;path class="arr" d="M640,52 L655,52"/>
&lt;path class="cyc" d="M710,72 L710,82 L85,82 L85,72"/>
&lt;/svg>
&lt;/div>
&lt;h2 id="tldr">TL;DR&lt;/h2>
&lt;p>La inferencia LLM autoregresiva tiene un problema estructural: cada token nuevo necesita un forward pass completo del modelo, y los forward passes de decode están memory-bandwidth-bound, no compute-bound (el detalle está en &lt;a href="https://blog.lo0.es/posts/kv-cache-fundamentos/">KV cache&lt;/a>). La GPU pasa la mayor parte del tiempo esperando a que llegue el siguiente peso desde HBM. &lt;strong>Speculative decoding&lt;/strong> aprovecha ese tiempo muerto haciendo dos cosas en paralelo: un modelo pequeño y barato (&lt;em>draft&lt;/em>) genera γ tokens autoregresivamente —rápido pero impreciso—, y el modelo grande (&lt;em>target&lt;/em>) verifica esos γ tokens &lt;strong>en un único forward pass paralelo&lt;/strong>, casi al mismo coste que un forward pass de un solo token. Una regla de aceptación basada en rejection sampling decide cuántos tokens del draft se aceptan, y la matemática prueba —no aproxima, prueba— que la distribución del output coincide exactamente con muestrear directamente del target. La técnica tiene techo: nunca se generan más de &lt;code>1/(1-α)&lt;/code> tokens por step, con α la tasa de aceptación. Las cinco familias dominantes en mayo de 2026 son &lt;strong>vanilla SD&lt;/strong> (Leviathan 2023), &lt;strong>Medusa&lt;/strong> (cabezas paralelas extra), &lt;strong>EAGLE-1/2/3&lt;/strong> (draft que opera a nivel de hidden states), &lt;strong>MTP&lt;/strong> (multi-token prediction nativo de DeepSeek-V3) y &lt;strong>P-EAGLE&lt;/strong> (draft que produce los γ tokens en un único forward pass, integrado en vLLM 0.16+). En workloads de baja concurrencia con prompts cortos y outputs largos —el caso típico del asistente conversacional on-premise— el speedup real está entre 2× y 4× en hardware moderno.&lt;/p>
&lt;h2 id="la-analogía-el-secretario-que-adelanta-y-el-jefe-que-valida">La analogía: el secretario que adelanta y el jefe que valida&lt;/h2>
&lt;p>Imagina una rueda de prensa muy concurrida con un sistema de transcripción en tiempo real. Hay dos personas trabajando:&lt;/p>
&lt;p>El &lt;strong>secretario&lt;/strong> está sentado frente al teclado. Sabe de qué va el tema, ha leído los briefings y conoce las muletillas del jefe. Cuando el jefe abre la boca, el secretario empieza a teclear inmediatamente lo que cree que va a decir. Es rápido —teclea tres o cuatro palabras antes de que el jefe las pronuncie— pero a veces se equivoca, especialmente cuando el jefe coge un giro inesperado.&lt;/p>
&lt;p>El &lt;strong>jefe&lt;/strong> lee periódicamente la pantalla del secretario. Lee un bloque entero de un golpe —tres o cuatro palabras— y compara mentalmente lo escrito con lo que él habría dicho. Si coincide con su intención, deja el texto y sigue. Si en algún punto el secretario se desvió, corrige justo ahí, y lo que el secretario tecleó después de la divergencia se descarta (porque podría estar mal por culpa del error anterior). Si el bloque entero estaba bien, el jefe aprovecha y añade una palabra más mientras corrige.&lt;/p>
&lt;p>El resultado: el texto final es &lt;strong>idéntico al que habría dictado el jefe a solas&lt;/strong>, pero se produce más rápido porque el secretario adelanta trabajo.&lt;/p>
&lt;p>La analogía se sostiene en cuatro detalles:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>El secretario es el draft model&lt;/strong>. Pequeño, barato, rápido, aproximado.&lt;/li>
&lt;li>&lt;strong>El jefe es el target model&lt;/strong>. Lento, caro, pero es la única autoridad sobre la calidad del output.&lt;/li>
&lt;li>&lt;strong>El bloque que lee el jefe de un golpe es el forward pass paralelo del target&lt;/strong> sobre &lt;code>prompt + γ tokens del draft&lt;/code>. El coste de verificar γ tokens es casi el mismo que el de generar uno (porque el cuello de botella es cargar los pesos desde HBM, no las operaciones).&lt;/li>
&lt;li>&lt;strong>La regla de &amp;ldquo;corrige justo donde divergió&amp;rdquo; es el rejection sampling&lt;/strong> que preserva la distribución del target.&lt;/li>
&lt;/ul>
&lt;p>A partir de aquí entramos al mecanismo y a por qué la calidad &lt;strong>se preserva exactamente&lt;/strong>, no aproximadamente.&lt;/p>
&lt;h2 id="el-mecanismo-desnudo">El mecanismo desnudo&lt;/h2>
&lt;p>Llamemos &lt;code>p&lt;/code> a la distribución del target (lo que produciría si fuese el único en hablar) y &lt;code>q&lt;/code> a la del draft. La iteración de speculative decoding tiene tres pasos:&lt;/p>
&lt;p>&lt;strong>Paso 1 — Draft.&lt;/strong> El draft genera γ tokens autoregresivamente: &lt;code>x_1, x_2, ..., x_γ&lt;/code>. Cada uno por su forward pass propio. Como el draft es pequeño, los γ steps cuestan poco. Típicamente γ ∈ [4, 8].&lt;/p>
&lt;p>&lt;strong>Paso 2 — Verify.&lt;/strong> El target ejecuta &lt;strong>un único forward pass&lt;/strong> sobre la secuencia completa &lt;code>prompt + x_1 ... x_γ&lt;/code>. Por la atención causal, ese forward pass produce simultáneamente las distribuciones &lt;code>p(·|prompt, x_&amp;lt;i)&lt;/code> para cada posición &lt;em>i&lt;/em>. Es decir, en un solo paso obtiene la verificación de los γ tokens y, además, una distribución extra &lt;code>p(·|prompt, x_1...x_γ)&lt;/code> para el siguiente token.&lt;/p>
&lt;p>&lt;strong>Paso 3 — Accept/reject token a token.&lt;/strong> Para cada &lt;code>x_i&lt;/code> de izquierda a derecha aplica:&lt;/p>
&lt;ul>
&lt;li>Si &lt;code>p(x_i) ≥ q(x_i)&lt;/code>: aceptar siempre.&lt;/li>
&lt;li>Si &lt;code>p(x_i) &amp;lt; q(x_i)&lt;/code>: aceptar con probabilidad &lt;code>p(x_i)/q(x_i)&lt;/code>.&lt;/li>
&lt;li>Si rechaza: parar, muestrear un token de reemplazo desde la distribución residual normalizada &lt;code>norm(max(0, p − q))&lt;/code>, y descartar el resto.&lt;/li>
&lt;li>Si llega al final habiendo aceptado los γ tokens: añadir un &lt;strong>token bonus&lt;/strong> muestreado directamente de &lt;code>p(·|prompt, x_1...x_γ)&lt;/code>, que ya está en los logits del forward del target.&lt;/li>
&lt;/ul>
&lt;p>Resultado por iteración: entre 1 y γ+1 tokens nuevos. En el mejor caso (todos aceptados + bonus) se generan γ+1 tokens al coste de &lt;strong>un único forward pass del target más γ forward passes del draft&lt;/strong>, contra γ+1 forward passes del target en la versión sin speculative.&lt;/p>
&lt;div class="diagram" style="max-width:780px;margin:1.5rem auto;">
&lt;svg viewBox="0 0 780 340" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Mecanismo speculative decoding">
&lt;style>
.dbox{fill:#fff4d6;stroke:#a48000;stroke-width:1.4;rx:6}
.tbox{fill:#d4ecff;stroke:#1f5fa8;stroke-width:1.4;rx:6}
.acc{fill:#cdebd0;stroke:#2a7a40;stroke-width:1.4;rx:6}
.rej{fill:#f6caca;stroke:#a52a2a;stroke-width:1.4;rx:6}
.bon{fill:#e6d0ff;stroke:#5a2db0;stroke-width:1.4;rx:6}
.lbl{font:600 12px sans-serif;fill:#222}
.sub{font:400 11px sans-serif;fill:#555}
.arr{stroke:#666;stroke-width:1.4;fill:none;marker-end:url(#spec1)}
&lt;/style>
&lt;defs>&lt;marker id="spec1" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto">&lt;path d="M0,0 L10,5 L0,10 z" fill="#666"/>&lt;/marker>&lt;/defs>
&lt;p>&lt;text x="20" y="25" class="lbl">1. Draft model (q) genera γ=4 tokens autoregresivamente&lt;/text>
&lt;rect x="20" y="35" width="80" height="35" class="dbox"/>&lt;text x="60" y="58" text-anchor="middle" class="lbl">x₁&lt;/text>
&lt;rect x="120" y="35" width="80" height="35" class="dbox"/>&lt;text x="160" y="58" text-anchor="middle" class="lbl">x₂&lt;/text>
&lt;rect x="220" y="35" width="80" height="35" class="dbox"/>&lt;text x="260" y="58" text-anchor="middle" class="lbl">x₃&lt;/text>
&lt;rect x="320" y="35" width="80" height="35" class="dbox"/>&lt;text x="360" y="58" text-anchor="middle" class="lbl">x₄&lt;/text>
&lt;text x="420" y="58" class="sub">4 forward passes baratos&lt;/text>&lt;/p>
&lt;p>&lt;text x="20" y="105" class="lbl">2. Target model (p) verifica los 4 tokens en UN forward pass paralelo&lt;/text>
&lt;rect x="20" y="115" width="380" height="40" class="tbox"/>&lt;text x="210" y="140" text-anchor="middle" class="lbl">forward pass único sobre [prompt, x₁, x₂, x₃, x₄]&lt;/text>
&lt;text x="420" y="140" class="sub">obtiene p(·|prompt, x&amp;lt;i) para i=1..5&lt;/text>&lt;/p>
&lt;p>&lt;text x="20" y="180" class="lbl">3. Rejection sampling izquierda-a-derecha&lt;/text>
&lt;rect x="20" y="190" width="80" height="35" class="acc"/>&lt;text x="60" y="208" text-anchor="middle" class="lbl">x₁ ✓&lt;/text>&lt;text x="60" y="221" text-anchor="middle" class="sub">p≥q&lt;/text>
&lt;rect x="120" y="190" width="80" height="35" class="acc"/>&lt;text x="160" y="208" text-anchor="middle" class="lbl">x₂ ✓&lt;/text>&lt;text x="160" y="221" text-anchor="middle" class="sub">aceptado&lt;/text>
&lt;rect x="220" y="190" width="80" height="35" class="acc"/>&lt;text x="260" y="208" text-anchor="middle" class="lbl">x₃ ✓&lt;/text>&lt;text x="260" y="221" text-anchor="middle" class="sub">aceptado&lt;/text>
&lt;rect x="320" y="190" width="80" height="35" class="rej"/>&lt;text x="360" y="208" text-anchor="middle" class="lbl">x₄ ✗&lt;/text>&lt;text x="360" y="221" text-anchor="middle" class="sub">rechazado&lt;/text>&lt;/p>
&lt;p>&lt;text x="20" y="255" class="lbl">4. Resultado de esta iteración&lt;/text>
&lt;rect x="20" y="265" width="80" height="35" class="acc"/>&lt;text x="60" y="288" text-anchor="middle" class="lbl">x₁&lt;/text>
&lt;rect x="120" y="265" width="80" height="35" class="acc"/>&lt;text x="160" y="288" text-anchor="middle" class="lbl">x₂&lt;/text>
&lt;rect x="220" y="265" width="80" height="35" class="acc"/>&lt;text x="260" y="288" text-anchor="middle" class="lbl">x₃&lt;/text>
&lt;rect x="320" y="265" width="80" height="35" class="rej"/>&lt;text x="360" y="288" text-anchor="middle" class="lbl">x&amp;rsquo;₄&lt;/text>
&lt;text x="420" y="280" class="sub">x&amp;rsquo;₄ = muestra de norm(max(0, p−q)) en pos 4&lt;/text>
&lt;text x="420" y="295" class="sub">se descarta el bonus (sólo si todos aceptados)&lt;/text>&lt;/p>
&lt;p>&lt;text x="20" y="325" class="sub">→ 4 tokens nuevos en una sola iteración del target. Sin speculative serían 4 iteraciones.&lt;/text>
&lt;/svg>&lt;/p>
&lt;/div>
&lt;h2 id="por-qué-la-calidad-no-se-degrada-la-prueba">Por qué la calidad no se degrada (la prueba)&lt;/h2>
&lt;p>Esta es la parte de la técnica que más confunde la primera vez. Parece magia: ¿cómo es posible que muestrear de un draft cualquiera y luego &amp;ldquo;validar&amp;rdquo; produzca exactamente la misma distribución que muestrear del target original?&lt;/p>
&lt;p>La prueba cabe en dos líneas. Para cualquier token &lt;code>x&lt;/code>, la probabilidad final de emitirlo es la suma de dos eventos disjuntos: que el draft lo proponga y se acepte, o que el draft proponga otra cosa, se rechace, y al muestrear del residual salga &lt;code>x&lt;/code>. Formalmente:&lt;/p>
&lt;p>$$P(\text{emit } x) = q(x) \cdot \min!\left(1, \frac{p(x)}{q(x)}\right) + P(\text{reject}) \cdot \frac{\max(0, p(x)-q(x))}{\sum_y \max(0, p(y)-q(y))}$$&lt;/p>
&lt;p>El primer término es &lt;code>min(p(x), q(x))&lt;/code>. El segundo es &lt;code>max(0, p(x) − q(x))&lt;/code> (la masa total rechazada &lt;code>Σ max(0, p−q)&lt;/code> cancela exactamente el denominador). Sumando: &lt;code>min(p(x), q(x)) + max(0, p(x) − q(x)) = p(x)&lt;/code>. &lt;strong>El output es estadísticamente indistinguible de muestrear directamente del target&lt;/strong>, hasta diferencias numéricas de coma flotante.&lt;/p>
&lt;p>Esto es importante operacionalmente: significa que speculative decoding &lt;strong>no es una compresión perceptual&lt;/strong>, no es un trade-off de calidad-velocidad, no requiere validación adicional con evals. Si el target hubiese pasado un eval determinado, el sistema con speculative también lo pasa.&lt;/p>
&lt;h2 id="la-matemática-que-importa-techo-y-speedup">La matemática que importa: techo y speedup&lt;/h2>
&lt;p>Llamemos α a la &lt;strong>tasa de aceptación&lt;/strong>: la probabilidad esperada de que un token draft individual sea aceptado. Asumiendo que las aceptaciones de tokens consecutivos son independientes (aproximación razonable en práctica), el número esperado de tokens generados por iteración es:&lt;/p>
&lt;p>$$E[\text{tokens por step}] = \sum_{k=0}^{\gamma} \alpha^k + \alpha^{\gamma+1} = \frac{1 - \alpha^{\gamma+1}}{1 - \alpha}$$&lt;/p>
&lt;p>Y el &lt;strong>speedup teórico&lt;/strong> respecto a generar un token por iteración del target —con &lt;code>c = T_draft / T_target&lt;/code> el coste relativo del draft— es:&lt;/p>
&lt;p>$$\text{Speedup} = \frac{1 - \alpha^{\gamma+1}}{(1 - \alpha)(\gamma c + 1)}$$&lt;/p>
&lt;p>Hay un techo que muchas implementaciones intentan superar inútilmente: cuando &lt;code>γ → ∞&lt;/code>, el speedup converge a:&lt;/p>
&lt;p>$$\lim_{\gamma \to \infty} E[\text{tokens por step}] = \frac{1}{1 - \alpha}$$&lt;/p>
&lt;p>Es un &lt;strong>techo algorítmico, no de hardware&lt;/strong>. Con α = 0.7 nunca se generan más de 3.33 tokens por iteración por más que el hardware mejore. Con α = 0.8 → 5. Con α = 0.9 → 10. Por eso EAGLE-3, que apunta a α &amp;gt; 0.8 en muchos benchmarks, no es una mejora incremental sobre vanilla SD (α ≈ 0.5-0.6): &lt;strong>es un cambio de régimen&lt;/strong> porque sube el techo, no se limita a acercarse a él.&lt;/p>
&lt;p>Ejemplo numérico concreto, Llama 3 70B target con un draft que da α = 0.75, γ = 5, c = 0.1:&lt;/p>
&lt;ul>
&lt;li>Tokens esperados por step: &lt;code>(1 − 0.75⁶) / (1 − 0.75) = (1 − 0.178) / 0.25 = 3.29&lt;/code>&lt;/li>
&lt;li>Speedup: &lt;code>3.29 / (5 × 0.1 + 1) = 3.29 / 1.5 = 2.19×&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>Si subimos α a 0.85 con la misma configuración: tokens por step = &lt;code>(1 − 0.85⁶) / 0.15 = (1 − 0.377) / 0.15 = 4.16&lt;/code>, speedup = &lt;code>4.16 / 1.5 = 2.77×&lt;/code>. Un cambio de α = 0.75 → 0.85 (10 puntos absolutos) sube el speedup un 27 %. Por eso la investigación de los últimos dos años se centra en empujar α: ahí está el premio.&lt;/p>
&lt;h2 id="las-cinco-familias-modernas-mayo-2026">Las cinco familias modernas (mayo 2026)&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Familia&lt;/th>
&lt;th>Año&lt;/th>
&lt;th>Idea central&lt;/th>
&lt;th>Tamaño draft&lt;/th>
&lt;th>α / τ típico&lt;/th>
&lt;th>Speedup&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>Vanilla SD&lt;/strong>&lt;/td>
&lt;td>2023&lt;/td>
&lt;td>Pareja target + draft same-family, rejection sampling&lt;/td>
&lt;td>1/10 – 1/100 target&lt;/td>
&lt;td>0.5 – 0.8&lt;/td>
&lt;td>2-3×&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Medusa&lt;/strong>&lt;/td>
&lt;td>2024&lt;/td>
&lt;td>N cabezas extra en paralelo predicen t+1, t+2, &amp;hellip;; tree attention verifica varios candidatos&lt;/td>
&lt;td>sin draft, +1-2 % params&lt;/td>
&lt;td>top-5 &amp;gt; 0.8&lt;/td>
&lt;td>2.18-2.83×&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>EAGLE-1/2/3&lt;/strong>&lt;/td>
&lt;td>2024-25&lt;/td>
&lt;td>Draft autoregresivo a nivel de &lt;strong>features (hidden states)&lt;/strong>, no de tokens. Reusa embedding del target&lt;/td>
&lt;td>1 bloque transformer (~0.5-2 % params)&lt;/td>
&lt;td>EAGLE-3: α &amp;gt; 0.8, τ hasta 7.5&lt;/td>
&lt;td>hasta 6.5× peak&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>MTP&lt;/strong> (DeepSeek-V3)&lt;/td>
&lt;td>2024&lt;/td>
&lt;td>Cabezas multi-token entrenadas desde cero como parte del modelo; en inferencia hacen de draft &amp;ldquo;gratis&amp;rdquo;&lt;/td>
&lt;td>14B params en V3 671B&lt;/td>
&lt;td>α &amp;gt; 0.8 (MTP1)&lt;/td>
&lt;td>1.5-1.8×&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>P-EAGLE&lt;/strong>&lt;/td>
&lt;td>2026&lt;/td>
&lt;td>EAGLE pero produciendo los γ drafts en &lt;strong>un único forward pass&lt;/strong> (paralelo, no autoregresivo)&lt;/td>
&lt;td>igual que EAGLE&lt;/td>
&lt;td>+30 % sobre EAGLE-3&lt;/td>
&lt;td>4-5× vs AR&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Tres observaciones operacionales:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>EAGLE domina en producción&lt;/strong> porque su overhead es mínimo (un bloque transformer, ~1 % del target en parámetros) y su α es alto. El draft no necesita su propio KV cache &amp;ldquo;completo&amp;rdquo; porque comparte features del target.&lt;/li>
&lt;li>&lt;strong>Medusa fue importante históricamente&lt;/strong> —demostró que se podía hacer speculative sin draft separado—, pero EAGLE lo superó en todos los benchmarks publicados durante 2024-2025.&lt;/li>
&lt;li>&lt;strong>MTP es especial&lt;/strong>: no es algo que añadas a un modelo existente. Es algo que el modelo entrenó nativamente. Si compras DeepSeek-V3, MTP viene gratis y da un ~1.8× sin tocar nada. Si compras Llama 3, no hay MTP que valga; usa EAGLE-3.&lt;/li>
&lt;/ol>
&lt;p>Hay además dos técnicas relacionadas que merecen mención breve, ambas &lt;strong>sin draft model&lt;/strong>: &lt;strong>Lookahead decoding&lt;/strong> (Fu et al. 2024), que formula decoding como iteración de Jacobi y extrae n-gramas de la trayectoria; y &lt;strong>REST&lt;/strong> (He et al. 2024), que mantiene un datastore de n-gramas y propone drafts por longest-prefix match contra los últimos tokens generados. Ambas dan 1.5-2× sin VRAM extra. Útiles cuando no tienes draft model entrenado y no quieres mantener uno.&lt;/p>
&lt;h2 id="implementaciones-reales-en-mayo-2026">Implementaciones reales en mayo 2026&lt;/h2>
&lt;ul>
&lt;li>&lt;strong>vLLM v0.16+&lt;/strong>: soporta unificadamente EAGLE-1/2/3, P-EAGLE, Medusa, MTP nativo (DeepSeek-V3 y variantes), n-gram/suffix decoding sin draft, draft model arbitrario y MLP speculators. El flag canónico es &lt;code>--speculative-config '{&amp;quot;method&amp;quot;:&amp;quot;eagle3&amp;quot;, &amp;quot;model&amp;quot;:&amp;quot;...&amp;quot;, &amp;quot;num_speculative_tokens&amp;quot;: 5}'&lt;/code>.&lt;/li>
&lt;li>&lt;strong>SGLang&lt;/strong>: soporte EAGLE-3 nativo con &lt;code>--speculative-algorithm EAGLE3&lt;/code>. Para DeepSeek-V3 usa MTP vía adaptador EAGLE. Tiene framework propio de entrenamiento de drafts (SpecForge).&lt;/li>
&lt;li>&lt;strong>TensorRT-LLM&lt;/strong>: Medusa, EAGLE (variante simplificada sin árbol), ReDrafter, Lookahead. Reportan ~2.2× con EAGLE.&lt;/li>
&lt;li>&lt;strong>llama.cpp&lt;/strong>: speculative básico solo con draft model (&lt;code>--model-draft&lt;/code>). Sin EAGLE/Medusa/MTP nativos hasta donde he verificado. Speedup típico 1.5-2.5× single-user.&lt;/li>
&lt;/ul>
&lt;h2 id="cuándo-speculative-no-ayuda">Cuándo speculative NO ayuda&lt;/h2>
&lt;p>La técnica tiene tres puntos ciegos importantes:&lt;/p>
&lt;p>&lt;strong>Batch grande.&lt;/strong> El GPU pasa de memory-bound (decode con concurrencia baja) a compute-bound (decode con concurrencia alta). En régimen compute-bound los forward passes &amp;ldquo;casi gratis&amp;rdquo; del target dejan de serlo: γ tokens pasan a costar γ veces más en lugar de casi 1. El cruce típico está en &lt;strong>batch 16-32 para modelos densos&lt;/strong>; para MoE con pocos parámetros activos por token el cruce ocurre más tarde. En picos de carga, speculative puede empeorar el throughput agregado por GPU.&lt;/p>
&lt;p>&lt;strong>Prefill / TTFT.&lt;/strong> Speculative decoding &lt;strong>no toca el prefill&lt;/strong>. La fase de procesar el prompt completo sigue siendo idéntica. El TTFT no mejora y puede empeorar marginalmente por el setup del draft. Si el SLA es TTFT-bound (asistentes con outputs cortos, búsqueda, RAG con respuestas breves), no es la herramienta correcta.&lt;/p>
&lt;p>&lt;strong>Outputs cortos.&lt;/strong> Si el modelo genera 10-20 tokens y se acaba la respuesta, el overhead fijo del setup no se amortiza. Speculative brilla con outputs largos (300+ tokens): asistentes generativos, code completion extenso, redacción.&lt;/p>
&lt;h2 id="pitfalls-operacionales">Pitfalls operacionales&lt;/h2>
&lt;p>&lt;strong>VRAM extra del draft.&lt;/strong> En vanilla SD, cargar un draft completo más su KV cache cuesta caro. Llama 3 8B en BF16 como draft de un 70B son ~16 GB de pesos más KV cache. En un H100 80 GB con el target ya casi lleno, eso puede forzar a reducir el KV cache del target y bajar la concurrencia máxima. EAGLE resuelve este problema: el draft es un bloque transformer (~1 GB para un 70B) y reusa features del target.&lt;/p>
&lt;p>&lt;strong>Quantization del draft.&lt;/strong> Cuantizar el draft a INT4 con GPTQ degrada α sustancialmente (los errores en logits se acumulan en la comparación contra &lt;code>p&lt;/code>). AWQ aguanta mejor pero también baja α. &lt;strong>Práctica común en 2026: target en FP8 o INT4, draft en FP16/BF16&lt;/strong>. El draft es lo suficientemente pequeño como para que el ahorro de VRAM por cuantizarlo no compense la caída de α y, por tanto, de speedup. El detalle de cada formato está en &lt;a href="https://blog.lo0.es/posts/quantization-fundamentos-inferencia/">Quantization&lt;/a>.&lt;/p>
&lt;p>&lt;strong>Interacción con continuous batching.&lt;/strong> No lo rompe, pero crea &lt;em>nested raggedness&lt;/em>: cada request del batch puede aceptar un número distinto de tokens en cada iteración. PagedAttention de vLLM lo absorbe, pero el planificador pierde eficiencia. A QPS bajo (asistente conversacional, baja concurrencia simultánea) la combinación es excelente. A QPS alto, hay tensión real y trabajos como &lt;em>Goodput-optimized speculative decoding&lt;/em> (Liu et al., 2024) optimizan γ dinámicamente según el estado del batch.&lt;/p>
&lt;p>&lt;strong>Sampling temperature.&lt;/strong> α cae con temperaturas altas. A T = 1.0 con outputs creativos (redacción libre), α puede bajar 10-15 puntos respecto a T = 0 con la misma pareja modelo-draft. El speedup escala consecuentemente.&lt;/p>
&lt;h2 id="implicaciones-en-hardware-on-premise">Implicaciones en hardware on-premise&lt;/h2>
&lt;h3 id="en-una-rtx-4090-24-gb-ada-lovelace">En una RTX 4090 (24 GB, Ada Lovelace)&lt;/h3>
&lt;p>El uso clásico es &lt;strong>vanilla SD con dos modelos cuantizados&lt;/strong>: por ejemplo, Llama 3 70B AWQ-INT4 como target (~35 GB → necesita TP=2 sobre dos 4090) y Llama 3 8B AWQ-INT4 como draft. En la práctica, el caso single-card más realista es &lt;strong>Llama 3 8B target + un modelo de 1B como draft&lt;/strong> o &lt;strong>Qwen 3 14B target + Qwen 3 0.5B como draft&lt;/strong>: caben holgadamente y dan 1.8-2.5× con tareas conversacionales. Para EAGLE en consumer, los drafts oficiales para familias populares (Llama 3, Qwen 3) están publicados en Hugging Face y ocupan 0.5-2 GB extra.&lt;/p>
&lt;h3 id="en-un-cluster-genérico-4h100-sxm-320-gb-nvlink-fp8-nativo">En un cluster genérico 4×H100 SXM (320 GB, NVLink, FP8 nativo)&lt;/h3>
&lt;p>Aquí EAGLE-3 brilla:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Llama 3 70B FP8 + EAGLE-3 draft FP16&lt;/strong>: el draft ocupa ~0.7 GB; el target ~70 GB con TP=2 (35 GB por GPU). El speedup observado en benchmarks reproducibles está entre 2.5× y 4× a batch 1-4, cayendo a casi break-even a batch 32.&lt;/li>
&lt;li>&lt;strong>DeepSeek-V3 671B FP8 + MTP nativo&lt;/strong>: el modelo viene con MTP entrenado; no hay nada que añadir. El speedup es 1.5-1.8× con cero VRAM extra. Es la opción más eficiente operacionalmente: cero piezas adicionales.&lt;/li>
&lt;li>&lt;strong>Combinar con disaggregated serving&lt;/strong>: como detalla &lt;a href="https://blog.lo0.es/posts/disaggregated-serving-prefill-decode/">Disaggregated serving&lt;/a>, prefill y decode pueden vivir en pods distintos. Speculative se aplica &lt;strong>solo en los pods de decode&lt;/strong>, lo cual encaja perfectamente con la separación (prefill es compute-bound y no se beneficiaría).&lt;/li>
&lt;/ul>
&lt;p>La regla de pulgar en cluster H100 mayo 2026: &lt;strong>si el modelo es DeepSeek-V3 / V4 → MTP nativo, sin más; si es Llama 3 / Qwen 3 → EAGLE-3 con draft oficial; si es exótico → vanilla SD con un draft de la misma familia&lt;/strong>.&lt;/p>
&lt;h2 id="lo-que-no-hemos-cubierto">Lo que no hemos cubierto&lt;/h2>
&lt;ul>
&lt;li>&lt;strong>Entrenamiento de drafts EAGLE custom&lt;/strong> con SpecForge: cómo recolectar trayectorias del target y entrenar el draft on-policy.&lt;/li>
&lt;li>&lt;strong>Speculative Prefill&lt;/strong> (arXiv:2502.02789): variante para acelerar TTFT, mecanismo distinto del decode aquí descrito.&lt;/li>
&lt;li>&lt;strong>Tree attention en detalle&lt;/strong>: cómo Medusa y EAGLE-2 verifican varios candidatos a la vez con máscaras de atención específicas.&lt;/li>
&lt;li>&lt;strong>MoE + speculative&lt;/strong>: la combinación tiene interacciones no triviales con el router de expertos. Los activated params bajos hacen que el régimen memory-bound se mantenga incluso a batch alto, lo que cambia las reglas.&lt;/li>
&lt;/ul>
&lt;h2 id="ver-también">Ver también&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://blog.lo0.es/posts/kv-cache-fundamentos/">KV cache: la memoria de trabajo que sostiene la inferencia LLM&lt;/a> — speculative no existiría sin el fenómeno memory-bound del decode, que es consecuencia directa del KV cache; este post da el marco.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/pagedattention-deep-dive/">PagedAttention deep dive&lt;/a> — el planificador de vLLM tiene que gestionar el nested raggedness de los γ tokens aceptados por request; PagedAttention es lo que lo hace posible sin reservar bloques fijos por sesión.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/quantization-fundamentos-inferencia/">Quantization para inferencia LLM&lt;/a> — palanca ortogonal y multiplicativa con speculative; este post explica por qué el draft suele dejarse en BF16 aunque el target esté en FP8/INT4.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/disaggregated-serving-prefill-decode/">Disaggregated serving: prefill y decode en pods especializados&lt;/a> — speculative se aplica solo en decode; disaggregated facilita esa especialización sin tocar el prefill.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/pipeline-llmops-seis-etapas/">El pipeline LLMOps de seis etapas&lt;/a> — el mapa maestro donde Deploy es la etapa 4.&lt;/li>
&lt;/ul>
&lt;h2 id="referencias">Referencias&lt;/h2>
&lt;ul>
&lt;li>Leviathan, Y., Kalman, M., Matias, Y. &lt;em>Fast Inference from Transformers via Speculative Decoding&lt;/em>. ICML 2023. &lt;a href="https://arxiv.org/abs/2211.17192">https://arxiv.org/abs/2211.17192&lt;/a>&lt;/li>
&lt;li>Chen, C., Borgeaud, S., Irving, G., Lespiau, J.B., Sifre, L., Jumper, J. &lt;em>Accelerating Large Language Model Decoding with Speculative Sampling&lt;/em>. DeepMind 2023. &lt;a href="https://arxiv.org/abs/2302.01318">https://arxiv.org/abs/2302.01318&lt;/a>&lt;/li>
&lt;li>Cai, T., Li, Y., Geng, Z., Peng, H., Lee, J.D., Chen, D., Dao, T. &lt;em>Medusa: Simple LLM Inference Acceleration with Multiple Decoding Heads&lt;/em>. ICML 2024. &lt;a href="https://arxiv.org/abs/2401.10774">https://arxiv.org/abs/2401.10774&lt;/a>&lt;/li>
&lt;li>Li, Y., Wei, F., Zhang, C., Zhang, H. &lt;em>EAGLE: Speculative Sampling Requires Rethinking Feature Uncertainty&lt;/em>. ICML 2024. &lt;a href="https://arxiv.org/abs/2401.15077">https://arxiv.org/abs/2401.15077&lt;/a>&lt;/li>
&lt;li>Li, Y., Wei, F., Zhang, C., Zhang, H. &lt;em>EAGLE-2: Faster Inference of Language Models with Dynamic Draft Trees&lt;/em>. EMNLP 2024. &lt;a href="https://arxiv.org/abs/2406.16858">https://arxiv.org/abs/2406.16858&lt;/a>&lt;/li>
&lt;li>Li, Y., Wei, F., Zhang, C., Zhang, H. &lt;em>EAGLE-3: Scaling up Inference Acceleration of Large Language Models via Training-Time Test&lt;/em>. NeurIPS 2025. &lt;a href="https://arxiv.org/abs/2503.01840">https://arxiv.org/abs/2503.01840&lt;/a>&lt;/li>
&lt;li>DeepSeek-AI. &lt;em>DeepSeek-V3 Technical Report&lt;/em> — Multi-Token Prediction. &lt;a href="https://arxiv.org/abs/2412.19437">https://arxiv.org/abs/2412.19437&lt;/a>&lt;/li>
&lt;li>Fu, Y., Bailis, P., Stoica, I., Zhang, H. &lt;em>Break the Sequential Dependency of LLM Inference Using Lookahead Decoding&lt;/em>. ICML 2024. &lt;a href="https://arxiv.org/abs/2402.02057">https://arxiv.org/abs/2402.02057&lt;/a>&lt;/li>
&lt;li>He, Z., Zhong, Z., Cai, T., Lee, J.D., He, D. &lt;em>REST: Retrieval-Based Speculative Decoding&lt;/em>. NAACL 2024. &lt;a href="https://arxiv.org/abs/2311.08252">https://arxiv.org/abs/2311.08252&lt;/a>&lt;/li>
&lt;li>vLLM speculative decoding docs: &lt;a href="https://docs.vllm.ai/en/latest/features/speculative_decoding/">https://docs.vllm.ai/en/latest/features/speculative_decoding/&lt;/a>&lt;/li>
&lt;li>SGLang speculative decoding docs: &lt;a href="https://docs.sglang.ai/advanced_features/speculative_decoding.html">https://docs.sglang.ai/advanced_features/speculative_decoding.html&lt;/a>&lt;/li>
&lt;li>TensorRT-LLM speculative sampling: &lt;a href="https://nvidia.github.io/TensorRT-LLM/advanced/speculative-decoding.html">https://nvidia.github.io/TensorRT-LLM/advanced/speculative-decoding.html&lt;/a>&lt;/li>
&lt;li>vLLM blog &lt;em>Speculative Decoding in vLLM&lt;/em> (oct 2024): &lt;a href="https://blog.vllm.ai/2024/10/17/spec-decode.html">https://blog.vllm.ai/2024/10/17/spec-decode.html&lt;/a>&lt;/li>
&lt;li>vLLM blog &lt;em>P-EAGLE: Parallel Speculative Decoding in vLLM&lt;/em> (mar 2026): &lt;a href="https://vllm.ai/blog/2026-03-13-p-eagle">https://vllm.ai/blog/2026-03-13-p-eagle&lt;/a>&lt;/li>
&lt;li>Repo oficial EAGLE: &lt;a href="https://github.com/SafeAILab/EAGLE">https://github.com/SafeAILab/EAGLE&lt;/a>&lt;/li>
&lt;li>Repo oficial Medusa: &lt;a href="https://github.com/FasterDecoding/Medusa">https://github.com/FasterDecoding/Medusa&lt;/a>&lt;/li>
&lt;/ul></description></item></channel></rss>