<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Self-Speculative-Decoding on lo0 — Blog Técnico</title><link>https://blog.lo0.es/tags/self-speculative-decoding/</link><description>Recent content in Self-Speculative-Decoding on lo0 — Blog Técnico</description><generator>Hugo -- gohugo.io</generator><language>es</language><lastBuildDate>Tue, 09 Jun 2026 01:40:00 +0000</lastBuildDate><atom:link href="https://blog.lo0.es/tags/self-speculative-decoding/index.xml" rel="self" type="application/rss+xml"/><item><title>Self-speculative decoding: el modelo que se adelanta a sí mismo</title><link>https://blog.lo0.es/posts/self-speculative-decoding-early-exit/</link><pubDate>Tue, 09 Jun 2026 01:40:00 +0000</pubDate><guid>https://blog.lo0.es/posts/self-speculative-decoding-early-exit/</guid><description>&lt;blockquote>
&lt;p>Este post es el complemento directo de &lt;a href="https://blog.lo0.es/posts/speculative-decoding-fundamentos/">Speculative decoding: el secretario que adelanta&lt;/a>. Allí draft y target son &lt;strong>dos modelos distintos&lt;/strong>; aquí son &lt;strong>el mismo modelo a dos profundidades&lt;/strong>. Léelo primero: damos por sabidos el rejection sampling, el techo &lt;code>1/(1-α)&lt;/code> y la fórmula del speedup, y aquí solo cambiamos qué es el draft.&lt;/p>
&lt;/blockquote>
&lt;h2 id="tldr">TL;DR&lt;/h2>
&lt;p>Speculative decoding clásico exige una pareja: un modelo &lt;em>draft&lt;/em> barato propone γ tokens y un &lt;em>target&lt;/em> caro los verifica en un único forward pass paralelo. En modelos grandes el draft puede ser un 1 % del target y caber holgado. En &lt;strong>modelos pequeños&lt;/strong> (SLM, 1B–8B) esa receta se rompe por dos lados: un draft que sea 1/10 de un 3B es un 0.3B que apenas acierta (α se desploma), y cargar un segundo modelo —por pequeño que sea— &lt;strong>dobla las piezas a mantener y se come VRAM que en una 4090 o en device no sobra&lt;/strong>. &lt;em>Self-speculative decoding&lt;/em> resuelve ambos: el draft es el &lt;strong>propio modelo ejecutado de forma superficial&lt;/strong>. Un modelo de &lt;code>L&lt;/code> capas produce tokens borrador saliendo en una capa intermedia &lt;code>k &amp;lt; L&lt;/code> (&lt;em>early-exit&lt;/em>) o saltando un subconjunto de capas (&lt;em>layer-skip&lt;/em>), y luego verifica esos tokens con el forward completo de las &lt;code>L&lt;/code> capas. Como draft y verify &lt;strong>comparten pesos y comparten el KV cache de las capas comunes&lt;/strong>, el coste extra de memoria es &lt;strong>cero&lt;/strong>: no hay un segundo modelo, no hay un segundo KV cache, no hay nada nuevo que cargar. El precio es que el draft early-exit es &lt;strong>más caro&lt;/strong> que un draft externo minúsculo (recorre &lt;code>k/L&lt;/code> del modelo en vez de un 1 %), así que el coste relativo &lt;code>c&lt;/code> sube. El trade-off honesto: con draft dedicado bien entrenado (EAGLE-3) que &lt;strong>quepa&lt;/strong> en memoria, su α suele ser mayor y gana; self-spec gana cuando no hay draft entrenado, no cabe, o estás en device.&lt;/p>
&lt;h2 id="la-analogía-el-ajedrecista-que-juega-a-ojo-y-luego-calcula">La analogía: el ajedrecista que juega a ojo y luego calcula&lt;/h2>
&lt;p>Un buen jugador de ajedrez hace dos cosas con el mismo cerebro. Primero mira el tablero y, &lt;strong>a ojo&lt;/strong>, en medio segundo, propone una jugada &amp;ldquo;que pinta bien&amp;rdquo;: es intuición de patrones, reconocimiento rápido, las capas superficiales del juicio. Después, antes de mover, &lt;strong>calcula a fondo&lt;/strong>: tres jugadas por delante, las respuestas del rival, las líneas tácticas. Ese cálculo profundo confirma la intuición o la corrige.&lt;/p>
&lt;p>Lo decisivo es que &lt;strong>es la misma persona&lt;/strong> haciendo de borrador y de revisor. No contrata a un segundo ajedrecista más débil para que adivine la jugada y luego él la valida —eso sería el speculative clásico con draft externo—. Aquí el borrador rápido y la verificación lenta salen del mismo cerebro, recorrido a dos profundidades.&lt;/p>
&lt;p>La analogía se sostiene punto por punto:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>El vistazo a ojo es el forward early-exit&lt;/strong>: el modelo recorre solo las primeras &lt;code>k&lt;/code> capas y emite un token borrador. Rápido, aproximado.&lt;/li>
&lt;li>&lt;strong>El cálculo a fondo es el forward completo de las &lt;code>L&lt;/code> capas&lt;/strong>, que verifica el borrador con rejection sampling exacto.&lt;/li>
&lt;li>&lt;strong>Que sea la misma persona es el reuso de pesos y de KV cache&lt;/strong>: las &lt;code>k&lt;/code> capas superficiales del draft son &lt;strong>literalmente las mismas&lt;/strong> que las &lt;code>k&lt;/code> primeras capas del verify; lo ya computado no se recomputa.&lt;/li>
&lt;li>&lt;strong>Que la jugada final sea idéntica a la que el jugador habría elegido calculando siempre a fondo&lt;/strong> es la garantía de rejection sampling: la calidad del output no se degrada (la prueba está en el &lt;a href="https://blog.lo0.es/posts/speculative-decoding-fundamentos/">post de speculative&lt;/a>).&lt;/li>
&lt;/ul>
&lt;h2 id="por-qué-el-draft-externo-no-encaja-en-modelos-pequeños">Por qué el draft externo no encaja en modelos pequeños&lt;/h2>
&lt;p>Repasemos el coste del speculative clásico con dos números. El speedup depende de la tasa de aceptación α (cuánto acierta el draft) y del coste relativo &lt;code>c = T_draft / T_target&lt;/code>. Un draft útil necesita &lt;strong>α alto y c bajo a la vez&lt;/strong>. En modelos grandes eso es alcanzable: un draft de 1B para un target de 70B tiene &lt;code>c ≈ 0.015&lt;/code> y, si está bien destilado (EAGLE), α &amp;gt; 0.8. El producto sale rentable.&lt;/p>
&lt;p>En un modelo pequeño el equilibrio se rompe:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>El draft proporcional es inservible.&lt;/strong> Si quieres &lt;code>c ≈ 0.1&lt;/code> para un target de 3B, tu draft es un ~0.3B. Un 0.3B genérico tiene una distribución tan distinta del 3B que α cae a la zona 0.3–0.5. Y &lt;code>1/(1-α)&lt;/code> con α = 0.4 es un techo de 1.67 tokens/step: ni con γ infinito sacas más. El premio se evapora.&lt;/li>
&lt;li>&lt;strong>Cargar un segundo modelo dobla las piezas.&lt;/strong> Aunque el draft sea pequeño en VRAM, es &lt;strong>otro checkpoint que versionar, cuantizar, validar y servir&lt;/strong>, y tiene &lt;strong>su propio KV cache&lt;/strong>. En una RTX 4090 (24 GB, Ada Lovelace) con un 8B cuantizado y un contexto largo, el KV cache ya aprieta; meter un segundo modelo y su cache puede forzarte a bajar la concurrencia o el contexto máximo. En &lt;strong>device&lt;/strong> (un móvil, un NUC, un edge box) directamente no hay sitio.&lt;/li>
&lt;li>&lt;strong>No siempre existe un draft entrenado&lt;/strong> para tu modelo exótico o fine-tuneado. EAGLE necesita entrenar el draft on-policy contra ese target concreto (ver &lt;a href="https://blog.lo0.es/posts/knowledge-distillation-fundamentos/">knowledge distillation&lt;/a>). Si tu SLM es un fine-tune propio, no hay draft oficial publicado.&lt;/li>
&lt;/ol>
&lt;p>Self-speculative ataca los tres a la vez con una idea: &lt;strong>no traigas un segundo modelo; usa el primero a media profundidad.&lt;/strong>&lt;/p>
&lt;h2 id="el-mecanismo-early-exit-como-draft-forward-completo-como-verify">El mecanismo: early-exit como draft, forward completo como verify&lt;/h2>
&lt;p>Un transformer de &lt;code>L&lt;/code> capas, en cada posición, transforma el hidden state capa a capa: &lt;code>h_0 → h_1 → ... → h_L&lt;/code>, y la &lt;em>LM head&lt;/em> proyecta &lt;code>h_L&lt;/code> a logits. La observación que lo habilita todo: &lt;strong>&lt;code>h_k&lt;/code> para &lt;code>k &amp;lt; L&lt;/code> ya es un hidden state razonable&lt;/strong>. Si lo pasas por la misma LM head (o por una head ligera dedicada), obtienes una distribución de salida &amp;ldquo;prematura&amp;rdquo; pero a menudo correcta para los tokens fáciles. Esa es la fuente del borrador.&lt;/p>
&lt;p>La iteración de self-speculative tiene la misma estructura que el speculative clásico —draft, verify, accept/reject— pero ambos roles son el mismo modelo:&lt;/p>
&lt;p>&lt;strong>Paso 1 — Draft superficial.&lt;/strong> Para producir γ tokens borrador, el modelo recorre solo las primeras &lt;code>k&lt;/code> capas (o un subconjunto de capas en el caso layer-skip) y aplica la LM head. Cada token borrador cuesta ≈ &lt;code>k/L&lt;/code> de un forward completo. Llamamos &lt;code>c = k/L&lt;/code> al coste relativo del draft. Los γ borradores se generan autoregresivamente a este coste reducido.&lt;/p>
&lt;p>&lt;strong>Paso 2 — Verify completo.&lt;/strong> El modelo ejecuta &lt;strong>un único forward pass de las &lt;code>L&lt;/code> capas&lt;/strong> sobre &lt;code>prompt + x_1...x_γ&lt;/code>. Por la atención causal obtiene &lt;code>p(·|prompt, x_&amp;lt;i)&lt;/code> para cada posición, exactamente igual que en el speculative clásico.&lt;/p>
&lt;p>&lt;strong>Paso 3 — Accept/reject.&lt;/strong> Rejection sampling idéntico al del post anterior: se aceptan tokens de izquierda a derecha, se corrige en la primera divergencia muestreando del residual &lt;code>norm(max(0, p−q))&lt;/code>, y si se aceptan los γ se añade el token bonus. La calidad del output es &lt;strong>exactamente&lt;/strong> la del modelo completo.&lt;/p>
&lt;h3 id="el-truco-que-hace-c-aún-más-barato-reuso-de-kv-cache-de-capas-compartidas">El truco que hace &lt;code>c&lt;/code> aún más barato: reuso de KV cache de capas compartidas&lt;/h3>
&lt;p>Aquí está la diferencia clave frente a un draft externo. Cuando el modelo hace el draft recorriendo las capas &lt;code>0..k&lt;/code>, calcula y &lt;strong>almacena el KV cache de esas &lt;code>k&lt;/code> capas&lt;/strong> para los tokens del prompt y los borradores. Cuando llega el verify completo, las capas &lt;code>0..k&lt;/code> del forward de &lt;code>L&lt;/code> capas son &lt;strong>bit a bit las mismas operaciones sobre los mismos pesos&lt;/strong> que ya hizo el draft. No hay que recomputarlas: el verify &lt;strong>reusa directamente el KV cache&lt;/strong> que el draft dejó para las capas &lt;code>0..k&lt;/code>, y solo computa de verdad las capas &lt;code>k..L&lt;/code> que faltan.&lt;/p>
&lt;p>Eso tiene dos consecuencias:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Memoria extra cero.&lt;/strong> No hay un segundo KV cache. El KV de las capas comunes es uno solo, compartido entre draft y verify. Contrasta con vanilla SD, donde el draft tiene su propio cache completo (ver &lt;a href="https://blog.lo0.es/posts/kv-cache-fundamentos/">KV cache&lt;/a>).&lt;/li>
&lt;li>&lt;strong>Cómputo parcialmente reusado.&lt;/strong> El verify solo paga las capas &lt;code>k..L&lt;/code> &amp;ldquo;nuevas&amp;rdquo; para los tokens que ya pasaron por el draft. El forward completo no es tan caro como sugiere &lt;code>L&lt;/code>, porque las primeras &lt;code>k&lt;/code> capas vienen del cache.&lt;/li>
&lt;/ul>
&lt;div class="diagram" style="max-width:780px;margin:1.5rem auto;">
&lt;svg viewBox="0 0 780 400" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Early-exit draft y forward completo verify con KV compartido">
&lt;text x="390" y="22" text-anchor="middle" fill="currentColor" font-size="14" font-weight="700">Un solo modelo de L=32 capas, recorrido a dos profundidades&lt;/text>
&lt;!-- DRAFT column -->
&lt;p>&lt;text x="180" y="50" text-anchor="middle" fill="currentColor" font-size="12" font-weight="700">DRAFT · early-exit en k=8&lt;/text>
&lt;rect x="120" y="60" width="120" height="120" fill="#fff4d6" stroke="#a48000" stroke-width="1.4" rx="6"/>
&lt;text x="180" y="95" text-anchor="middle" fill="#a48000" font-size="12" font-weight="600">capas 0..8&lt;/text>
&lt;text x="180" y="115" text-anchor="middle" fill="#a48000" font-size="11">recorrido superficial&lt;/text>
&lt;text x="180" y="133" text-anchor="middle" fill="#a48000" font-size="11">coste ≈ k/L = 0.25&lt;/text>
&lt;rect x="120" y="190" width="120" height="30" fill="#fff4d6" stroke="#a48000" stroke-width="1.4" rx="6"/>
&lt;text x="180" y="210" text-anchor="middle" fill="#a48000" font-size="11" font-weight="600">LM head → borrador&lt;/text>
&lt;text x="180" y="245" text-anchor="middle" fill="currentColor" font-size="11">x₁ x₂ x₃ x₄ (γ=4)&lt;/text>&lt;/p>
&lt;!-- VERIFY column -->
&lt;p>&lt;text x="600" y="50" text-anchor="middle" fill="currentColor" font-size="12" font-weight="700">VERIFY · forward completo L=32&lt;/text>
&lt;rect x="540" y="60" width="120" height="120" fill="#d4ecff" stroke="#1f5fa8" stroke-width="1.4" rx="6"/>
&lt;text x="600" y="92" text-anchor="middle" fill="#1f5fa8" font-size="12" font-weight="600">capas 0..8&lt;/text>
&lt;text x="600" y="110" text-anchor="middle" fill="#1f5fa8" font-size="11">(reusadas, no&lt;/text>
&lt;text x="600" y="125" text-anchor="middle" fill="#1f5fa8" font-size="11">se recomputan)&lt;/text>
&lt;line x1="540" y1="135" x2="660" y2="135" stroke="#1f5fa8" stroke-width="1" stroke-dasharray="4 2"/>
&lt;text x="600" y="158" text-anchor="middle" fill="#1f5fa8" font-size="12" font-weight="600">capas 8..32&lt;/text>
&lt;text x="600" y="174" text-anchor="middle" fill="#1f5fa8" font-size="11">cómputo nuevo&lt;/text>
&lt;rect x="540" y="190" width="120" height="30" fill="#d4ecff" stroke="#1f5fa8" stroke-width="1.4" rx="6"/>
&lt;text x="600" y="210" text-anchor="middle" fill="#1f5fa8" font-size="11" font-weight="600">LM head → p(·)&lt;/text>&lt;/p>
&lt;!-- shared KV cache box -->
&lt;rect x="300" y="80" width="180" height="80" fill="#cdebd0" stroke="#2a7a40" stroke-width="1.6" rx="8"/>
&lt;text x="390" y="110" text-anchor="middle" fill="#2a7a40" font-size="12" font-weight="700">KV cache COMPARTIDO&lt;/text>
&lt;text x="390" y="128" text-anchor="middle" fill="#2a7a40" font-size="11">capas 0..8 · un solo cache&lt;/text>
&lt;text x="390" y="145" text-anchor="middle" fill="#2a7a40" font-size="11">memoria extra = 0&lt;/text>
&lt;!-- arrows draft->KV and KV->verify -->
&lt;p>&lt;defs>&lt;marker id="ssd1" 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="#2a7a40"/>&lt;/marker>&lt;/defs>
&lt;path d="M240,120 L300,120" fill="none" stroke="#2a7a40" stroke-width="1.6" marker-end="url(#ssd1)"/>
&lt;text x="270" y="112" text-anchor="middle" fill="#2a7a40" font-size="10">escribe KV 0..8&lt;/text>
&lt;path d="M480,120 L540,120" fill="none" stroke="#2a7a40" stroke-width="1.6" marker-end="url(#ssd1)"/>
&lt;text x="510" y="112" text-anchor="middle" fill="#2a7a40" font-size="10">lee KV 0..8&lt;/text>&lt;/p>
&lt;!-- accept/reject row -->
&lt;p>&lt;text x="390" y="280" text-anchor="middle" fill="currentColor" font-size="12" font-weight="700">Rejection sampling (idéntico al speculative clásico)&lt;/text>
&lt;rect x="180" y="295" width="80" height="34" fill="#cdebd0" stroke="#2a7a40" stroke-width="1.4" rx="6"/>&lt;text x="220" y="316" text-anchor="middle" fill="#2a7a40" font-size="12" font-weight="600">x₁ ✓&lt;/text>
&lt;rect x="270" y="295" width="80" height="34" fill="#cdebd0" stroke="#2a7a40" stroke-width="1.4" rx="6"/>&lt;text x="310" y="316" text-anchor="middle" fill="#2a7a40" font-size="12" font-weight="600">x₂ ✓&lt;/text>
&lt;rect x="360" y="295" width="80" height="34" fill="#cdebd0" stroke="#2a7a40" stroke-width="1.4" rx="6"/>&lt;text x="400" y="316" text-anchor="middle" fill="#2a7a40" font-size="12" font-weight="600">x₃ ✓&lt;/text>
&lt;rect x="450" y="295" width="80" height="34" fill="#f6caca" stroke="#a52a2a" stroke-width="1.4" rx="6"/>&lt;text x="490" y="316" text-anchor="middle" fill="#a52a2a" font-size="12" font-weight="600">x₄ ✗&lt;/text>
&lt;text x="390" y="360" text-anchor="middle" fill="currentColor" font-size="11">Output = exactamente el del modelo completo · 0 modelos extra · 0 KV extra&lt;/text>
&lt;text x="390" y="385" text-anchor="middle" fill="currentColor" font-size="11">El draft y el verify son el mismo modelo; las capas 0..8 se computan una sola vez.&lt;/text>
&lt;/svg>&lt;/p>
&lt;/div>
&lt;h2 id="las-familias-estado-2026">Las familias (estado 2026)&lt;/h2>
&lt;p>No hay una sola forma de hacer self-speculative. Difieren en &lt;strong>qué capas se saltan&lt;/strong> y en &lt;strong>si hace falta entrenar&lt;/strong>.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Familia&lt;/th>
&lt;th>Año / venue&lt;/th>
&lt;th>Cómo elige qué saltar&lt;/th>
&lt;th>¿Entrenamiento?&lt;/th>
&lt;th>KV extra&lt;/th>
&lt;th>Idea distintiva&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>LayerSkip&lt;/strong> (Elhoushi et al.)&lt;/td>
&lt;td>2024, arXiv:2404.16710&lt;/td>
&lt;td>Early-exit en capa fija &lt;code>k&lt;/code>; una sola LM head sirve a todas las salidas&lt;/td>
&lt;td>Sí — &lt;em>layer dropout&lt;/em> + &lt;em>early-exit loss&lt;/em> en train/fine-tune&lt;/td>
&lt;td>0&lt;/td>
&lt;td>Un único modelo entrenado para hacer draft y verify; reusa cómputo parcial&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>SWIFT&lt;/strong>&lt;/td>
&lt;td>ICLR 2025 (OpenReview EKJhH5D5wA)&lt;/td>
&lt;td>Selecciona qué capas saltar &lt;strong>on-the-fly&lt;/strong>, sin tocar pesos&lt;/td>
&lt;td>&lt;strong>No&lt;/strong> — plug-and-play sobre el modelo dado&lt;/td>
&lt;td>0&lt;/td>
&lt;td>Self-spec &lt;em>training-free&lt;/em>: optimiza el conjunto de capas saltadas en caliente&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>CLaSp&lt;/strong>&lt;/td>
&lt;td>2025, arXiv:2505.24196&lt;/td>
&lt;td>&lt;em>In-context&lt;/em> layer skip dinámico: el patrón de capas saltadas se adapta al contexto&lt;/td>
&lt;td>No (dinámico en inferencia)&lt;/td>
&lt;td>0&lt;/td>
&lt;td>El skip no es fijo; cambia según lo que se está generando&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>ConfLayers&lt;/strong>&lt;/td>
&lt;td>2026, arXiv:2604.14612&lt;/td>
&lt;td>Salta capas según &lt;strong>confianza&lt;/strong> del estado intermedio (adaptativo por token)&lt;/td>
&lt;td>No (criterio de confianza)&lt;/td>
&lt;td>0&lt;/td>
&lt;td>Profundidad variable: tokens fáciles salen antes, difíciles llegan más hondo&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Saguaro&lt;/strong>&lt;/td>
&lt;td>2025–26&lt;/td>
&lt;td>Formulación &lt;strong>asíncrona&lt;/strong>: el draft sigue especulando en paralelo mientras corre la verificación&lt;/td>
&lt;td>Depende de la variante&lt;/td>
&lt;td>0&lt;/td>
&lt;td>Solapa draft y verify en el tiempo en lugar de alternarlos&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>SSD para MoE on-device&lt;/strong>&lt;/td>
&lt;td>ACM Web Conf. 2026, doi 10.1145/3774904.3792218&lt;/td>
&lt;td>Self-spec aprovechando la &lt;em>sparsity&lt;/em> del MoE (pocos expertos activos por token)&lt;/td>
&lt;td>Variante específica MoE&lt;/td>
&lt;td>0&lt;/td>
&lt;td>El draft superficial activa aún menos expertos; encaja con MoE en device&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Tres lecturas operacionales de la tabla:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>El eje que más importa es entrenamiento sí/no.&lt;/strong> LayerSkip da el mejor α porque el modelo aprende a ser un buen draft superficial (con early-exit loss las capas intermedias se entrenan explícitamente para predecir bien). Pero exige fine-tune. SWIFT, CLaSp y ConfLayers son &lt;strong>training-free&lt;/strong>: peor α, pero se aplican a cualquier modelo ya entrenado sin tocar nada. Para un SLM que no controlas, training-free es lo realista.&lt;/li>
&lt;li>&lt;strong>El skip adaptativo (CLaSp, ConfLayers) sube α&lt;/strong> porque ajusta la profundidad del draft al token: gasta poco en lo fácil y más en lo difícil, en vez de un &lt;code>k&lt;/code> fijo. A cambio, el &lt;code>c&lt;/code> efectivo deja de ser constante.&lt;/li>
&lt;li>&lt;strong>Saguaro ataca otra cosa:&lt;/strong> no sube α, solapa el tiempo de draft y verify. Es ortogonal al resto y combinable.&lt;/li>
&lt;/ol>
&lt;h2 id="la-matemática-mismo-marco-distinto-c">La matemática: mismo marco, distinto &lt;code>c&lt;/code>&lt;/h2>
&lt;p>Reutilizamos el aparato del &lt;a href="https://blog.lo0.es/posts/speculative-decoding-fundamentos/">post de speculative&lt;/a> sin cambiar una letra. Con α la tasa de aceptación y γ el número de borradores:&lt;/p>
&lt;p>$$E[\text{tokens por step}] = \frac{1 - \alpha^{\gamma+1}}{1 - \alpha}, \qquad \text{Speedup} = \frac{1 - \alpha^{\gamma+1}}{(1 - \alpha)(\gamma c + 1)}$$&lt;/p>
&lt;p>Y el techo algorítmico es el mismo: &lt;code>lim_{γ→∞} = 1/(1-α)&lt;/code>. Lo único que cambia en self-speculative es &lt;strong>el valor de &lt;code>c&lt;/code>&lt;/strong>: ya no es el ratio de tamaños de dos modelos, sino &lt;code>c = k/L&lt;/code>, la fracción de capas que recorre el draft early-exit.&lt;/p>
&lt;h3 id="ejemplo-numérico-self-spec-con-l32-salida-en-k8">Ejemplo numérico: self-spec con L=32, salida en k=8&lt;/h3>
&lt;p>Tomemos un SLM de &lt;code>L = 32&lt;/code> capas que sale en &lt;code>k = 8&lt;/code> para el draft: &lt;code>c = k/L = 8/32 = 0.25&lt;/code>. Supongamos α = 0.7 (razonable para early-exit en tokens conversacionales) y γ = 4.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Tokens esperados por step:&lt;/strong> &lt;code>(1 − 0.7⁵) / (1 − 0.7) = (1 − 0.168) / 0.3 = 0.832 / 0.3 = 2.77&lt;/code>&lt;/li>
&lt;li>&lt;strong>Speedup:&lt;/strong> &lt;code>2.77 / (4 × 0.25 + 1) = 2.77 / 2.0 = 1.39×&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>El factor del denominador es &lt;code>γc + 1 = 4·0.25 + 1 = 2.0&lt;/code>: el draft early-exit, al costar un cuarto del modelo cada token, se come parte del beneficio. Salir más arriba ayuda: con &lt;code>k = 4&lt;/code> (&lt;code>c = 0.125&lt;/code>), denominador &lt;code>= 1.5&lt;/code> y speedup &lt;code>= 2.77/1.5 = 1.85×&lt;/code> — pero salir más arriba normalmente baja α, así que hay tensión real entre &lt;code>k&lt;/code> pequeño (barato) y α alto (acierta).&lt;/p>
&lt;h3 id="comparación-honesta-con-un-draft-externo">Comparación honesta con un draft externo&lt;/h3>
&lt;p>Pongamos al lado un draft externo minúsculo bien destilado: &lt;code>c = 0.1&lt;/code> y α = 0.78 (lo que un EAGLE-style draft puede dar), mismo γ = 4.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Tokens/step:&lt;/strong> &lt;code>(1 − 0.78⁵)/(1 − 0.78) = (1 − 0.289)/0.22 = 0.711/0.22 = 3.23&lt;/code>&lt;/li>
&lt;li>&lt;strong>Speedup:&lt;/strong> &lt;code>3.23 / (4 × 0.1 + 1) = 3.23 / 1.4 = 2.31×&lt;/code>&lt;/li>
&lt;/ul>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Configuración&lt;/th>
&lt;th>c&lt;/th>
&lt;th>α&lt;/th>
&lt;th>tokens/step&lt;/th>
&lt;th>speedup&lt;/th>
&lt;th>VRAM extra&lt;/th>
&lt;th>piezas a mantener&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Self-spec early-exit (k=8)&lt;/td>
&lt;td>0.25&lt;/td>
&lt;td>0.70&lt;/td>
&lt;td>2.77&lt;/td>
&lt;td>&lt;strong>1.39×&lt;/strong>&lt;/td>
&lt;td>&lt;strong>0&lt;/strong>&lt;/td>
&lt;td>&lt;strong>0&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Self-spec early-exit (k=4)&lt;/td>
&lt;td>0.125&lt;/td>
&lt;td>0.65&lt;/td>
&lt;td>2.50&lt;/td>
&lt;td>&lt;strong>1.67×&lt;/strong>&lt;/td>
&lt;td>&lt;strong>0&lt;/strong>&lt;/td>
&lt;td>&lt;strong>0&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Draft externo destilado&lt;/td>
&lt;td>0.10&lt;/td>
&lt;td>0.78&lt;/td>
&lt;td>3.23&lt;/td>
&lt;td>&lt;strong>2.31×&lt;/strong>&lt;/td>
&lt;td>sí (+modelo +KV)&lt;/td>
&lt;td>1 modelo extra&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>La lectura es exactamente la que cabe esperar y conviene &lt;strong>no maquillar&lt;/strong>: si tienes un draft dedicado, entrenado contra tu target, y &lt;strong>cabe en memoria&lt;/strong>, su α mayor y su &lt;code>c&lt;/code> menor le dan más speedup. EAGLE-3 con draft bien entrenado suele ganar en speedup bruto. &lt;strong>Self-spec no compite en speedup bruto; compite en coste total.&lt;/strong> Sus columnas ganadoras son las dos de la derecha: cero VRAM extra y cero piezas que mantener. Self-spec gana cuando:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>no hay draft entrenado&lt;/strong> para tu modelo (SLM propio, fine-tune raro),&lt;/li>
&lt;li>&lt;strong>el draft no cabe&lt;/strong> (4090 ya llena, contexto largo que necesita el KV),&lt;/li>
&lt;li>&lt;strong>estás en device&lt;/strong> (móvil, NUC, edge), donde un segundo modelo y su KV simplemente no entran.&lt;/li>
&lt;/ul>
&lt;p>Es el mismo patrón que con MTP en el post anterior: a veces el mejor draft es &lt;strong>el que no tienes que cargar&lt;/strong>.&lt;/p>
&lt;h2 id="por-qué-encaja-justo-con-modelos-pequeños-y-device">Por qué encaja justo con modelos pequeños y device&lt;/h2>
&lt;p>El régimen donde self-spec brilla es el de baja concurrencia, memory-bandwidth-bound, con presupuesto de memoria escaso — exactamente el de un SLM en una sola GPU o en device (el porqué del régimen está en &lt;a href="https://blog.lo0.es/posts/roofline-invertido-modelos-pequenos/">el roofline invertido&lt;/a>). Tres razones:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Cero memoria extra es decisivo donde no sobra.&lt;/strong> En una RTX 4090 (24 GB, Ada Lovelace) sirviendo un 7B–8B cuantizado con contexto largo, cada GB cuenta. Self-spec no pide ni uno: reusa pesos y KV. Un draft externo, aunque pequeño, te obliga a recortar contexto o concurrencia. En device la diferencia es binaria: con self-spec aceleras; con draft externo no hay sitio y punto.&lt;/li>
&lt;li>&lt;strong>No hay segundo checkpoint que versionar.&lt;/strong> Operacionalmente, un SLM en edge desplegado en cientos de cajas se vuelve insostenible si cada una necesita dos modelos sincronizados. Un solo binario que hace draft y verify es muchísimo más simple de mantener.&lt;/li>
&lt;li>&lt;strong>Encaja con MoE en device.&lt;/strong> En un MoE de grano fino para device (ver &lt;a href="https://blog.lo0.es/posts/arquitecturas-nativas-device-moe-grano-fino/">arquitecturas nativas para device&lt;/a>), el draft superficial activa aún menos expertos, y el régimen memory-bound persiste incluso a batch medio — justo lo que el trabajo de SSD para MoE on-device (ACM WWW 2026) explota.&lt;/li>
&lt;/ol>
&lt;p>El contrapunto, repetido para que no se olvide: en un &lt;strong>cluster genérico 4×H100 SXM (320 GB, NVLink, FP8 nativo)&lt;/strong>, donde la memoria no es el cuello de botella, un draft EAGLE-3 dedicado &lt;strong>sí cabe&lt;/strong> y su α mayor le da más speedup. Allí self-spec es plan B: lo usas si el modelo es exótico y no hay draft entrenado, no porque la memoria apriete.&lt;/p>
&lt;h2 id="pitfalls">Pitfalls&lt;/h2>
&lt;ul>
&lt;li>&lt;strong>El α depende muchísimo de &lt;code>k&lt;/code>.&lt;/strong> Salir demasiado arriba (&lt;code>k&lt;/code> pequeño) abarata el draft pero hunde α; salir demasiado abajo (&lt;code>k&lt;/code> cercano a &lt;code>L&lt;/code>) sube α pero el draft cuesta casi un forward completo y &lt;code>c → 1&lt;/code>, matando el speedup. El óptimo es empírico y específico del modelo. Desconfía de cualquier número de speedup que no diga en qué &lt;code>k&lt;/code> se midió.&lt;/li>
&lt;li>&lt;strong>Training-free no es gratis en calidad de draft.&lt;/strong> SWIFT/CLaSp dan α menores que LayerSkip precisamente porque las capas intermedias del modelo no se entrenaron para ser buenas salidas prematuras. El número que importa es α medido en &lt;em>tu&lt;/em> distribución, no el del paper.&lt;/li>
&lt;li>&lt;strong>Sampling temperature y outputs creativos&lt;/strong> bajan α igual que en el speculative clásico. A T alta, el speedup de self-spec se erosiona más rápido todavía porque parte de un α más bajo.&lt;/li>
&lt;li>&lt;strong>Batch grande lo neutraliza igual que al speculative clásico.&lt;/strong> En cuanto el decode pasa a compute-bound, los borradores dejan de ser &amp;ldquo;casi gratis&amp;rdquo;. Self-spec es para baja concurrencia.&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/speculative-decoding-fundamentos/">Speculative decoding: el secretario que adelanta&lt;/a> — el complemento directo y prerequisito: draft + verify + rejection sampling, el techo &lt;code>1/(1-α)&lt;/code> y la fórmula del speedup que aquí reutilizamos tal cual.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/roofline-invertido-modelos-pequenos/">El roofline invertido en modelos pequeños&lt;/a> — por qué el SLM vive en régimen memory-bound, que es justo lo que habilita cualquier forma de speculative.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/arquitecturas-nativas-device-moe-grano-fino/">Arquitecturas nativas para device: MoE de grano fino&lt;/a> — dónde aterriza el self-spec sobre MoE en device, aprovechando la sparsity del router.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/kv-cache-fundamentos/">KV cache: la memoria de trabajo&lt;/a> — el reuso del KV de las capas compartidas entre draft y verify es lo que hace que la memoria extra sea cero; aquí está el mecanismo del cache.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/knowledge-distillation-fundamentos/">Knowledge distillation&lt;/a> — el early-exit loss de LayerSkip es pariente de la destilación: enseña a las capas intermedias a predecir como el modelo completo.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/poda-pruning-llm-fundamentos/">Poda de modelos LLM&lt;/a> — saltar capas es una forma de poda estructurada &lt;em>en inferencia&lt;/em>; layer-skip y layer-dropping comparten raíz conceptual.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/moe-inference-fundamentos/">MoE inference: el call center con 256 especialistas&lt;/a> — el régimen memory-bound persistente del MoE hace que el self-spec sobre MoE gane incluso a batch medio.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/decode-optimizaciones-vllm/">Optimizando el decode en vLLM&lt;/a> — dónde se configuran en la práctica los métodos speculative en producción.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/entornos-mixtos-nvidia-intel-servidores-nucs/">Entornos mixtos NVIDIA + Intel&lt;/a> — el caso device/edge donde &amp;ldquo;cero modelo extra&amp;rdquo; deja de ser una comodidad y pasa a ser la única opción viable.&lt;/li>
&lt;/ul>
&lt;h2 id="referencias">Referencias&lt;/h2>
&lt;ul>
&lt;li>Elhoushi, M., et al. &lt;em>LayerSkip: Enabling Early Exit Inference and Self-Speculative Decoding&lt;/em>. 2024. &lt;a href="https://arxiv.org/abs/2404.16710">https://arxiv.org/abs/2404.16710&lt;/a>&lt;/li>
&lt;li>&lt;em>SWIFT: On-the-Fly Self-Speculative Decoding for LLM Inference Acceleration&lt;/em>. ICLR 2025. &lt;a href="https://openreview.net/forum?id=EKJhH5D5wA">https://openreview.net/forum?id=EKJhH5D5wA&lt;/a>&lt;/li>
&lt;li>&lt;em>CLaSp: In-Context Layer Skip for Self-Speculative Decoding&lt;/em>. 2025. &lt;a href="https://arxiv.org/abs/2505.24196">https://arxiv.org/abs/2505.24196&lt;/a>&lt;/li>
&lt;li>&lt;em>ConfLayers: Confidence-Adaptive Layer Skipping for Self-Speculative Decoding&lt;/em>. 2026. &lt;a href="https://arxiv.org/abs/2604.14612">https://arxiv.org/abs/2604.14612&lt;/a>&lt;/li>
&lt;li>&lt;em>Self-Speculative Decoding for MoE on Device&lt;/em>. ACM Web Conference 2026. &lt;a href="https://doi.org/10.1145/3774904.3792218">https://doi.org/10.1145/3774904.3792218&lt;/a>&lt;/li>
&lt;li>Hugging Face blog. &lt;em>Faster Text Generation with Self-Speculative Decoding&lt;/em>. &lt;a href="https://huggingface.co/blog/layerskip">https://huggingface.co/blog/layerskip&lt;/a>&lt;/li>
&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;/ul></description></item></channel></rss>