<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Quip on lo0 — Blog Técnico</title><link>https://blog.lo0.es/tags/quip/</link><description>Recent content in Quip on lo0 — Blog Técnico</description><generator>Hugo -- gohugo.io</generator><language>es</language><lastBuildDate>Tue, 09 Jun 2026 02:10:00 +0000</lastBuildDate><atom:link href="https://blog.lo0.es/tags/quip/index.xml" rel="self" type="application/rss+xml"/><item><title>Cuantización agresiva (estado del arte): del 4-bit al ternario</title><link>https://blog.lo0.es/posts/cuantizacion-agresiva-sub-4-bit-ternario/</link><pubDate>Tue, 09 Jun 2026 02:10:00 +0000</pubDate><guid>https://blog.lo0.es/posts/cuantizacion-agresiva-sub-4-bit-ternario/</guid><description>&lt;blockquote>
&lt;p>Este post es la continuación directa de &lt;a href="https://blog.lo0.es/posts/quantization-fundamentos-inferencia/">Quantization para inferencia LLM&lt;/a>, que cubre el régimen &amp;ldquo;resuelto&amp;rdquo; (FP8, INT4 con GPTQ/AWQ). Léelo primero: aquí asumo la matemática del scale+zero-point, qué hacen GPTQ y AWQ, y la distinción PTQ/QAT. Lo que añadimos es la &lt;strong>frontera sub-4-bit&lt;/strong>, donde la cuantización post-hoc escalar deja de funcionar y hay que cambiar de herramienta.&lt;/p>
&lt;/blockquote>
&lt;h2 id="tldr">TL;DR&lt;/h2>
&lt;p>Hay una línea divisoria nítida alrededor de los 4 bits. Por encima, cuantizar es un problema &lt;strong>resuelto&lt;/strong>: INT8 es indistinguible de BF16, e INT4 con un método bueno (AWQ, GPTQ) pierde 1-2 puntos de MMLU y poco más. El método sigue siendo el mismo de siempre —tomar cada peso, escalarlo, redondearlo a un entero corto— y funciona. Por debajo de 4 bits, ese método &lt;strong>colapsa&lt;/strong>: a 2 bits la cuantización escalar ingenua puede duplicar la perplexity. La razón es geométrica —cada peso tiene solo 4 valores posibles, el error de redondeo deja de ser despreciable— y la salida no es &amp;ldquo;redondear mejor&amp;rdquo;, es &lt;strong>cambiar de representación&lt;/strong>. Los métodos SOTA de 2 bits (AQLM, QuIP#, QTIP) dejan de cuantizar pesos individuales y cuantizan &lt;strong>vectores&lt;/strong> de pesos contra diccionarios (códigos), y &amp;ldquo;blanquean&amp;rdquo; la matriz de pesos para repartir su energía y aplastar outliers (incoherence processing). El ternario es otra cosa todavía: BitNet b1.58, con pesos en {-1, 0, +1} (~1.58 bits), &lt;strong>no es PTQ&lt;/strong> —es un modelo entrenado nativamente con esa restricción— y cambia la aritmética de la matmul de multiplicaciones a sumas/restas, tocando a la vez el techo de cómputo y el de memoria. La regla mental: ≥4-bit comprimes la foto; &amp;lt;4-bit tienes que repintarla.&lt;/p>
&lt;h2 id="la-analogía-el-jpeg-que-ya-no-se-puede-comprimir-más">La analogía: el JPEG que ya no se puede comprimir más&lt;/h2>
&lt;p>En &lt;a href="https://blog.lo0.es/posts/quantization-fundamentos-inferencia/">el post de quantization&lt;/a> usamos el JPEG con detector de bordes para explicar INT4. Aquí la analogía sigue, pero hay que llevarla hasta su límite.&lt;/p>
&lt;p>Un JPEG con factor de calidad 90 es indistinguible del original. A calidad 60 ya se nota un poco, pero sigue siendo &amp;ldquo;la misma foto&amp;rdquo;. A calidad 30 aparecen los bloques 8×8, los halos alrededor de los bordes, el banding en los degradados. A calidad 10 la imagen está destruida: reconoces que &lt;strong>había&lt;/strong> una cara, pero los detalles han desaparecido bajo los artefactos. Y aquí está la clave: &lt;strong>no existe ningún encoder JPEG que comprima a calidad 10 sin esos artefactos&lt;/strong>, porque el algoritmo JPEG (DCT por bloques + cuantización de coeficientes) tiene un suelo de información por debajo del cual su propio mecanismo introduce el ruido.&lt;/p>
&lt;p>¿Qué haces si necesitas la foto a ese tamaño de archivo y que se siga viendo bien? No comprimes más la original. &lt;strong>Repintas la foto sabiendo de antemano que va a vivir comprimida&lt;/strong>: un ilustrador la redibuja con líneas limpias, paleta reducida, cero degradados sutiles —una imagen diseñada para sobrevivir a la compresión brutal—. El resultado a &amp;ldquo;10 KB&amp;rdquo; se ve infinitamente mejor que el JPEG original aplastado a 10 KB, porque no es el mismo proceso: uno destruye información existente, el otro genera información nueva ya adaptada a la restricción.&lt;/p>
&lt;p>Esa es exactamente la frontera de este post:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>PTQ escalar (≥4-bit)&lt;/strong> = comprimir el JPEG. Hasta cierto ratio, sigue siendo la misma foto.&lt;/li>
&lt;li>&lt;strong>PTQ vectorial SOTA (2-bit: AQLM, QuIP#, QTIP)&lt;/strong> = un códec de imagen mucho más sofisticado (diccionarios, transformadas que decorrelacionan) que estira el ratio comprimible un poco más antes del colapso.&lt;/li>
&lt;li>&lt;strong>Ternario nativo (BitNet b1.58)&lt;/strong> = repintar la foto. No comprimes un modelo BF16 existente; entrenas uno nuevo que nace ternario.&lt;/li>
&lt;/ul>
&lt;h2 id="el-mapa-de-la-frontera-bit-a-bit">El mapa de la frontera, bit a bit&lt;/h2>
&lt;p>Cuantizar un modelo es decidir cuántos valores distintos puede tomar cada peso. Con &lt;code>b&lt;/code> bits por peso hay &lt;code>2^b&lt;/code> valores posibles. La pregunta central es: ¿a partir de qué &lt;code>b&lt;/code> el número de valores es tan pequeño que el redondeo destruye el modelo?&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Bits&lt;/th>
&lt;th>Valores/peso&lt;/th>
&lt;th>Estado del arte&lt;/th>
&lt;th>Método necesario&lt;/th>
&lt;th>Pérdida típica vs BF16&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>8&lt;/td>
&lt;td>256&lt;/td>
&lt;td>&lt;strong>Resuelto&lt;/strong>&lt;/td>
&lt;td>RTN, SmoothQuant, FP8&lt;/td>
&lt;td>~0 (indistinguible)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>4&lt;/td>
&lt;td>16&lt;/td>
&lt;td>&lt;strong>Resuelto&lt;/strong>&lt;/td>
&lt;td>AWQ, GPTQ&lt;/td>
&lt;td>1-2 pp MMLU, +0.1-0.3 PPL&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>3&lt;/td>
&lt;td>8&lt;/td>
&lt;td>Degradación pequeña&lt;/td>
&lt;td>GPTQ/AWQ tuneado, GGUF Q3_K&lt;/td>
&lt;td>3-5 pp MMLU&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>2&lt;/td>
&lt;td>4&lt;/td>
&lt;td>Serio salvo SOTA&lt;/td>
&lt;td>&lt;strong>AQLM, QuIP#, QTIP&lt;/strong> (no escalar)&lt;/td>
&lt;td>escalar: colapso; SOTA: 4-8 pp&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>1.58&lt;/td>
&lt;td>3 (ternario)&lt;/td>
&lt;td>Solo nativo&lt;/td>
&lt;td>&lt;strong>BitNet b1.58&lt;/strong> (QAT/entrenamiento nativo)&lt;/td>
&lt;td>n/a (no es PTQ)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>1&lt;/td>
&lt;td>2 (binario)&lt;/td>
&lt;td>Investigación&lt;/td>
&lt;td>nativo, claims dudosos&lt;/td>
&lt;td>grande / sin metodología clara&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Las tres transiciones que importan:&lt;/p>
&lt;p>&lt;strong>8 → 4 bits: nada se rompe.&lt;/strong> Con 16 niveles por peso y un scale por bloque de 128, el error de redondeo es pequeño relativo a la dinámica de los pesos. GPTQ compensa el error propagándolo a los pesos vecinos; AWQ protege el ~1 % de canales salientes. El modelo casi no lo nota. Esto está en el post anterior.&lt;/p>
&lt;p>&lt;strong>4 → 2 bits: el codo.&lt;/strong> Aquí pasan dos cosas a la vez. Primero, con solo 4 niveles, el cuantizador escalar ya no puede representar la distribución de pesos —que es aproximadamente gaussiana con colas largas— sin un error de redondeo enorme en proporción. Segundo, y más sutil: el error de cuantización deja de ser &amp;ldquo;ruido pequeño que el modelo absorbe&amp;rdquo; y se vuelve &lt;strong>estructurado&lt;/strong>, sesgando sistemáticamente las activaciones. La PTQ escalar ingenua a 2 bits sobre un Llama 8B típicamente &lt;strong>duplica la perplexity o más&lt;/strong>. Es el codo de la curva.&lt;/p>
&lt;p>&lt;strong>2 → 1.58 bits: cambio de naturaleza.&lt;/strong> No se cruza con un método de compresión mejor. Se cruza entrenando el modelo desde el principio con la restricción. Es una discontinuidad: a la izquierda estás haciendo PTQ, a la derecha estás haciendo entrenamiento.&lt;/p>
&lt;h2 id="por-qué-la-ptq-escalar-colapsa-por-debajo-de-4-bits">Por qué la PTQ escalar colapsa por debajo de 4 bits&lt;/h2>
&lt;p>El cuantizador escalar tiene una limitación de fondo: cuantiza &lt;strong>cada peso por separado&lt;/strong>, ignorando que los pesos de una fila/columna están correlacionados y que el error de uno se podría compensar con otro. A 4 bits esto importa poco; a 2 bits es letal. Hay tres ataques posibles, y los métodos SOTA usan los tres.&lt;/p>
&lt;h3 id="1-cuantización-vectorial-diccionarios-en-lugar-de-escalas">1. Cuantización vectorial: diccionarios en lugar de escalas&lt;/h3>
&lt;p>En lugar de mapear cada peso a uno de 4 valores, agrupa los pesos en &lt;strong>vectores&lt;/strong> (p. ej. de 8 pesos) y mapea cada vector al entrada más cercana de un &lt;strong>diccionario&lt;/strong> (codebook) aprendido. Si el diccionario tiene 256 entradas, codificar un vector de 8 pesos cuesta 8 bits (el índice) → 1 bit/peso, pero cada &amp;ldquo;valor reconstruido&amp;rdquo; es un punto en un espacio de 8 dimensiones elegido para minimizar el error sobre la distribución real de pesos.&lt;/p>
&lt;p>La ventaja es de teoría de la información: un diccionario de vectores puede colocar sus puntos de reconstrucción donde &lt;strong>realmente&lt;/strong> están los pesos (en racimos), mientras que el cuantizador escalar está obligado a poner sus 4 niveles en una rejilla regular, gastando resolución en zonas vacías. Es la diferencia entre un mapa de carreteras con cuadrícula uniforme y uno que pone más detalle donde hay ciudades.&lt;/p>
&lt;p>&lt;strong>AQLM&lt;/strong> (Additive Quantization of Language Models, arXiv:2401.06118) lleva esto al extremo con &lt;strong>cuantización aditiva&lt;/strong>: cada vector de pesos se reconstruye como &lt;strong>suma de varios códigos&lt;/strong> de varios diccionarios (multi-codebook). Es más expresivo que un solo diccionario porque el número de combinaciones es el producto de los tamaños, no la suma. AQLM fue uno de los primeros métodos en hacer 2-bit &amp;ldquo;usable&amp;rdquo; (no colapsado) en modelos grandes, a costa de un proceso de calibración caro y kernels de inferencia especializados.&lt;/p>
&lt;h3 id="2-incoherence-processing-blanquear-la-matriz">2. Incoherence processing: blanquear la matriz&lt;/h3>
&lt;p>El segundo ataque es contra los &lt;strong>outliers&lt;/strong>. Las matrices de pesos de un transformer tienen unas pocas entradas (y unos pocos canales) con magnitud mucho mayor que el resto. Esos outliers dominan el rango del cuantizador: si tienes que representar un peso de magnitud 8 y el resto son de magnitud 0.5, tu scale se estira para cubrir el 8 y desperdicias casi toda la resolución.&lt;/p>
&lt;p>&lt;strong>Incoherence processing&lt;/strong> (la idea central de QuIP y QuIP#) ataca esto multiplicando la matriz de pesos &lt;code>W&lt;/code> por matrices ortogonales aleatorias por la izquierda y la derecha: &lt;code>W' = U W V^T&lt;/code>. Como &lt;code>U&lt;/code> y &lt;code>V&lt;/code> son ortogonales, la operación es invertible y la matemática del producto se puede deshacer en inferencia absorbiéndola en las capas vecinas (igual que AWQ absorbe sus escalas). Pero la rotación &lt;strong>reparte la energía&lt;/strong>: una matriz &amp;ldquo;incoherente&amp;rdquo; tiene sus valores repartidos de forma casi uniforme, sin outliers concentrados, porque mezclar coordenadas con una rotación aleatoria aplana la distribución (es, en esencia, el teorema central del límite actuando sobre combinaciones lineales). Una matriz sin outliers se cuantiza muchísimo mejor a 2 bits. Es el equivalente a &amp;ldquo;blanquear&amp;rdquo; una señal antes de digitalizarla.&lt;/p>
&lt;p>&lt;strong>QuIP#&lt;/strong> (arXiv:2402.04396) combina incoherence processing con &lt;strong>códigos reticulares E8&lt;/strong>: en vez de un diccionario arbitrario, usa el retículo E8 (un empaquetamiento de esferas óptimo en 8 dimensiones, el mejor conocido). Cuantizar vectores de 8 pesos contra el retículo E8 da el menor error de reconstrucción posible para una densidad de bits dada, porque E8 es literalmente la forma más eficiente de colocar puntos en 8D. Es teoría de codificación clásica aplicada a pesos de LLM.&lt;/p>
&lt;h3 id="3-codificación-con-memoria-trellis">3. Codificación con memoria: trellis&lt;/h3>
&lt;p>&lt;strong>QTIP&lt;/strong> (arXiv:2406.11235) añade el tercer ataque: &lt;strong>trellis-coded quantization&lt;/strong>. En lugar de cuantizar cada vector de forma independiente, modela la secuencia de pesos como un camino a través de un trellis (la misma estructura de los códigos convolucionales de las telecomunicaciones) y elige la secuencia de códigos óptima con el algoritmo de Viterbi. La intuición: introducir &lt;strong>memoria&lt;/strong> entre cuantizaciones sucesivas permite errores correlacionados que se cancelan, en vez de errores independientes que se acumulan. QTIP, sobre incoherence processing, mejora a QuIP# en calidad a 2-3 bits manteniendo kernels de inferencia rápidos.&lt;/p>
&lt;p>La idea común a los tres: &lt;strong>dejar de cuantizar escalares y empezar a cuantizar vectores con diccionarios, y decorrelacionar la matriz antes de hacerlo&lt;/strong>. Ninguno es &amp;ldquo;redondear mejor&amp;rdquo;; los tres cambian la representación de raíz. Por eso, por debajo de 4 bits, ya no basta con un flag en vLLM: hace falta co-diseño de método de cuantización + kernel de inferencia.&lt;/p>
&lt;h2 id="el-ternario-nativo-bitnet-b158">El ternario nativo: BitNet b1.58&lt;/h2>
&lt;p>Aquí cambiamos de continente. Todo lo anterior es &lt;strong>PTQ&lt;/strong>: parte de un modelo BF16 entrenado y lo comprime. El ternario de BitNet no comprime nada.&lt;/p>
&lt;p>&lt;strong>BitNet b1.58&lt;/strong> (arXiv:2402.17764) entrena un transformer desde cero donde &lt;strong>cada peso está restringido a {-1, 0, +1}&lt;/strong> durante todo el entrenamiento. Tres valores ⇒ log₂(3) ≈ &lt;strong>1.58 bits/peso&lt;/strong>. La cuantización no es un paso posterior: las capas lineales (&lt;code>BitLinear&lt;/code>) cuantizan sus pesos a ternario en el forward pass de cada step de entrenamiento, y los gradientes fluyen a través de un estimador straight-through. El modelo &lt;strong>aprende a funcionar con pesos ternarios&lt;/strong>. Esto es QAT llevado al extremo: no un fine-tune corto de robustez, sino la restricción presente desde el primer token de entrenamiento.&lt;/p>
&lt;p>Esa diferencia es la que esquiva el codo de la curva. La PTQ a 2 bits intenta encontrar la mejor aproximación ternaria/quaternaria de un modelo que se entrenó esperando precisión completa —y ese modelo tiene pesos &amp;ldquo;frágiles&amp;rdquo; que dependen de matices que 2 bits no capturan—. BitNet, en cambio, nunca tuvo esos matices: sus pesos nacieron ternarios, así que la red distribuyó su capacidad representacional de forma compatible con la restricción. Es repintar la foto en vez de comprimirla.&lt;/p>
&lt;h3 id="lo-que-cambia-no-es-solo-la-memoria-es-la-aritmética">Lo que cambia no es solo la memoria, es la aritmética&lt;/h3>
&lt;p>El punto que más se subestima de BitNet: con pesos en {-1, 0, +1}, &lt;strong>la multiplicación desaparece de la matmul&lt;/strong>. Multiplicar una activación &lt;code>x&lt;/code> por un peso ternario &lt;code>w&lt;/code> es trivial: si &lt;code>w = +1&lt;/code> sumas &lt;code>x&lt;/code>, si &lt;code>w = -1&lt;/code> restas &lt;code>x&lt;/code>, si &lt;code>w = 0&lt;/code> no haces nada. La operación dominante de un transformer —el producto matriz-vector— pasa de ser un mar de multiplica-acumula (MAC) en coma flotante a ser &lt;strong>sumas y restas enteras&lt;/strong>.&lt;/p>
&lt;p>Esto importa porque conecta con el roofline. Como se explica en &lt;a href="https://blog.lo0.es/posts/roofline-invertido-modelos-pequenos/">El roofline invertido de los modelos pequeños&lt;/a>, la inferencia LLM tiene dos techos: el de &lt;strong>memoria&lt;/strong> (ancho de banda HBM para cargar pesos) y el de &lt;strong>cómputo&lt;/strong> (FLOPs de las tensor cores). La cuantización normal (INT4, FP8) ataca &lt;strong>solo el techo de memoria&lt;/strong>: el peso ocupa menos, pero para multiplicarlo lo descuantizas a FP16 y haces la misma multiplicación de siempre. El ternario ataca &lt;strong>ambos techos a la vez&lt;/strong>: el peso ocupa 1.58 bits (memoria) &lt;strong>y&lt;/strong> la operación es una suma en lugar de una multiplicación (cómputo). Por eso BitNet necesita kernels propios —&lt;strong>bitnet.cpp&lt;/strong>— que ejecutan la matmul ternaria sin pasar nunca por FP16; un kernel que descuantizara a FP16 para multiplicar tiraría a la basura la mitad de la ventaja.&lt;/p>
&lt;p>La contrapartida honesta: BitNet b1.58 es entrenamiento desde cero. No puedes &amp;ldquo;convertir tu Llama 8B a BitNet&amp;rdquo;. Si quieres ternario, entrenas (o usas) un modelo nativamente ternario, con todo lo que implica en coste de pre-entrenamiento y en disponibilidad de pesos. Hoy es una línea de investigación con modelos publicados a escalas modestas, no un drop-in para reemplazar tu serving actual.&lt;/p>
&lt;h2 id="qat-como-puente-entre-ptq-y-nativo">QAT como puente entre PTQ y nativo&lt;/h2>
&lt;p>Entre &amp;ldquo;comprimir post-hoc&amp;rdquo; (PTQ) y &amp;ldquo;entrenar nativamente ternario&amp;rdquo; (BitNet) hay un punto intermedio: &lt;strong>QAT&lt;/strong> (Quantization-Aware Training). Tomas un modelo ya entrenado y haces un fine-tune corto &lt;strong>con las operaciones de cuantización dentro del bucle&lt;/strong>, para que aprenda a ser robusto a bits bajos sin pagar un pre-entrenamiento completo.&lt;/p>
&lt;p>&lt;strong>Gemma 3&lt;/strong> publica variantes &lt;strong>QAT&lt;/strong> oficiales precisamente para esto: modelos que, tras el fine-tune QAT, sostienen INT4 con una pérdida de calidad mucho menor que la PTQ pura sobre el mismo modelo. El coste es de entrenamiento (horas-días de GPU sobre un modelo ya existente), no de inferencia. Para INT4 con QAT recuperas casi toda la calidad; para 2-bit, QAT ayuda pero sigue siendo terreno difícil; para ternario, el QAT deja de ser &amp;ldquo;fine-tune corto&amp;rdquo; y se convierte en entrenamiento nativo (BitNet).&lt;/p>
&lt;p>La jerarquía de decisión:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>PTQ&lt;/strong> = default a ≥4 bits. Minutos-horas, sin tocar pesos de entrenamiento. Cubre el 90 % de producción.&lt;/li>
&lt;li>&lt;strong>QAT&lt;/strong> = cuando PTQ pierde demasiado y la diferencia importa. Bits bajos (2-3), o modelos sensibles. Pagas fine-tune.&lt;/li>
&lt;li>&lt;strong>Nativo (ternario)&lt;/strong> = cuando quieres bajar de 2 bits &lt;strong>y&lt;/strong> cambiar la aritmética. Pagas pre-entrenamiento. Solo tiene sentido si controlas el modelo desde su creación.&lt;/li>
&lt;/ul>
&lt;h2 id="las-matemáticas-que-importan-footprint-y-cuántos-caben">Las matemáticas que importan: footprint y cuántos caben&lt;/h2>
&lt;p>El footprint de los pesos es directo: &lt;code>bytes = (bits/param / 8) × N&lt;/code>, con &lt;code>N&lt;/code> el número de parámetros. Para un modelo de 8B:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Nivel&lt;/th>
&lt;th>bits/param&lt;/th>
&lt;th>Footprint 8B&lt;/th>
&lt;th>Ratio vs BF16&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>BF16&lt;/td>
&lt;td>16&lt;/td>
&lt;td>16.0 GB&lt;/td>
&lt;td>1.0×&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>INT8&lt;/td>
&lt;td>8&lt;/td>
&lt;td>8.0 GB&lt;/td>
&lt;td>2.0×&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>INT4&lt;/td>
&lt;td>4&lt;/td>
&lt;td>4.0 GB&lt;/td>
&lt;td>4.0×&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>3-bit&lt;/td>
&lt;td>3&lt;/td>
&lt;td>3.0 GB&lt;/td>
&lt;td>5.3×&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>2-bit&lt;/td>
&lt;td>2&lt;/td>
&lt;td>2.0 GB&lt;/td>
&lt;td>8.0×&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>1.58-bit (ternario)&lt;/td>
&lt;td>~1.58&lt;/td>
&lt;td>~1.6 GB&lt;/td>
&lt;td>~10×&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>(El ternario real ocupa algo más de 1.58 bits/param porque hay que empaquetar 5 valores ternarios en 8 bits —5 × log₂(3) ≈ 7.92 bits— y porque las normas y embeddings suelen quedarse en más precisión. La cifra de ~1.6 GB para 8B es el orden de magnitud correcto.)&lt;/p>
&lt;h3 id="cuántos-modelos-de-8b-caben-en-una-rtx-4090">¿Cuántos modelos de 8B caben en una RTX 4090?&lt;/h3>
&lt;p>Una &lt;strong>RTX 4090 (24 GB, Ada Lovelace)&lt;/strong> tiene 24 GB. Reservamos ~4 GB para KV cache y activaciones, dejando &lt;strong>20 GB&lt;/strong> para pesos. Cuántos modelos de 8B distintos caben cargados simultáneamente:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Nivel&lt;/th>
&lt;th>Footprint 8B&lt;/th>
&lt;th>Modelos en 20 GB&lt;/th>
&lt;th>Comentario&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>BF16&lt;/td>
&lt;td>16.0 GB&lt;/td>
&lt;td>&lt;strong>1&lt;/strong>&lt;/td>
&lt;td>uno y queda margen escaso&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>INT8&lt;/td>
&lt;td>8.0 GB&lt;/td>
&lt;td>&lt;strong>2&lt;/strong>&lt;/td>
&lt;td>dos modelos distintos&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>INT4&lt;/td>
&lt;td>4.0 GB&lt;/td>
&lt;td>&lt;strong>5&lt;/strong>&lt;/td>
&lt;td>régimen resuelto; calidad ~lossless con AWQ&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>3-bit&lt;/td>
&lt;td>3.0 GB&lt;/td>
&lt;td>&lt;strong>6&lt;/strong>&lt;/td>
&lt;td>degradación pequeña ya visible&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>2-bit&lt;/td>
&lt;td>2.0 GB&lt;/td>
&lt;td>&lt;strong>10&lt;/strong>&lt;/td>
&lt;td>solo viable con AQLM/QuIP#/QTIP&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>1.58-bit&lt;/td>
&lt;td>~1.6 GB&lt;/td>
&lt;td>&lt;strong>~12&lt;/strong>&lt;/td>
&lt;td>solo modelos nativamente ternarios&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>La cuenta es seductora —de 1 a 12 modelos en la misma tarjeta— pero hay que leerla con escepticismo. Saltar de INT4 (5 modelos, casi sin pérdida) a 2-bit (10 modelos) duplica la capacidad, pero solo si usas un método SOTA y aceptas 4-8 puntos de MMLU. Y el salto de 2-bit a ternario (10 → 12) es marginal en memoria: el ternario &lt;strong>no se justifica por footprint&lt;/strong> frente a un 2-bit SOTA, se justifica por la aritmética (el techo de cómputo) y porque evita el codo de calidad al ser nativo. Si tu única métrica es &amp;ldquo;cuántos GB ocupa&amp;rdquo;, el 2-bit SOTA ya te da casi todo. El ternario es para cuando además quieres el ahorro de cómputo.&lt;/p>
&lt;h3 id="la-curva-conceptual-perplexity-vs-bits">La curva conceptual: perplexity vs bits&lt;/h3>
&lt;div class="diagram" style="max-width:780px;margin:1.5rem auto;">
&lt;svg viewBox="0 0 780 380" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Curva conceptual de perplexity frente a bits por peso">
&lt;text x="390" y="26" text-anchor="middle" fill="currentColor" font-size="15" font-weight="700">Perplexity vs bits por peso (conceptual): el codo y la rama nativa&lt;/text>
&lt;line x1="90" y1="320" x2="730" y2="320" stroke="currentColor" stroke-width="1.5"/>
&lt;line x1="90" y1="60" x2="90" y2="320" stroke="currentColor" stroke-width="1.5"/>
&lt;text x="410" y="362" text-anchor="middle" fill="currentColor" font-size="13">bits por peso (eje invertido: más comprimido a la derecha)&lt;/text>
&lt;text x="30" y="190" text-anchor="middle" fill="currentColor" font-size="13" transform="rotate(-90 30 190)">perplexity (peor arriba)&lt;/text>
&lt;text x="120" y="338" text-anchor="middle" fill="currentColor" font-size="12">16&lt;/text>
&lt;text x="250" y="338" text-anchor="middle" fill="currentColor" font-size="12">8&lt;/text>
&lt;text x="380" y="338" text-anchor="middle" fill="currentColor" font-size="12">4&lt;/text>
&lt;text x="470" y="338" text-anchor="middle" fill="currentColor" font-size="12">3&lt;/text>
&lt;text x="560" y="338" text-anchor="middle" fill="currentColor" font-size="12">2&lt;/text>
&lt;text x="650" y="338" text-anchor="middle" fill="currentColor" font-size="12">1.58&lt;/text>
&lt;line x1="380" y1="60" x2="380" y2="320" stroke="currentColor" stroke-width="0.8" stroke-dasharray="4 3"/>
&lt;text x="384" y="74" fill="currentColor" font-size="11">frontera 4-bit&lt;/text>
&lt;polyline points="120,300 250,298 380,292 470,278 560,170 620,95" fill="none" stroke="#c0392b" stroke-width="2.6"/>
&lt;circle cx="120" cy="300" r="4" fill="#c0392b"/>
&lt;circle cx="250" cy="298" r="4" fill="#c0392b"/>
&lt;circle cx="380" cy="292" r="4" fill="#c0392b"/>
&lt;circle cx="470" cy="278" r="4" fill="#c0392b"/>
&lt;circle cx="560" cy="170" r="4" fill="#c0392b"/>
&lt;text x="600" y="92" fill="#c0392b" font-size="12" font-weight="700">PTQ escalar ingenua&lt;/text>
&lt;text x="600" y="108" fill="#c0392b" font-size="11">colapsa &amp;lt;3 bits&lt;/text>
&lt;polyline points="380,292 470,284 560,250 650,232" fill="none" stroke="#2471a3" stroke-width="2.6" stroke-dasharray="6 3"/>
&lt;circle cx="560" cy="250" r="4" fill="#2471a3"/>
&lt;circle cx="650" cy="232" r="4" fill="#2471a3"/>
&lt;text x="560" y="282" fill="#2471a3" font-size="12" font-weight="700">PTQ SOTA vectorial&lt;/text>
&lt;text x="560" y="298" fill="#2471a3" font-size="11">AQLM / QuIP# / QTIP&lt;/text>
&lt;circle cx="650" cy="225" r="6" fill="#27ae60"/>
&lt;text x="600" y="208" fill="#27ae60" font-size="12" font-weight="700">ternario nativo&lt;/text>
&lt;text x="600" y="222" fill="#27ae60" font-size="11">BitNet b1.58 (no PTQ)&lt;/text>
&lt;text x="120" y="290" fill="currentColor" font-size="11">≈ plano ≥ 4 bits con buen método&lt;/text>
&lt;/svg>
&lt;/div>
&lt;p>Tres lecturas de la curva. &lt;strong>Uno&lt;/strong>: a la derecha de 4 bits, las tres ramas están casi pegadas y casi planas —el régimen resuelto—. &lt;strong>Dos&lt;/strong>: la rama roja (PTQ escalar ingenua) tiene un codo brutal entre 3 y 2 bits; ahí es donde duplica la perplexity. La rama azul (PTQ SOTA vectorial) aplana ese codo —no lo elimina, pero lo hace tolerable hasta 2 bits—. &lt;strong>Tres&lt;/strong>: el punto verde del ternario nativo &lt;strong>no está en ninguna de las dos curvas de PTQ&lt;/strong>, porque no se obtiene comprimiendo: se obtiene entrenando, y por eso puede caer por debajo del codo sin pagar el precio de calidad que paga cualquier PTQ a esa densidad de bits. Es la diferencia entre el JPEG aplastado y la foto repintada.&lt;/p>
&lt;h2 id="escepticismo-obligatorio-el-1-bit-sin-pérdida-y-los-benchmarks-sin-metodología">Escepticismo obligatorio: el 1-bit &amp;ldquo;sin pérdida&amp;rdquo; y los benchmarks sin metodología&lt;/h2>
&lt;p>Tres alertas para leer la literatura de cuantización agresiva:&lt;/p>
&lt;p>&lt;strong>&amp;ldquo;1-bit sin pérdida&amp;rdquo; casi siempre tiene letra pequeña.&lt;/strong> El binario puro {-1, +1} (1 bit) pierde la capacidad de representar el cero, que en transformers es importante (muchos pesos efectivamente nulos). Por eso el verdadero estado del arte de baja densidad es &lt;strong>ternario&lt;/strong> (1.58 bits), no binario: el cero vale su 0.58 de bit extra. Cuando un paper anuncia &amp;ldquo;1-bit&amp;rdquo;, conviene mirar si (a) es realmente 1 bit o 1.58 redondeado hacia abajo en el titular, (b) &amp;ldquo;sin pérdida&amp;rdquo; se mide en perplexity de WikiText (fácil) o en benchmarks de razonamiento (donde el colapso aparece), y (c) compara contra un baseline del mismo tamaño efectivo o contra un modelo mucho mayor para inflar la ventaja.&lt;/p>
&lt;p>&lt;strong>Perplexity plana ≠ calidad preservada.&lt;/strong> La perplexity en un corpus genérico es la métrica más indulgente con la cuantización agresiva. Un modelo 2-bit puede tener perplexity casi idéntica al BF16 y a la vez caer 10 puntos en GSM8K o en un benchmark de código, porque el razonamiento multi-paso amplifica errores que la perplexity media no ve. Desconfía de cualquier claim sub-4-bit que solo reporte perplexity. Como ya dijimos en &lt;a href="https://blog.lo0.es/posts/quantization-fundamentos-inferencia/">el post de quantization&lt;/a>, la pérdida hay que medirla en la tarea de destino.&lt;/p>
&lt;p>&lt;strong>Comparabilidad de hardware.&lt;/strong> Los números de &amp;ldquo;X veces más rápido&amp;rdquo; del ternario solo aplican &lt;strong>con los kernels especializados&lt;/strong> (bitnet.cpp) y en el hardware donde la aritmética suma/resta gana de verdad. En una GPU con tensor cores diseñadas para FP16/FP8, un kernel ternario ingenuo puede ser &lt;strong>más lento&lt;/strong> que INT4 bien optimizado, porque desaprovecha el silicio. La ventaja del ternario es real, pero es una ventaja de &lt;strong>co-diseño&lt;/strong> (modelo + kernel + a veces hardware), no un flag que activas sobre tu stack actual. Cualquier benchmark que no especifique el kernel y el hardware exacto es ruido.&lt;/p>
&lt;h2 id="implicaciones-para-inferencia-on-premise">Implicaciones para inferencia on-premise&lt;/h2>
&lt;p>En la &lt;strong>RTX 4090 (24 GB, Ada Lovelace)&lt;/strong>: el régimen práctico hoy sigue siendo INT4 AWQ para modelos de 7-14B —resuelto, casi lossless, soportado nativamente—. El 2-bit SOTA (AQLM/QuIP#/QTIP) es viable y permite cargar modelos más grandes o más modelos a la vez, pero exige los kernels específicos de cada método y una calibración cara, y paga calidad. Tiene sentido cuando el cuello es la VRAM y aceptas el trade-off; no como default. El ternario en 4090 es experimental: sin tensor cores diseñadas para suma/resta ternaria, la ventaja de cómputo se diluye, aunque el ahorro de memoria se mantiene.&lt;/p>
&lt;p>En un &lt;strong>cluster genérico 4×H100 SXM (320 GB, NVLink, FP8 nativo)&lt;/strong>: aquí el default es FP8 (calidad casi indistinguible, throughput nativo) o INT4 AWQ para modelos que no caben en FP8. El sub-4-bit SOTA es para servir modelos enormes (200B+) cuando ni FP8 ni INT4 caben con el margen de KV cache que quieres, a costa de calidad y de complejidad de kernel. El ternario nativo, hoy, es objeto de investigación más que de producción: su promesa —tocar ambos techos del roofline— es mayor en CPU/edge (donde no hay tensor cores FP8 que aprovechar) que en un cluster H100, que ya tiene hardware FP8 dedicado.&lt;/p>
&lt;p>La regla de pulgar, junio 2026: &lt;strong>≥4-bit es ingeniería resuelta; 2-bit SOTA es una palanca real pero con coste de método y de calidad; ternario es una apuesta de arquitectura, no un ajuste de despliegue&lt;/strong>.&lt;/p>
&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: FP8, INT4, GGUF&lt;/a> — la base imprescindible: la matemática del scale+zero-point, GPTQ/AWQ y PTQ vs QAT que aquí se dan por sabidas; este post es su continuación hacia la frontera sub-4-bit.&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 otro extremo del espectro, el régimen resuelto del datacenter donde la cuantización ya casi no cuesta calidad.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/knowledge-distillation-fundamentos/">Knowledge distillation&lt;/a> — la palanca complementaria: destilar reduce parámetros, cuantizar reduce bits por parámetro; a 2-bit suelen combinarse para llegar al footprint objetivo.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/poda-pruning-llm-fundamentos/">Poda de modelos LLM&lt;/a> — sparsidad y cuantización agresiva son ortogonales y se acumulan: 50 % sparso + 2-bit es otra ruta al mismo footprint que el ternario.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/kv-cache-fundamentos/">KV cache: la memoria de trabajo&lt;/a> — los ~4 GB que reservamos para KV en la cuenta de la 4090 salen de aquí; cuantizar el cache es la otra mitad del presupuesto de memoria.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/roofline-invertido-modelos-pequenos/">El roofline invertido de los modelos pequeños&lt;/a> — por qué el ternario es especial: ataca a la vez el techo de memoria y el de cómputo, mientras INT4/FP8 solo tocan el de memoria.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/test-time-quantization-en-caliente/">Test-time quantization en caliente&lt;/a> — cuantizar dinámicamente en inferencia frente a la cuantización estática y calibrada que describen AQLM/QuIP#/QTIP.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/arquitecturas-nativas-device-moe-grano-fino/">Arquitecturas nativas device + MoE de grano fino&lt;/a> — el Q4 en device como punto de partida del que el sub-4-bit y el ternario son la siguiente frontera para edge.&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> — fine-tune sobre una base ya cuantizada; el límite de cuánto puedes comprimir la base antes de que el adapter no pueda recuperar la calidad.&lt;/li>
&lt;/ul>
&lt;h2 id="referencias">Referencias&lt;/h2>
&lt;ul>
&lt;li>Ma, S. et al. &lt;em>The Era of 1-bit LLMs: All Large Language Models are in 1.58 Bits&lt;/em> (BitNet b1.58). &lt;a href="https://arxiv.org/abs/2402.17764">https://arxiv.org/abs/2402.17764&lt;/a>&lt;/li>
&lt;li>Egiazarian, V., Panferov, A., Kuznedelev, D. et al. &lt;em>Extreme Compression of Large Language Models via Additive Quantization&lt;/em> (AQLM). &lt;a href="https://arxiv.org/abs/2401.06118">https://arxiv.org/abs/2401.06118&lt;/a>&lt;/li>
&lt;li>Tseng, A., Chee, J., Sun, Q., Kuleshov, V., De Sa, C. &lt;em>QuIP#: Even Better LLM Quantization with Hadamard Incoherence and Lattice Codebooks&lt;/em>. &lt;a href="https://arxiv.org/abs/2402.04396">https://arxiv.org/abs/2402.04396&lt;/a>&lt;/li>
&lt;li>Tseng, A., Sun, Q., Hou, D., De Sa, C. &lt;em>QTIP: Quantization with Trellises and Incoherence Processing&lt;/em>. &lt;a href="https://arxiv.org/abs/2406.11235">https://arxiv.org/abs/2406.11235&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>. &lt;a href="https://arxiv.org/abs/2210.17323">https://arxiv.org/abs/2210.17323&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>. &lt;a href="https://arxiv.org/abs/2306.00978">https://arxiv.org/abs/2306.00978&lt;/a>&lt;/li>
&lt;li>Google DeepMind. &lt;em>Gemma 3 QAT (Quantization-Aware Training) models&lt;/em> — blog oficial: &lt;a href="https://developers.googleblog.com/en/gemma-3-quantized-aware-trained-state-of-the-art-ai-to-consumer-gpus/">https://developers.googleblog.com/en/gemma-3-quantized-aware-trained-state-of-the-art-ai-to-consumer-gpus/&lt;/a>&lt;/li>
&lt;li>Microsoft. &lt;em>bitnet.cpp&lt;/em> — kernels de inferencia ternaria 1-bit: &lt;a href="https://github.com/microsoft/BitNet">https://github.com/microsoft/BitNet&lt;/a>&lt;/li>
&lt;/ul></description></item></channel></rss>