<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Ttq on lo0 — Blog Técnico</title><link>https://blog.lo0.es/tags/ttq/</link><description>Recent content in Ttq on lo0 — Blog Técnico</description><generator>Hugo -- gohugo.io</generator><language>es</language><lastBuildDate>Tue, 09 Jun 2026 02:00:00 +0000</lastBuildDate><atom:link href="https://blog.lo0.es/tags/ttq/index.xml" rel="self" type="application/rss+xml"/><item><title>Test-time quantization: cuantizar en caliente sin dataset de calibración</title><link>https://blog.lo0.es/posts/test-time-quantization-en-caliente/</link><pubDate>Tue, 09 Jun 2026 02:00:00 +0000</pubDate><guid>https://blog.lo0.es/posts/test-time-quantization-en-caliente/</guid><description>&lt;blockquote>
&lt;p>Este post es la continuación natural de &lt;a href="https://blog.lo0.es/posts/quantization-fundamentos-inferencia/">Quantization para inferencia LLM&lt;/a>, que conviene leer primero: allí están GPTQ, AWQ, el scale + zero-point y por qué los outliers de activación son el problema central. Aquí no discutimos &lt;em>cuántos bits&lt;/em> usar, sino &lt;strong>cuándo y con qué información se calculan las escalas&lt;/strong>: offline contra un corpus (PTQ) o en caliente contra el tráfico real (TTQ).&lt;/p>
&lt;/blockquote>
&lt;h2 id="tldr">TL;DR&lt;/h2>
&lt;p>La cuantización activation-aware (AWQ, SmoothQuant) decide qué canales proteger midiendo la magnitud de las activaciones sobre un &lt;strong>dataset de calibración&lt;/strong> en un &lt;strong>pase offline&lt;/strong>, antes de desplegar. El supuesto implícito es que ese corpus representa el tráfico futuro. Pero los outliers de activación —los canales de magnitud 10-100× la mediana que dominan el error de cuantización— &lt;strong>dependen del input&lt;/strong>: cambian con el dominio, el idioma y la distribución del cliente. Cuando el tráfico real se aleja de la calibración, las escalas fijas dejan de ser óptimas y la calidad cae. &lt;strong>Test-time quantization (TTQ)&lt;/strong> elimina el corpus y el pase offline: deriva las escalas activation-aware &lt;strong>en tiempo de inferencia&lt;/strong>, a partir de las activaciones que realmente se observan, por token o por batch. La contrapartida es honesta y no menor: introduce &lt;strong>overhead en runtime&lt;/strong> —calcular estadísticas, detectar outliers, recomputar escalas en cada step— que compite directamente con el ahorro de cuantizar. En modelos pequeños ese overhead pesa proporcionalmente más, porque el forward es corto y los costes fijos por step dominan (el marco está en &lt;a href="https://blog.lo0.es/posts/roofline-invertido-modelos-pequenos/">roofline invertido para SLM&lt;/a>). TTQ es &lt;strong>ortogonal&lt;/strong> al formato: no es un competidor de INT4 o FP8, es una forma distinta de derivar &lt;em>s&lt;/em>. Compensa cuando no hay pipeline de calibración, cuando la distribución del tráfico es cambiante o desconocida, y en multitenant donde no existe un corpus representativo.&lt;/p>
&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;defs>&lt;marker id="ttqm" 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" fill="currentColor" font-size="12" font-weight="600">Estás aquí: DEPLOY · derivar escalas de cuantización en caliente&lt;/text>
&lt;rect x="30" y="35" width="110" height="35" rx="6" fill="#f4f4f4" stroke="#444" stroke-width="1.4"/>&lt;text x="85" y="58" text-anchor="middle" fill="currentColor" font-size="12" font-weight="600">1 · Data&lt;/text>
&lt;rect x="155" y="35" width="110" height="35" rx="6" fill="#f4f4f4" stroke="#444" stroke-width="1.4"/>&lt;text x="210" y="58" text-anchor="middle" fill="currentColor" font-size="12" font-weight="600">2 · Tune&lt;/text>
&lt;rect x="280" y="35" width="110" height="35" rx="6" fill="#f4f4f4" stroke="#444" stroke-width="1.4"/>&lt;text x="335" y="58" text-anchor="middle" fill="currentColor" font-size="12" font-weight="600">3 · Eval&lt;/text>
&lt;rect x="405" y="35" width="110" height="35" rx="6" fill="#7ad88f" stroke="#444" stroke-width="3"/>&lt;text x="460" y="58" text-anchor="middle" fill="#111" font-size="12" font-weight="600">4 · Deploy&lt;/text>
&lt;rect x="530" y="35" width="110" height="35" rx="6" fill="#f4f4f4" stroke="#444" stroke-width="1.4"/>&lt;text x="585" y="58" text-anchor="middle" fill="currentColor" font-size="12" font-weight="600">5 · Observe&lt;/text>
&lt;rect x="655" y="35" width="110" height="35" rx="6" fill="#f4f4f4" stroke="#444" stroke-width="1.4"/>&lt;text x="710" y="58" text-anchor="middle" fill="currentColor" font-size="12" font-weight="600">6 · Retrain&lt;/text>
&lt;path d="M140,52 L155,52" stroke="#666" stroke-width="1.4" fill="none" marker-end="url(#ttqm)"/>
&lt;path d="M265,52 L280,52" stroke="#666" stroke-width="1.4" fill="none" marker-end="url(#ttqm)"/>
&lt;path d="M390,52 L405,52" stroke="#666" stroke-width="1.4" fill="none" marker-end="url(#ttqm)"/>
&lt;path d="M515,52 L530,52" stroke="#666" stroke-width="1.4" fill="none" marker-end="url(#ttqm)"/>
&lt;path d="M640,52 L655,52" stroke="#666" stroke-width="1.4" fill="none" marker-end="url(#ttqm)"/>
&lt;path d="M710,72 L710,82 L85,82 L85,72" stroke="#888" stroke-width="1.2" fill="none" stroke-dasharray="4 2" marker-end="url(#ttqm)"/>
&lt;/svg>
&lt;/div>
&lt;h2 id="la-analogía-el-sastre-que-toma-medidas-frente-a-las-tallas-pre-confeccionadas">La analogía: el sastre que toma medidas frente a las tallas pre-confeccionadas&lt;/h2>
&lt;p>Una tienda de ropa tiene dos formas de vestir a un cliente.&lt;/p>
&lt;p>La primera es &lt;strong>vender tallas pre-confeccionadas&lt;/strong>. La fábrica midió en su día a un &amp;ldquo;cliente medio&amp;rdquo; —un maniquí promedio construido sobre una muestra de población— y cortó las prendas según esas medidas. Cuando entra un cliente, le das la talla que más se le acerca. Es rapidísimo: la prenda ya está cosida, solo se entrega. El problema aparece cuando el cliente no se parece al maniquí promedio: si tiene los hombros mucho más anchos que la media —su outlier particular—, la talla estándar le tira o le sobra tela, porque se cortó protegiendo &lt;em>otras&lt;/em> zonas. Esto es la &lt;strong>PTQ offline calibrada&lt;/strong>: AWQ midió la importancia de cada canal sobre un corpus y fijó las escalas de una vez; rápido en inferencia, pero ciego al cliente concreto.&lt;/p>
&lt;p>La segunda es &lt;strong>el sastre que toma medidas en el momento&lt;/strong>. Cuando entra el cliente, el sastre saca el metro, mide &lt;em>a ese cliente&lt;/em>, detecta dónde está su volumen particular y ajusta el corte a su anatomía real. El resultado encaja mejor, sobre todo en los clientes que se salen del molde. Pero cada cliente cuesta tiempo: medir, marcar, decidir. Esto es &lt;strong>TTQ&lt;/strong>: las escalas se derivan en caliente de las activaciones que ese input genera realmente.&lt;/p>
&lt;p>La analogía se sostiene en tres detalles:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>El maniquí promedio = el dataset de calibración.&lt;/strong> Si la población que entra a la tienda se parece al maniquí, las tallas funcionan; si no, fallan en los extremos.&lt;/li>
&lt;li>&lt;strong>Tomar medidas en cada cliente = calcular estadísticas de activación por token/batch.&lt;/strong> Mejor ajuste, pero un coste fijo que se paga en &lt;em>cada&lt;/em> prenda.&lt;/li>
&lt;li>&lt;strong>Los hombros anchos = los canales outlier de activación.&lt;/strong> Son precisamente las zonas donde el ajuste importa y donde la talla genérica más se equivoca.&lt;/li>
&lt;/ul>
&lt;p>El sastre gana cuando los clientes son variados o desconocidos. Pierde cuando tienes una población homogénea y un maniquí que la representa bien: ahí pagar la medición en cada cliente es tirar el tiempo.&lt;/p>
&lt;h2 id="el-problema-que-ttq-resuelve-la-calibración-fija-envejece-con-el-tráfico">El problema que TTQ resuelve: la calibración fija envejece con el tráfico&lt;/h2>
&lt;p>Recordemos del &lt;a href="https://blog.lo0.es/posts/quantization-fundamentos-inferencia/">post de quantization&lt;/a> qué hacen exactamente AWQ y SmoothQuant. No cuantizan todos los canales por igual: identifican el ~1 % de canales cuyas activaciones tienen magnitud grande —los &lt;em>salient channels&lt;/em>— y los protegen escalándolos antes de cuantizar. Para medir esa importancia necesitan ver activaciones, y las ven sobre un &lt;strong>dataset de calibración&lt;/strong> (128-512 muestras, típicamente WikiText o un slice del dominio) en un &lt;strong>pase offline&lt;/strong> previo al despliegue.&lt;/p>
&lt;p>El supuesto es fuerte: que la distribución de activaciones del corpus de calibración &lt;strong>representa la del tráfico de producción&lt;/strong>. Dos razones por las que ese supuesto se rompe:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Los outliers de activación dependen del input.&lt;/strong> No son una propiedad fija del modelo como los pesos. El canal que es outlier procesando código C++ puede no serlo procesando árabe conversacional o JSON de logs. La magnitud y la posición de los picos cambian con el dominio, el idioma y el formato de entrada.&lt;/li>
&lt;li>&lt;strong>El tráfico real rara vez es el corpus.&lt;/strong> Calibras con WikiText en inglés y el cliente te manda tickets de soporte en español con tablas pegadas. La calibración protegió los canales que &lt;em>WikiText&lt;/em> activaba, no los que activa el tráfico real. Las escalas son subóptimas justo donde el cliente vive.&lt;/li>
&lt;/ol>
&lt;p>El resultado es &lt;strong>degradación dependiente de la distribución&lt;/strong>: el modelo cuantizado mantiene la calidad mientras el input se parece a la calibración y la pierde a medida que se aleja. El caso más incómodo es el &lt;strong>multitenant&lt;/strong>: si sirves a clientes con dominios distintos desde el mismo modelo cuantizado, no existe un único corpus representativo; cualquier calibración fija favorece a unos tenants y penaliza a otros.&lt;/p>
&lt;h2 id="el-mecanismo-de-ttq-medir-las-activaciones-reales-y-escalar-en-caliente">El mecanismo de TTQ: medir las activaciones reales y escalar en caliente&lt;/h2>
&lt;p>TTQ (arXiv:2603.19296, marzo 2026) propone derivar la cuantización &lt;strong>activation-aware en tiempo de inferencia&lt;/strong>, sin pase offline ni dataset de calibración. La idea, en su forma desnuda y conceptual:&lt;/p>
&lt;p>&lt;strong>Paso 1 — Observar.&lt;/strong> Cuando llega el tensor de activaciones &lt;code>X&lt;/code> a una capa lineal (por token o por batch), se calculan estadísticas baratas sobre los canales: una medida de tendencia central (mediana o media de magnitud) y una de dispersión por canal. Esto es el equivalente a que AWQ mirase su corpus, pero hecho sobre las activaciones que &lt;em>de verdad&lt;/em> están entrando ahora.&lt;/p>
&lt;p>&lt;strong>Paso 2 — Detectar outliers en caliente.&lt;/strong> Con esas estadísticas se identifican los canales cuya magnitud se dispara respecto a la mediana del tensor —el criterio típico es un umbral del estilo &amp;ldquo;magnitud &amp;gt; k × mediana&amp;rdquo;. Son los canales que, si se cuantizan con la misma escala que el resto, disparan el error.&lt;/p>
&lt;p>&lt;strong>Paso 3 — Derivar escalas y segregar.&lt;/strong> Para los canales normales se calcula una escala que aprovecha el rango; para los outliers se aplica un tratamiento distinto —una escala propia, o mantenerlos en precisión más alta— al estilo &lt;em>mixed-precision en caliente&lt;/em>. Es la misma filosofía que LLM.int8() (segregar outliers a FP16) o AWQ (escalar salient channels), pero con el umbral y las escalas &lt;strong>recalculados sobre el input actual&lt;/strong>, no congelados desde la calibración.&lt;/p>
&lt;p>&lt;strong>Paso 4 — Cuantizar y multiplicar.&lt;/strong> Con las escalas frescas se cuantiza y se ejecuta el GEMM. Las activaciones que entran al siguiente layer compensan el reescalado, igual que en AWQ, para que la matemática se cancele.&lt;/p>
&lt;p>La diferencia clave con AWQ no está en &lt;em>qué&lt;/em> se hace (proteger outliers de activación) sino en &lt;em>cuándo&lt;/em> y &lt;em>contra qué&lt;/em>: AWQ lo decide una vez, offline, contra un corpus; TTQ lo decide en cada step, en caliente, contra el tráfico real. Es la traslación a inferencia de la idea de &amp;ldquo;test-time&amp;rdquo;: adaptar el cómputo a la muestra concreta que tienes delante en lugar de a un promedio precomputado.&lt;/p>
&lt;div class="diagram" style="max-width:780px;margin:1.5rem auto;">
&lt;svg viewBox="0 0 780 300" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="PTQ offline calibrada frente a TTQ en caliente">
&lt;defs>&lt;marker id="ttq2" 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="195" y="22" text-anchor="middle" fill="currentColor" font-size="13" font-weight="700">PTQ offline-calibrada (AWQ / GPTQ)&lt;/text>
&lt;text x="585" y="22" text-anchor="middle" fill="currentColor" font-size="13" font-weight="700">TTQ en-caliente&lt;/text>
&lt;line x1="390" y1="35" x2="390" y2="285" stroke="#bbb" stroke-width="1" stroke-dasharray="4 3"/>
&lt;p>&lt;rect x="30" y="45" width="150" height="38" rx="6" fill="#ffe6d6" stroke="#a05a2c" stroke-width="1.4"/>&lt;text x="105" y="68" text-anchor="middle" fill="#111" font-size="11" font-weight="600">dataset calibración&lt;/text>
&lt;rect x="210" y="45" width="150" height="38" rx="6" fill="#ffe6d6" stroke="#a05a2c" stroke-width="1.4"/>&lt;text x="285" y="64" text-anchor="middle" fill="#111" font-size="11" font-weight="600">pase OFFLINE&lt;/text>&lt;text x="285" y="78" text-anchor="middle" fill="#444" font-size="10">fija escalas s, outliers&lt;/text>
&lt;path d="M180,64 L210,64" stroke="#666" stroke-width="1.4" fill="none" marker-end="url(#ttq2)"/>
&lt;rect x="120" y="105" width="150" height="34" rx="6" fill="#f0f0f0" stroke="#444" stroke-width="1.4"/>&lt;text x="195" y="126" text-anchor="middle" fill="#111" font-size="11" font-weight="600">escalas CONGELADAS&lt;/text>
&lt;path d="M285,83 L285,98 L195,98 L195,105" stroke="#666" stroke-width="1.4" fill="none" marker-end="url(#ttq2)"/>
&lt;rect x="30" y="165" width="150" height="34" rx="6" fill="#d6eaff" stroke="#1f5fa8" stroke-width="1.4"/>&lt;text x="105" y="186" text-anchor="middle" fill="#111" font-size="11" font-weight="600">input parecido → OK&lt;/text>
&lt;rect x="210" y="165" width="150" height="34" rx="6" fill="#f6caca" stroke="#a52a2a" stroke-width="1.4"/>&lt;text x="285" y="182" text-anchor="middle" fill="#111" font-size="11" font-weight="600">input lejano →&lt;/text>&lt;text x="285" y="195" text-anchor="middle" fill="#a52a2a" font-size="10" font-weight="600">degradación&lt;/text>
&lt;path d="M150,139 L105,165" stroke="#666" stroke-width="1.4" fill="none" marker-end="url(#ttq2)"/>
&lt;path d="M240,139 L285,165" stroke="#666" stroke-width="1.4" fill="none" marker-end="url(#ttq2)"/>
&lt;text x="195" y="225" text-anchor="middle" fill="#444" font-size="10">overhead inferencia ≈ 0 · calidad depende de la calibración&lt;/text>&lt;/p>
&lt;p>&lt;rect x="510" y="45" width="150" height="38" rx="6" fill="#d9f5d6" stroke="#2a7a40" stroke-width="1.4"/>&lt;text x="585" y="62" text-anchor="middle" fill="#111" font-size="11" font-weight="600">activaciones REALES&lt;/text>&lt;text x="585" y="77" text-anchor="middle" fill="#444" font-size="10">del tráfico actual&lt;/text>
&lt;rect x="510" y="100" width="150" height="34" rx="6" fill="#d9f5d6" stroke="#2a7a40" stroke-width="1.4"/>&lt;text x="585" y="115" text-anchor="middle" fill="#111" font-size="11" font-weight="600">medir + detectar&lt;/text>&lt;text x="585" y="128" text-anchor="middle" fill="#444" font-size="10">outliers EN CALIENTE&lt;/text>
&lt;path d="M585,83 L585,100" stroke="#666" stroke-width="1.4" fill="none" marker-end="url(#ttq2)"/>
&lt;rect x="510" y="151" width="150" height="34" rx="6" fill="#d9f5d6" stroke="#2a7a40" stroke-width="1.4"/>&lt;text x="585" y="166" text-anchor="middle" fill="#111" font-size="11" font-weight="600">escalas FRESCAS&lt;/text>&lt;text x="585" y="179" text-anchor="middle" fill="#444" font-size="10">por token / batch&lt;/text>
&lt;path d="M585,134 L585,151" stroke="#666" stroke-width="1.4" fill="none" marker-end="url(#ttq2)"/>
&lt;rect x="510" y="202" width="150" height="34" rx="6" fill="#fff5b0" stroke="#9a8400" stroke-width="1.4"/>&lt;text x="585" y="217" text-anchor="middle" fill="#111" font-size="11" font-weight="600">cuantizar + GEMM&lt;/text>&lt;text x="585" y="230" text-anchor="middle" fill="#9a6b00" font-size="10" font-weight="600">+ overhead por step&lt;/text>
&lt;path d="M585,185 L585,202" stroke="#666" stroke-width="1.4" fill="none" marker-end="url(#ttq2)"/>
&lt;text x="585" y="258" text-anchor="middle" fill="#444" font-size="10">sin corpus · calidad robusta a la distribución · overhead ≠ 0&lt;/text>
&lt;/svg>&lt;/p>
&lt;/div>
&lt;h2 id="las-matemáticas-que-importan">Las matemáticas que importan&lt;/h2>
&lt;h3 id="el-error-de-cuantizar-un-outlier-con-la-escala-equivocada">El error de cuantizar un outlier con la escala equivocada&lt;/h3>
&lt;p>Recordemos la cuantización uniforme afín del &lt;a href="https://blog.lo0.es/posts/quantization-fundamentos-inferencia/">post base&lt;/a>: un código entero &lt;code>q = round(x/s) - z&lt;/code> con escala &lt;code>s&lt;/code> y zero-point &lt;code>z&lt;/code>, y reconstrucción &lt;code>x̂ = s·(q + z)&lt;/code>. Para un cuantizador de &lt;code>b&lt;/code> bits con rango simétrico, la escala que cubre un tensor de magnitud máxima &lt;code>M&lt;/code> es aproximadamente &lt;code>s = M / (2^{b-1} - 1)&lt;/code>. El error de redondeo de cada elemento está acotado por media escala: &lt;code>|x - x̂| ≤ s/2&lt;/code>.&lt;/p>
&lt;p>Aquí está el problema del outlier. La escala &lt;code>s&lt;/code> se elige para cubrir el valor &lt;strong>más grande&lt;/strong> del grupo. Si un canal tiene magnitud 30× la mediana y compartes una sola escala con el resto del tensor, esa magnitud manda: &lt;code>M&lt;/code> es el outlier, así que &lt;code>s&lt;/code> se infla 30× respecto a lo que necesitaría la mayoría. El error absoluto de redondeo de los valores normales sube proporcionalmente.&lt;/p>
&lt;p>Cuenta concreta. Tomemos un grupo donde la mediana de magnitudes es 1.0 y un canal outlier vale 30.0, cuantizado a INT4 (&lt;code>b = 4&lt;/code>, niveles ±7):&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Con escala compartida&lt;/strong>, &lt;code>s = 30 / 7 ≈ 4.29&lt;/code>. El error de redondeo de un valor típico (magnitud ~1) es de hasta &lt;code>s/2 ≈ 2.14&lt;/code>. Es decir, &lt;strong>el error sobre los valores normales es del orden de su propio valor&lt;/strong>: el outlier ha destruido la resolución de todo lo demás. Error relativo de un valor de magnitud 1: hasta ~214 %.&lt;/li>
&lt;li>&lt;strong>Segregando el outlier&lt;/strong> (lo sacas a FP16 o le das su propia escala) y cuantizando el resto con &lt;code>M = 1&lt;/code>, &lt;code>s = 1/7 ≈ 0.143&lt;/code>. El error de un valor típico baja a &lt;code>s/2 ≈ 0.071&lt;/code>, ~7 % relativo. &lt;strong>Treinta veces menos error&lt;/strong> sobre la mayoría de los pesos del grupo.&lt;/li>
&lt;/ul>
&lt;p>Esa es toda la razón de ser de la cuantización activation-aware: &lt;strong>detectar y tratar aparte el ~1 % de canales que, de no segregarse, secuestran la escala&lt;/strong>. AWQ lo hace contra el corpus; TTQ lo hace contra el input real. Y si el canal que es outlier &lt;em>en producción&lt;/em> no era outlier &lt;em>en la calibración&lt;/em>, AWQ no lo protegió: cuantizó el tráfico real con la escala inflada del caso de arriba. Ahí TTQ gana precisión.&lt;/p>
&lt;h3 id="el-overhead-el-coste-de-medir-en-cada-step">El overhead: el coste de medir en cada step&lt;/h3>
&lt;p>El precio es simétrico. Calcular las estadísticas por token —magnitudes por canal, mediana o percentil, umbral de outlier, escalas— son reducciones sobre el tensor de activación que &lt;strong>no existían&lt;/strong> en el forward con escalas congeladas. Llamemos:&lt;/p>
&lt;ul>
&lt;li>&lt;code>T&lt;/code> = tiempo del forward por token con escalas fijas (PTQ estática), en µs.&lt;/li>
&lt;li>&lt;code>Δ&lt;/code> = coste extra por token de derivar las estadísticas y escalas en caliente, en µs.&lt;/li>
&lt;/ul>
&lt;p>El overhead relativo es simplemente:&lt;/p>
&lt;p>$$\text{overhead} = \frac{\Delta}{T}$$&lt;/p>
&lt;p>La clave es que &lt;code>Δ&lt;/code> es relativamente &lt;strong>fijo por step&lt;/strong> (depende del número de canales y capas, no de cuánto trabajo &amp;ldquo;útil&amp;rdquo; haga el modelo), mientras que &lt;code>T&lt;/code> escala con el tamaño del modelo. Por eso el cociente se comporta de forma muy distinta según el modelo:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Modelo grande&lt;/strong> (p. ej. 70B): &lt;code>T&lt;/code> es grande —cada forward mueve decenas de GB de pesos desde HBM—. Si &lt;code>Δ ≈ 8 µs&lt;/code> y &lt;code>T ≈ 800 µs&lt;/code>, el overhead es &lt;code>8/800 = 1 %&lt;/code>. Despreciable frente al ahorro de cuantizar.&lt;/li>
&lt;li>&lt;strong>SLM&lt;/strong> (p. ej. 1B): &lt;code>T&lt;/code> es pequeño —el forward por token es corto—. Con el mismo &lt;code>Δ ≈ 8 µs&lt;/code> y &lt;code>T ≈ 60 µs&lt;/code>, el overhead es &lt;code>8/60 ≈ 13 %&lt;/code>. Ya no es despreciable: se come buena parte de lo que ganaste cuantizando.&lt;/li>
&lt;/ul>
&lt;p>Esto conecta directamente con el &lt;a href="https://blog.lo0.es/posts/roofline-invertido-modelos-pequenos/">roofline invertido para modelos pequeños&lt;/a>: en SLM los &lt;strong>costes fijos por step&lt;/strong> (lanzamiento de kernels, sincronizaciones, overheads que no escalan con el modelo) pesan proporcionalmente más, porque hay menos trabajo útil entre los que repartirlos. El &lt;code>Δ&lt;/code> de TTQ es exactamente uno de esos costes fijos. Per-batch en lugar de per-token amortiza &lt;code>Δ&lt;/code> entre todos los tokens del batch y baja el overhead relativo, a costa de escalas menos finas; es el primer parámetro a tocar.&lt;/p>
&lt;p>La conclusión incómoda: TTQ regala robustez a la distribución pero &lt;strong>gasta parte del presupuesto de aceleración en medir&lt;/strong>, y en el régimen donde la aceleración más escasea —los SLM, los que más se despliegan en el edge— es donde ese gasto más duele. No es gratis; es un cambio de moneda.&lt;/p>
&lt;blockquote>
&lt;p>Nota de escepticismo metodológico: arXiv:2603.19296 es de &lt;strong>marzo de 2026&lt;/strong>, muy reciente, y a la fecha de este post no hay reproducciones independientes amplias. Las cifras de speedup y de calidad que circulen conviene tomarlas con la misma cautela que cualquier número sin metodología publicada: ¿qué hardware, qué tamaño de batch, qué &lt;code>Δ&lt;/code> real medido, contra qué baseline (PTQ bien calibrada o mal calibrada), en qué dominio? El argumento &lt;em>conceptual&lt;/em> —robustez a la distribución a cambio de overhead por step— es sólido; los multiplicadores concretos, pendientes de validación.&lt;/p>
&lt;/blockquote>
&lt;h2 id="qué-no-es-ttq-deslindando-del-resto-del-zoo">Qué NO es TTQ: deslindando del resto del zoo&lt;/h2>
&lt;p>TTQ se confunde fácilmente con técnicas vecinas. La distinción que importa es que &lt;strong>TTQ es el &lt;em>cómo&lt;/em> derivas las escalas, no el formato ni el momento del entrenamiento&lt;/strong>.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Técnica&lt;/th>
&lt;th>Cuándo se fijan las escalas&lt;/th>
&lt;th>Necesita corpus calibración&lt;/th>
&lt;th>Toca entrenamiento&lt;/th>
&lt;th>Es un formato&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>PTQ estática&lt;/strong> (GPTQ, AWQ)&lt;/td>
&lt;td>Offline, antes de desplegar&lt;/td>
&lt;td>Sí&lt;/td>
&lt;td>No&lt;/td>
&lt;td>No (usa INT4/INT8)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>QAT&lt;/strong>&lt;/td>
&lt;td>Durante el entrenamiento&lt;/td>
&lt;td>No (datos de train)&lt;/td>
&lt;td>Sí (re-entrena)&lt;/td>
&lt;td>No&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>FP8 end-to-end&lt;/strong>&lt;/td>
&lt;td>En runtime, pero escalas simples por tensor&lt;/td>
&lt;td>Mínimo / ninguno&lt;/td>
&lt;td>No&lt;/td>
&lt;td>&lt;strong>Sí&lt;/strong> (E4M3/E5M2)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>TTQ&lt;/strong>&lt;/td>
&lt;td>En runtime, activation-aware por token/batch&lt;/td>
&lt;td>&lt;strong>No&lt;/strong>&lt;/td>
&lt;td>No&lt;/td>
&lt;td>No (ortogonal al formato)&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Las cuatro distinciones, una a una:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Frente a PTQ estática (GPTQ/AWQ).&lt;/strong> Misma meta (proteger outliers), mismo formato posible (INT4), pero PTQ congela las decisiones offline contra un corpus y TTQ las recalcula en caliente. TTQ es, en cierto sentido, &amp;ldquo;AWQ sin la fase de calibración, pagada en runtime&amp;rdquo;.&lt;/li>
&lt;li>&lt;strong>Frente a QAT.&lt;/strong> QAT mete la cuantización dentro del bucle de entrenamiento para que el modelo aprenda a ser robusto a ella; cuesta re-entrenar. TTQ no toca el entrenamiento: opera sobre un modelo ya entrenado, en inferencia. Son ataques en momentos opuestos del pipeline.&lt;/li>
&lt;li>&lt;strong>Frente a FP8 end-to-end.&lt;/strong> FP8 es un &lt;strong>formato&lt;/strong> con su propio rango logarítmico; su &amp;ldquo;dynamic scaling&amp;rdquo; calcula un escalar simple por tensor en runtime, pero no hace detección activation-aware de outliers por canal. TTQ podría, conceptualmente, derivar escalas en caliente &lt;em>para&lt;/em> un cuantizador FP8 o INT4: es ortogonal al formato.&lt;/li>
&lt;li>&lt;strong>TTQ es ortogonal al formato.&lt;/strong> Decide &lt;em>cómo&lt;/em> obtener &lt;code>s&lt;/code>, no en cuántos bits guardas &lt;code>q&lt;/code>. Puedes imaginar &amp;ldquo;TTQ sobre INT4&amp;rdquo; o &amp;ldquo;TTQ sobre FP8&amp;rdquo;. Lo que define a TTQ es la fuente de la escala —activaciones reales en caliente— no el ancho del código.&lt;/li>
&lt;/ul>
&lt;h2 id="cuándo-compensa-y-cuándo-no">Cuándo compensa (y cuándo no)&lt;/h2>
&lt;p>TTQ no es un reemplazo universal de AWQ. Es una herramienta para un perfil concreto de despliegue. &lt;strong>Compensa cuando:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>No tienes pipeline de calibración.&lt;/strong> Quieres desplegar un modelo cuantizado &lt;em>ya&lt;/em>, sin montar el dataset de calibración, ejecutar el pase offline ni validar que el corpus representa el tráfico. TTQ recorta esa fase entera: cargas el modelo y sirves.&lt;/li>
&lt;li>&lt;strong>La distribución del tráfico es cambiante o desconocida.&lt;/strong> Un asistente que un día recibe código y otro día contratos legales en otro idioma. Ninguna calibración fija cubre bien ambos; la adaptación en caliente sigue la distribución sin re-calibrar.&lt;/li>
&lt;li>&lt;strong>Multitenant sin corpus representativo.&lt;/strong> Sirves el mismo modelo a clientes con dominios dispares. No existe un corpus único que represente a todos; cualquier calibración fija crea ganadores y perdedores entre tenants. TTQ ajusta a cada input, sea del tenant que sea.&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>No compensa cuando:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Tienes un dominio estable y un buen corpus de calibración.&lt;/strong> Si tu tráfico es homogéneo y representativo, AWQ offline te da la misma calidad con &lt;strong>cero overhead en runtime&lt;/strong>. Pagar &lt;code>Δ&lt;/code> en cada token para reaprender lo que un corpus ya capturó es desperdicio.&lt;/li>
&lt;li>&lt;strong>Sirves SLM con SLA de latencia ajustado.&lt;/strong> Es justo el caso donde &lt;code>Δ/T&lt;/code> es alto. Si el modelo es pequeño y el TPOT importa, el overhead de medir puede borrar la ganancia de cuantizar. Mide tu &lt;code>Δ&lt;/code> real antes de asumir que sale a cuenta.&lt;/li>
&lt;li>&lt;strong>El batch es grande y compute-bound.&lt;/strong> Con concurrencia alta el forward ya no está memory-bound y el coste de las reducciones extra compite peor; conviene al menos amortizar &lt;code>Δ&lt;/code> per-batch.&lt;/li>
&lt;/ul>
&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 caso natural de la 4090 es el SLM —Qwen 3 1.5B, Llama 3 8B AWQ-INT4— sirviendo a baja concurrencia. Es precisamente el régimen donde TTQ es más arriesgado: &lt;code>T&lt;/code> por token es pequeño y la 4090 no tiene FP8 nativo acelerado (lo discutimos en el &lt;a href="https://blog.lo0.es/posts/quantization-fundamentos-inferencia/">post de quantization&lt;/a>), así que las reducciones extra de TTQ corren en CUDA cores compitiendo por el mismo tiempo. Aquí la pregunta no es &amp;ldquo;¿mejora la calidad?&amp;rdquo; sino &amp;ldquo;¿el overhead me deja un TPOT aceptable?&amp;rdquo;. Si el tráfico es homogéneo, AWQ offline gana por simplicidad y latencia. TTQ solo justifica su &lt;code>Δ&lt;/code> si la distribución de inputs es genuinamente impredecible y la degradación de la calibración fija es medible.&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í el cálculo se invierte parcialmente. Con modelos grandes &lt;code>T&lt;/code> es alto y el &lt;code>Δ/T&lt;/code> baja a la zona de pocos puntos porcentuales, así que el overhead de TTQ es más digerible. El caso de uso fuerte es el &lt;strong>multitenant&lt;/strong>: un cluster que sirve un modelo grande a clientes con dominios heterogéneos, donde no hay un corpus de calibración que contente a todos. Ahí la robustez a la distribución de TTQ tiene valor real y el overhead se diluye en un forward grande. Aun así, sobre H100 con FP8 nativo, el baseline a batir es exigente: FP8 estático casi no pierde calidad (ver tabla del post de quantization) y no cuesta nada en runtime. TTQ tiene que demostrar que su ganancia de robustez en los tenants outlier supera lo que regala en overhead. Con un paper de marzo de 2026 y sin reproducciones, esa demostración está pendiente.&lt;/p>
&lt;h2 id="lo-que-no-hemos-cubierto">Lo que no hemos cubierto&lt;/h2>
&lt;ul>
&lt;li>&lt;strong>El coste de memoria de las estadísticas en caliente&lt;/strong>: buffers por canal, su impacto en el footprint y en la presión de cache.&lt;/li>
&lt;li>&lt;strong>Interacción con continuous batching&lt;/strong>: cómo se derivan escalas cuando un batch mezcla requests de dominios distintos en el mismo step.&lt;/li>
&lt;li>&lt;strong>TTQ + speculative decoding&lt;/strong>: si el draft y el target derivan escalas en caliente por separado, y cómo afecta eso a la tasa de aceptación.&lt;/li>
&lt;li>&lt;strong>Estabilidad numérica&lt;/strong>: qué pasa cuando un batch tiene un outlier extremo puntual que infla la escala de todos los tokens de ese step.&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/quantization-fundamentos-inferencia/">Quantization para inferencia LLM&lt;/a> — la base imprescindible: scale + zero-point, GPTQ, AWQ y por qué los outliers de activación son el problema; TTQ es AWQ con las escalas derivadas en caliente en vez de offline.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/roofline-invertido-modelos-pequenos/">Roofline invertido para modelos pequeños&lt;/a> — por qué los costes fijos por step pesan más en SLM; explica directamente por qué el overhead &lt;code>Δ&lt;/code> de TTQ duele más en modelos pequeños.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/cuantizacion-agresiva-sub-4-bit-ternario/">Cuantización agresiva sub-4-bit y ternario&lt;/a> — la frontera estática por debajo de 4 bits; complementa a TTQ, que ataca el &lt;em>cómo&lt;/em> de la escala en vez del &lt;em>cuántos bits&lt;/em>.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/qlora-multi-lora-agresivo-slm/">QLoRA y multi-LoRA agresivo en SLM&lt;/a> — adapters sobre un base cuantizado; el base podría derivar escalas en caliente mientras los adapters van en BF16.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/fp8-end-to-end-pesos-kv-calidad/">FP8 end-to-end: pesos, KV y calidad&lt;/a> — el formato del datacenter Hopper/Blackwell; TTQ es ortogonal y podría derivar escalas para un cuantizador FP8.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/kv-cache-fundamentos/">KV cache: la memoria de trabajo de la inferencia LLM&lt;/a> — el KV cache también se cuantiza; sus escalas son otro candidato a derivarse en caliente por la misma lógica.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/knowledge-distillation-fundamentos/">Knowledge distillation&lt;/a> — la otra vía para servir modelos pequeños robustos; destilar reduce el modelo, TTQ ajusta su cuantización al tráfico.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/decode-optimizaciones-vllm/">Optimizando el decode en vLLM&lt;/a> — donde se materializan en parámetros las palancas de cuantización en runtime para exprimir una 4090.&lt;/li>
&lt;/ul>
&lt;h2 id="referencias">Referencias&lt;/h2>
&lt;ul>
&lt;li>&lt;em>TTQ: Activation-Aware Test-Time Quantization to Accelerate LLM Inference On The Fly&lt;/em> (marzo 2026). &lt;a href="https://arxiv.org/abs/2603.19296">https://arxiv.org/abs/2603.19296&lt;/a>&lt;/li>
&lt;li>Lin, J., Tang, J., Tang, H., Yang, S., Dang, X., Han, S. &lt;em>AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration&lt;/em> (MLSys 2024). &lt;a href="https://arxiv.org/abs/2306.00978">https://arxiv.org/abs/2306.00978&lt;/a>&lt;/li>
&lt;li>Frantar, E., Ashkboos, S., Hoefler, T., Alistarh, D. &lt;em>GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers&lt;/em> (ICLR 2023). &lt;a href="https://arxiv.org/abs/2210.17323">https://arxiv.org/abs/2210.17323&lt;/a>&lt;/li>
&lt;li>Xiao, G., Lin, J., Seznec, M., Wu, H., Demouth, J., Han, S. &lt;em>SmoothQuant: Accurate and Efficient Post-Training Quantization for Large Language Models&lt;/em> (ICML 2023). &lt;a href="https://arxiv.org/abs/2211.10438">https://arxiv.org/abs/2211.10438&lt;/a>&lt;/li>
&lt;li>Dettmers, T., Lewis, M., Belkada, Y., Zettlemoyer, L. &lt;em>LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale&lt;/em> (NeurIPS 2022). &lt;a href="https://arxiv.org/abs/2208.07339">https://arxiv.org/abs/2208.07339&lt;/a>&lt;/li>
&lt;/ul></description></item></channel></rss>