Que los pesos entren en VRAM no significa que el sistema funcione
El error más habitual al elegir un modelo local es comparar el tamaño del archivo cuantizado con la memoria de la GPU y dar la decisión por resuelta. Ese cálculo solo cubre, de forma aproximada, los pesos. Durante la inferencia intervienen además la caché de claves y valores —caché KV—, las activaciones y buffers temporales, el espacio reservado por el runtime, el contexto de cada petición y, en un servicio, las solicitudes simultáneas. Una configuración puede cargar el modelo, responder a una pregunta corta y aun así fallar cuando recibe un documento largo o varias peticiones a la vez.
La consecuencia práctica es importante: «cabe» debe significar que completa la carga máxima prevista con un margen medido, no que inicia una sesión aislada. Si el margen desaparece, el resultado puede ser un error de memoria, una reducción automática del contexto, traslado de parte del trabajo a RAM del sistema o una latencia muy irregular. Cuál de esos comportamientos ocurra depende del runtime y de su configuración; no debe suponerse sin comprobarlo.
La cuantización es una de varias palancas. Reducir los bits de los pesos suele liberar memoria y puede permitir usar un modelo mayor, pero no elimina por sí sola el coste creciente de la caché KV al ampliar el contexto o la concurrencia. También puede modificar la calidad, el rendimiento o las rutas de ejecución disponibles según el formato y el backend. Por eso no existe una equivalencia universal entre «4 bits», «6 bits» y «8 bits».
Esta guía parte de una carga de trabajo concreta: qué longitud de entrada debe aceptarse, cuántos tokens se generan, cuántas solicitudes coexistirán, qué latencia es útil y qué errores serían inaceptables. Si todavía se está eligiendo familia de modelos, conviene revisar primero la guía de modelos locales y, si la duda es entre modelos base distintos, usar la sección de comparación antes de atribuir a la cuantización diferencias que proceden del modelo.
Regla de decisión
No adopte una cuantización porque el modelo se cargue. Adóptela solo si mantiene el contexto, la concurrencia y la calidad objetivo bajo una carga repetible, con memoria libre suficiente para absorber variación del runtime.
Qué significa cuantizar y qué partes de un modelo se cuantizan
Cuantizar es representar valores reales con un conjunto pequeño y discreto de números para usar menos bits y operar con ellos más deprisa, a cambio de introducir un error de redondeo [1]. En la cuantización lineal de 8 bits, la documentación de ONNX Runtime la expresa como valor_fp32 = escala × (valor_cuantizado − punto_cero): la escala es un número real positivo y el punto cero es el valor cuantizado que representa exactamente el cero. En la variante simétrica la escala se calcula con el mayor valor absoluto; en la asimétrica, con el rango entre mínimo y máximo [3]. El trabajo de Wu et al. de NVIDIA revisa cómo elegir esos parámetros y propone un flujo de 8 bits que mantuvo la precisión a menos de un 1 % de la referencia en flotante en todas las redes que estudiaron [2].
En un LLM no se cuantiza una sola cosa, sino tres, y cada una se comporta de forma distinta. Los pesos son fijos, se pueden convertir una vez y ocupan la mayor parte de la memoria al cargar. Las activaciones cambian con cada entrada y son más difíciles de cuantizar porque contienen valores atípicos muy grandes. La caché KV crece con el contexto y la concurrencia. La notación WxAy resume la combinación: W4A16 significa pesos de 4 bits con activaciones de 16 bits, y W8A8 significa pesos y activaciones de 8 bits; vLLM, por ejemplo, documenta FP8 W8A8, INT8 W8A8 e INT4 W4A16 como esquemas distintos [27].
Cuantizar solo los pesos reduce la memoria y, como la generación token a token está limitada por la lectura de memoria y no por el cálculo, también puede reducir la latencia. GPTQ y AWQ lo documentan: el primero atribuye sus aceleraciones a un menor movimiento de datos, no a operar con enteros [5], y el segundo señala que, en FP16, la generación tiene una intensidad aritmética de aproximadamente 1 operación por byte leído [6]. Cuantizar también las activaciones permite usar unidades de cálculo de enteros o de FP8, pero obliga a tratar los valores atípicos: en SmoothQuant, algunas activaciones son unas 100 veces mayores que la mayoría de los valores [7], y en LLM.int8() anular las pocas dimensiones atípicas empeora la perplejidad entre un 600 % y un 1000 % [4].
La granularidad importa tanto como el número de bits. Un único factor de escala por tensor es barato, pero un solo valor extremo estropea la resolución de todos los demás. Por eso los formatos prácticos usan una escala por canal o por grupo de pesos, por ejemplo cada 128 [5][6]. Dettmers y Zettlemoyer encontraron que reducir el tamaño de bloque, es decir, dividir los parámetros en bloques que se cuantizan por separado, es una de las pocas mejoras claras del equilibrio entre bits y precisión [9]. La consecuencia es que «4 bits» nunca son exactamente 4 bits: Q4_K almacena 4,5 bits por peso y Q6_K, 6,5625 [22], y el Q4_K_M medido por llama.cpp en Llama 3.1 8B llega a 4,8944 bits por peso [21].
Las partidas de memoria que hay que separar
Una estimación útil empieza por descomponer la memoria en partidas que se puedan observar por separado. La primera son los pesos del modelo. Su tamaño depende del número de parámetros, de la representación cuantizada y de metadatos propios del formato, como escalas, bloques o estructuras auxiliares. Por tanto, dividir simplemente parámetros por ocho, seis o cuatro ofrece una orientación, pero no sustituye al tamaño real informado por el formato y el runtime.
La segunda partida es la caché KV. En un decodificador autorregresivo, el sistema conserva claves y valores de los tokens ya procesados para no recalcularlos en cada token generado. La documentación de Transformers describe tensores de caché con dimensiones de lote, cabezas, longitud de secuencia y dimensión de cabeza. Hay claves y valores, y este almacenamiento se repite por capa. A igual arquitectura, crecer el contexto, el lote o la concurrencia aumenta la memoria necesaria.
La tercera partida reúne activaciones y buffers temporales. Su tamaño depende del backend, de la precisión de cálculo, de kernels, del prefill de entradas largas, de la longitud de generación y de cómo se agrupen solicitudes. No conviene sustituirla por una constante universal. El cuarto componente es la memoria no atribuida directamente al modelo, como contexto de ejecución, bibliotecas, asignadores y fragmentación. El quinto es un margen operativo explícito: memoria que no se asigna deliberadamente para tolerar picos, diferencias entre mediciones y carga real.
En un servidor, lote y concurrencia merecen una precisión adicional. Un lote puede ser el número de secuencias procesadas juntas en un paso, mientras que la concurrencia es el número de solicitudes vivas. Según el planificador, ambas cifras pueden relacionarse pero no son intercambiables. Para estimar la caché debe contarse la suma de tokens vivos de las secuencias que coexisten, no solo la longitud máxima de una solicitud.
Partidas que deben registrarse antes de decidir
| Partida | Qué la determina | Cómo comprobarla |
|---|---|---|
| Pesos | Modelo base, formato y cuantización | Memoria tras cargar el modelo o reporte del runtime |
| Caché KV | Capas, cabezas KV, dimensión de cabeza, tokens vivos, dtype | Capacidad o uso de caché y longitud efectiva atendida |
| Activaciones y temporales | Prefill, generación, lote, kernels y backend | Pico de memoria durante carga representativa |
| Memoria del runtime | Bibliotecas, asignador, contexto del dispositivo y fragmentación | Memoria antes y después de iniciar el proceso |
| Margen | Variabilidad y carga máxima prevista | Memoria libre mínima observada en pruebas repetidas |
Estimar pesos y caché KV antes de descargar o desplegar
La estimación no pretende predecir cada byte: pretende descartar configuraciones inviables y definir qué pruebas merecen ejecutarse. Para los pesos, use el tamaño informado para el artefacto concreto que se piensa cargar, no una cifra genérica para la familia del modelo. Si solo se conoce el número de parámetros, trátese el resultado como un mínimo teórico incompleto. Los formatos de cuantización almacenan información adicional y algunos runtimes convierten o duplican estructuras durante la carga.
Para una arquitectura tipo Llama, una aproximación conceptual de la caché KV por secuencia es: capas multiplicadas por dos, multiplicadas por cabezas de clave-valor, multiplicadas por longitud de secuencia, multiplicadas por dimensión de cabeza y por bytes de cada elemento de caché. El factor dos representa K y V. Para varias secuencias simultáneas, se suman los tokens residentes de cada una. Si el runtime usa una caché estática, puede reservar capacidad hasta un máximo incluso cuando el uso instantáneo sea menor; si usa una caché dinámica, el uso puede crecer con la solicitud. Ambas estrategias requieren medición.
Es crucial emplear el número de cabezas de clave-valor, no siempre el número total de cabezas de atención. En atención multi-query o grouped-query, varias cabezas de consulta comparten proyecciones KV. Esto puede reducir de forma sustancial la caché frente a una arquitectura con una proyección KV por cabeza de atención. La configuración exacta del modelo debe aportar capas, cabezas KV y dimensión de cabeza; no deben deducirse de un nombre comercial.
Los bytes por elemento de la caché también deben verificarse. Cuantizar los pesos no implica automáticamente cuantizar la caché KV. Algunos entornos permiten elegir un tipo de datos de caché cuantizado, otros usan por defecto una precisión distinta, y otros aplican offloading. Estas opciones cambian el presupuesto de memoria y pueden afectar rendimiento o comportamiento numérico. Anote la configuración real del runtime, no solo los bits que aparecen en el nombre del archivo.
Ejemplo conceptual de cálculo de caché
Supóngase un modelo con 32 capas, 8 cabezas KV, dimensión de cabeza 128 y caché de 2 bytes por elemento. Para una secuencia de 8.192 tokens: 32 × 2 × 8 × 8.192 × 128 × 2 bytes, aproximadamente 1 GiB de caché KV. Dos secuencias simultáneas con esa longitud duplicarían aproximadamente esa partida. Es una estimación de la caché, no de toda la VRAM: faltan pesos, temporales, memoria del runtime y margen. Si la caché se guarda con otro tipo de datos o se preasigna, el resultado efectivo cambia.
Cuantizar también la caché KV: cuándo compensa
La fórmula anterior da la caché KV con 2 bytes por elemento. Para ver el orden de magnitud con arquitecturas reales, la tabla siguiente aplica la misma fórmula a los parámetros publicados en los archivos de configuración de cada modelo [30]. La diferencia entre Llama 2 7B (32 cabezas KV) y Llama 3 8B (8 cabezas KV) es un factor 4 por token, que corresponde a la atención por grupos de consultas (GQA): varias cabezas de consulta comparten cada cabeza de clave y valor [20].
Caché KV por secuencia en 16 bits, calculada con la fórmula de este artículo
| Modelo | Capas / cabezas KV / dimensión | KV por token | Contexto | Caché por secuencia |
|---|---|---|---|---|
| Llama 2 7B | 32 / 32 / 128 | 512 KiB | 4.096 tokens | 2 GiB |
| Llama 3 8B | 32 / 8 / 128 | 128 KiB | 8.192 tokens | 1 GiB |
| Llama 3.1 8B | 32 / 8 / 128 | 128 KiB | 131.072 tokens | 16 GiB |
| Llama 3.1 70B | 80 / 8 / 128 | 320 KiB | 131.072 tokens | 40 GiB |
Un detalle de la tabla importa para el dimensionado: con 131.072 tokens de contexto, la caché de una sola secuencia de Llama 3.1 8B ocupa más que los pesos del mismo modelo en Q4_K_M (4,58 GiB), de modo que bajar los bits de los pesos no resuelve el contexto largo. En Llama 3.1 70B, 40 GiB de caché por secuencia equivalen a algo más de la mitad de una GPU de 80 GB.
Cuantizar la caché ataca ese término. KIVI propone una cuantización asimétrica de 2 bits sin ajuste fino: las claves se cuantizan por canal y los valores por token, porque sus distribuciones son distintas. Sus autores informan de un uso de memoria máxima 2,6 veces menor, incluyendo los pesos, lotes hasta 4 veces mayores y un rendimiento de entre 2,35 y 3,47 veces en cargas reales [17]. KVQuant combina cuantización por canal de las claves, cuantización antes de la posición rotatoria y valores atípicos aislados por vector; con 3 bits informa de una degradación de perplejidad inferior a 0,1 en Wikitext-2 y C4, y de que LLaMA-7B puede atender un contexto de 1 millón de tokens en una sola A100 de 80 GB [18]. Son resultados de los autores, con sus propios núcleos y modelos: sirven para saber qué es posible, no para prometer lo que obtendrá en su runtime.
Las herramientas ya lo exponen. Transformers incluye una caché cuantizada con los backends hqq (de 1 a 8 bits) y quanto (2 y 4 bits), y advierte de que puede empeorar la latencia cuando el contexto es corto y la GPU tiene memoria de sobra [25]. TensorRT-LLM permite una caché KV en FP8 y, con cuantización previa, en NVFP4 [28], y vLLM lista la caché KV cuantizada entre sus opciones [27]. Si su problema es la fragmentación y el desperdicio de la caché, y no el tamaño de cada elemento, el enfoque es otro: PagedAttention gestiona la caché KV en bloques para aprovechar mejor la memoria en servicio [19]. En cualquier caso, la regla del artículo se mantiene: compruebe por separado si la cuantización afecta a pesos, a caché KV o a ambos, y mida la calidad con contexto largo.
Métodos de cuantización: de redondear al más cercano a GPTQ, AWQ y GGUF
Hay dos familias. La cuantización posterior al entrenamiento (PTQ) convierte un modelo ya entrenado, con poco o ningún reentrenamiento y, a veces, con un pequeño conjunto de calibración. La cuantización consciente del entrenamiento (QAT) vuelve a entrenar o destila el modelo simulando la cuantización. ONNX Runtime recomienda recurrir a QAT cuando ninguna PTQ alcanza la precisión deseada [3]. Para LLM, LLM-QAT observa que los métodos PTQ funcionan bien hasta 8 bits y se rompen con menos, y propone una destilación sin datos con generaciones del propio modelo [31]. Para quien ejecuta modelos de terceros en local, la decisión práctica casi siempre es elegir entre artefactos ya cuantizados con PTQ.
La línea base es el redondeo al valor más cercano (RTN). Los demás métodos intentan reducir el error que RTN comete en los pocos pesos o activaciones que más importan. La tabla resume los más citados y qué dato verificable aporta su fuente.
Métodos de cuantización de LLM y qué documenta cada fuente
| Método | Qué cuantiza | Idea central | Dato de la fuente |
|---|---|---|---|
| LLM.int8() [4] | Pesos y activaciones a 8 bits | Multiplicación con escalas por vector y descomposición mixta: las dimensiones atípicas (alrededor del 0,1 %) se mantienen en 16 bits | Los atípicos aparecen de forma sistemática desde 6.700 millones de parámetros; la memoria baja cerca de un 50 % |
| GPTQ [5] | Solo pesos, 3 o 4 bits | Cuantización capa a capa con información de segundo orden, sin reentrenar | Cuantiza modelos de 175.000 millones de parámetros en unas 4 horas de GPU; calibra con 128 segmentos de 2.048 tokens de C4 |
| AWQ [6] | Solo pesos, 4 bits | Protege entre el 0,1 % y el 1 % de canales salientes, elegidos por la magnitud de las activaciones, mediante escalado por canal, sin retropropagación | Usa un conjunto de calibración mucho menor que GPTQ en su comparación: 16 frente a 192 secuencias |
| SmoothQuant [7] | Pesos y activaciones a 8 bits (W8A8) | Traslada la dificultad de las activaciones a los pesos con un factor por canal; α = 0,5 es el valor habitual | Hasta 1,56 veces más rápido y cerca de la mitad de memoria |
| QLoRA y NF4 [8] | Pesos a 4 bits para ajuste fino | Tipo NormalFloat de 4 bits, doble cuantización y optimizadores paginados | Ajusta un modelo de 65.000 millones de parámetros en una sola GPU de 48 GB |
| k-quants de GGUF [21][22] | Pesos, por superbloques | Superbloques formados por bloques de 16 o 32 pesos, cada uno con su escala (de 4 a 8 bits según el tipo) | Q4_K ocupa 4,5 bits por peso y Q6_K, 6,5625 [22] |
| FP8 y formatos MX [13][14] | Pesos y activaciones | Coma flotante de 8 bits (E4M3 y E5M2) y formatos con un factor de escala por bloque | FP8 propone un formato de intercambio de 8 bits para entrenar e inferir; MX combina escala por bloque con tipos de elemento estrechos |
| AQLM y BitNet b1.58 [15][16] | Pesos a 1–3 bits | AQLM: cuantización aditiva con varios libros de códigos; BitNet b1.58: pesos ternarios {−1, 0, 1} entrenados así desde el principio | BitNet b1.58 no es una conversión de un modelo ya entrenado, sino una receta de entrenamiento |
Tres lecciones se repiten en estos trabajos. La primera es que los pesos son la parte fácil y las activaciones atípicas, la difícil: LLM.int8() y SmoothQuant parten de ese mismo problema y lo resuelven de formas opuestas, aislando los atípicos o repartiendo su magnitud con los pesos [4][7]. La segunda es que proteger una fracción minúscula de canales basta para recuperar gran parte de la calidad de 4 bits [6]. La tercera es que la mejora de precisión suele costar complejidad: cuantos menos bits se usan, más necesita el método de calibración, núcleos especializados o ambos, y por eso el formato debe ser compatible con su motor, como se detalla más adelante.
Qué cambia al pasar de 8 a 6 o 4 bits
Como regla general, reducir la precisión de los pesos reduce su huella de memoria respecto a una representación de mayor precisión del mismo modelo. Esto puede hacer viable una GPU menor, dejar más presupuesto para contexto o permitir más solicitudes concurrentes. Sin embargo, la reducción observada no tiene por qué seguir una proporción exacta de ocho a seis a cuatro. El empaquetado, las escalas por grupo, el formato de archivo, las conversiones internas y los buffers del backend cambian el resultado.
La calidad tampoco depende únicamente del número de bits. Importan el algoritmo de cuantización, el tamaño de grupo, qué tensores reciben un tratamiento especial, el modelo base y la tarea. Una cuantización de 4 bits de un método puede conservar bien una tarea y otra de 6 bits de otro método puede no hacerlo; el sentido inverso también es posible. Por esa razón, los bits sirven para formular hipótesis de prueba, no para certificar precisión.
La velocidad requiere la misma cautela. Menos memoria puede reducir transferencias y mejorar la viabilidad en un dispositivo limitado, pero un formato puede carecer de kernels eficientes en un backend concreto o requerir conversiones. Aumentar el contexto puede desplazar el cuello de botella hacia la gestión de caché y el prefill. El rendimiento debe medirse con dos métricas separadas: tiempo hasta el primer token para entradas representativas y tasa de generación posterior. Una sola cifra de tokens por segundo oculta diferencias relevantes.
Como punto de partida, pruebe 8 bits cuando la calidad crítica y el presupuesto lo permitan; 6 bits cuando haga falta recuperar una parte significativa de memoria sin saltar directamente a la opción más agresiva; y 4 bits cuando la VRAM sea la restricción dominante o cuando las pruebas demuestren que no hay pérdida inaceptable. Estas son prioridades de ensayo, no recomendaciones universales.
Qué ahorro y qué pérdida se han medido
Las cifras siguientes son mediciones publicadas, no estimaciones de este artículo. Empiezan por el tamaño: la tabla reproduce la documentación de llama.cpp para Llama 3.1 8B. La columna de generación procede del mismo README, que no indica el hardware de esas pruebas, de modo que solo sirve para comparar filas entre sí.
Llama 3.1 8B en GGUF: tamaño y velocidad medidos por llama.cpp [21]
| Formato | Bits por peso | Tamaño | Respecto a F16 | Generación (tokens/s) |
|---|---|---|---|---|
| IQ1_S | 2,0042 | 1,87 GiB | 12,5 % | 79,73 |
| Q2_K | 3,1593 | 2,95 GiB | 19,7 % | 79,85 |
| Q3_K_M | 3,9960 | 3,74 GiB | 25,0 % | 71,68 |
| Q4_K_M | 4,8944 | 4,58 GiB | 30,6 % | 71,93 |
| Q5_K_M | 5,7036 | 5,33 GiB | 35,6 % | 67,23 |
| Q6_K | 6,5633 | 6,14 GiB | 41,0 % | 58,67 |
| Q8_0 | 8,5008 | 7,95 GiB | 53,1 % | 50,93 |
| F16 | 16,0005 | 14,96 GiB | 100 % | 29,17 |
Dos observaciones. La primera es que Q8_0 ocupa algo más de la mitad que F16 y Q4_K_M, menos de un tercio: el ahorro de memoria sigue los bits por peso. La segunda es que la velocidad no sigue a los bits: IQ1_S y Q2_K generan casi igual de rápido (79,73 y 79,85 tokens/s) pese a que Q2_K usa 1,2 bits más por peso, y Q3_K_M genera a una velocidad casi idéntica a Q4_K_M (71,68 y 71,93). Aun así, todos los cuantizados superan con claridad a F16, que genera 29,17 tokens/s en esa misma prueba. El estudio de Jin et al. lo resume así: la cuantización ahorra memoria, pero también puede ralentizar la inferencia, y sacar partido de ella exige trabajo de ingeniería y soporte de hardware [12].
Un segundo conjunto de mediciones, hechas con Transformers sobre un modelo de unos 15.500 millones de parámetros, muestra la memoria máxima real frente a la regla de dos bytes por parámetro.
Memoria máxima de GPU de bigcode/octocoder en Transformers [26]
| Precisión | Pico de memoria | Nota |
|---|---|---|
| bfloat16 | 29,03 GiB | Cerca de 2 bytes por parámetro |
| 8 bits (LLM.int8()) | 15,22 GiB | La guía lo ve viable en GPU de consumo como la 4090; algo más lento |
| 4 bits (NF4) | 9,54 GiB | Cambio leve en la salida de ejemplo; algo más lento que 8 bits |
La guía de Hugging Face que contiene esa tabla expone una regla útil: los pesos de un modelo de X miles de millones de parámetros ocupan unos 2 × X GB en bfloat16 y unos 4 × X GB en float32 [26]. Por aritmética, con 8 bits el factor baja a 1 × X y con 4 bits a 0,5 × X, antes de añadir escalas, caché KV y memoria del runtime.
Ahora, la pérdida de calidad. La tabla reúne cuatro estudios con métricas distintas, por lo que sus filas no son comparables entre sí: cada fila compara el método con su propia referencia de 16 bits.
Pérdida de calidad publicada por los autores de cada método
| Estudio | Modelo y métrica | Referencia 16 bits | Resultado cuantizado |
|---|---|---|---|
| GPTQ [5] | OPT-175B, perplejidad en WikiText2 | 8,34 | 4 bits: RTN 10,54 y GPTQ 8,37. 3 bits: GPTQ 8,68 y RTN 7,3 × 10³ |
| AWQ [6] | Llama-2-7B, perplejidad en WikiText | 5,47 | INT4 g128: RTN 5,73, GPTQ 5,69, AWQ 5,60. INT3 g128: RTN 6,66, GPTQ 6,43, AWQ 6,24 |
| SmoothQuant [7] | OPT-175B, precisión media en tareas | 66,9 % | W8A8 ingenuo 35,5 %; LLM.int8() 66,7 %; SmoothQuant 66,8 % |
| QLoRA [8] | MMLU medio tras ajuste fino | BF16: 53,0 | NF4 con doble cuantización: 53,1; FP4: 52,2 |
Los estudios comparativos más recientes miran modelos y tareas más cercanos al uso real. Kurtic et al., con más de 500.000 evaluaciones sobre toda la familia Llama 3.1, concluyen que FP8 (W8A8) es prácticamente sin pérdida a cualquier tamaño, que INT8 W8A8 bien ajustado pierde entre un 1 % y un 3 % de precisión y que INT4 de solo pesos es más competitivo de lo esperado, a la altura de los 8 bits [10]. Lee et al., con modelos de 1.000 millones a 405.000 millones de parámetros y 13 conjuntos de datos, observan que los modelos pequeños pueden sufrir caídas severas a 4 bits, que los de 70.000 millones se mantienen estables, que AWQ tiende a superar a GPTQ en cuantización solo de pesos y que los modelos cuantizados suelen fallar más al seguir instrucciones y al detectar alucinaciones [11]. Dettmers y Zettlemoyer, con más de 35.000 experimentos, concluyen que 4 bits es casi universalmente la precisión óptima para los bits totales del modelo y su precisión zero-shot: un modelo de 30.000 millones de parámetros a 8 bits y otro de 60.000 millones a 4 bits ocupan lo mismo, pero pueden rendir de forma muy distinta, de modo que importan a la vez el tamaño del modelo y los bits [9].
Estas cifras no se deben aplicar sin más. Las de GPTQ, AWQ y SmoothQuant proceden de modelos de 2022 y 2023; la perplejidad en WikiText no mide el seguimiento de instrucciones ni la ausencia de alucinaciones; y las diferencias entre métodos pueden cambiar con cada familia de modelos. Su utilidad es fijar un orden de magnitud —8 bits casi sin pérdida, 4 bits aceptables en modelos grandes, 3 bits o menos solo con métodos específicos— que su propia prueba debe confirmar.
Árbol de decisión resumido
| Situación observada | Primera acción | Qué no debe suponerse |
|---|---|---|
| Los pesos no caben con margen | Probar menor precisión o un modelo menor | Que bajar bits resolverá el coste de contexto |
| Los pesos caben, pero falla con entradas largas | Reducir contexto objetivo, revisar caché KV o usar más VRAM | Que el tamaño del archivo predice la capacidad de contexto |
| Falla con varias solicitudes | Dimensionar por tokens vivos concurrentes y lote real | Que una prueba de una sola sesión representa el servicio |
| La calidad cae en tareas críticas | Subir precisión, cambiar método o usar modelo menor con más bits | Que más parámetros compensan cualquier pérdida |
| La latencia es inestable | Medir prefill, generación, offloading y memoria libre | Que el promedio de tokens por segundo basta |
Procedimiento de decisión: de la restricción al candidato viable
Defina primero el contrato operativo. Escriba la longitud máxima de entrada que realmente debe soportarse, una reserva de tokens de salida, el número máximo de solicitudes vivas, el objetivo de latencia y las tareas críticas. Diferencie un máximo excepcional de un objetivo habitual. Si una aplicación procesa documentos largos, medir solo mensajes breves no representa su riesgo de memoria ni su calidad útil.
Después reúna los parámetros de la arquitectura y del runtime. Para el modelo, registre capas, cabezas KV, dimensión de cabeza y el formato de pesos. Para el runtime, registre el tipo de datos de caché, si reserva caché estática o dinámica, si permite offloading, el límite de memoria de GPU y cualquier ajuste de lote o tokens en vuelo. En herramientas de servicio, el presupuesto de caché puede configurarse explícitamente o derivarse de una fracción de la memoria disponible; ambos casos deben constar en el experimento.
Calcule una banda, no una cifra única: pesos observados o estimados, caché KV para la carga objetivo, una reserva para temporales y un margen. Si el total supera la VRAM disponible antes de aplicar el margen, descarte la combinación. Si entra por muy poco, clasifíquela como candidata de riesgo y pruébela bajo carga máxima. Si entra con margen, no la dé por aprobada hasta validar calidad y latencia.
Elija al menos tres candidatos que respondan a hipótesis distintas: el modelo deseado a 8 bits, a 6 bits y a 4 bits; o, cuando un candidato no tenga sentido, un modelo menor con una precisión superior. Mantenga constantes modelo base, revisión, prompt, contexto máximo, límite de salida, semilla cuando sea compatible, parámetros de decodificación, hardware y versión de runtime. Cambiar varias variables a la vez impide atribuir una diferencia a la cuantización.
Proceso reproducible en siete pasos
-
Defina contexto de entrada, salida reservada, concurrencia y latencia objetivo.
-
Registre arquitectura, artefacto de pesos y configuración de caché.
-
Estime pesos, caché KV y margen para el máximo de tokens vivos.
-
Descarte candidatos que no entren antes del margen o que requieran supuestos no verificados.
-
Ejecute candidatos comparables con parámetros idénticos.
-
Mida memoria, tiempo al primer token, generación, errores y calidad de salida.
-
Conserve la configuración solo si supera el umbral de calidad y mantiene margen bajo carga máxima.
Prueba mínima para detectar una pérdida que importe
Una prueba útil no necesita ser enorme, pero sí representativa. Construya un conjunto pequeño de casos que incluya el trabajo que motiva el despliegue: extracción estructurada, clasificación, síntesis de documentos, asistencia de código o respuestas con restricciones, según corresponda. Incluya entradas de longitud habitual y algunas cercanas al límite operativo. Las entradas largas son necesarias porque pueden revelar tanto errores de memoria como pérdidas de seguimiento de instrucciones o de recuperación de detalles.
Para cada caso, defina qué se validará antes de ejecutar el modelo. Algunas tareas admiten comparación exacta: un esquema JSON válido, etiquetas permitidas, campos obligatorios, una consulta que debe contener valores concretos o pruebas automatizadas para código. Otras requieren revisión humana con una rúbrica: fidelidad al documento, cobertura, ausencia de invenciones, cumplimiento de formato y utilidad. No confunda fluidez con corrección.
Fije un umbral explícito. Por ejemplo, una configuración puede quedar descartada si incumple más casos críticos que el candidato de referencia, si empeora la validez estructural por encima de un límite decidido por el equipo o si produce errores nuevos en datos sensibles. El umbral pertenece al riesgo de la aplicación; no puede inferirse de los bits. En una tarea de borrador creativo puede aceptarse más variación que en extracción de datos para un proceso posterior.
Repita las pruebas. Con decodificación estocástica, varias ejecuciones ayudan a separar variación de generación y degradación sistemática. Con decodificación determinista, las repeticiones siguen siendo útiles para observar estabilidad de rendimiento y errores de memoria. Informe resultados por tipo de tarea y longitud de entrada, no solo un promedio global. Una cuantización que parece equivalente en el promedio puede concentrar sus fallos en los documentos más largos o en la tarea de mayor impacto.
Comparación justa
No compare una cuantización con contexto de 4.000 tokens contra otra con 16.000, ni una con offloading y otra sin él, si quiere atribuir diferencias a los pesos. Si se cambia una condición por necesidad de memoria, repórtela como una configuración operativa distinta.
Cómo medir la pérdida: perplejidad, divergencia KL y tareas propias
La perplejidad mide la sorpresa media del modelo ante un texto de referencia; se calcula con el modelo original y con el cuantizado sobre el mismo texto, y los trabajos citados la reportan sobre WikiText y C4 [5][6][18]. La documentación de llama.cpp recomienda la perplejidad y la divergencia KL entre las distribuciones del modelo original y del cuantizado como métricas de precisión del cuantizado [21]. Jin et al. observan que la perplejidad sirve como indicador sustituto en la mayoría de los benchmarks que probaron [12], pero Lee et al. muestran que el seguimiento de instrucciones y la detección de alucinaciones se resienten más de lo que sugieren las métricas de texto plano [11]. Por eso la perplejidad sirve para descartar candidatos y la prueba mínima con casos propios sigue siendo la decisión final.
La calibración es otra variable. GPTQ calibra con 128 segmentos aleatorios de 2.048 tokens de C4 [5] y AWQ, con 16 secuencias [6]; llama.cpp ofrece una matriz de importancia (imatrix) para guiar la cuantización [21]. Sin verificar: las fuentes consultadas no miden cómo afecta calibrar con texto principalmente en inglés a la calidad en español u otros idiomas; compruébelo con su corpus.
Una advertencia práctica de llama.cpp: volver a cuantizar un modelo que ya estaba cuantizado, con la opción --allow-requantize, produce una pérdida de calidad grave [21]. Parta siempre de los pesos de 16 bits y anote qué artefacto concreto ha descargado, en el registro mínimo de ejecución.
Tres perfiles operativos y sus prioridades
En un portátil con GPU limitada, la prioridad suele ser evitar una configuración que dependa continuamente de RAM del sistema o de offloading para una experiencia interactiva. Empiece con un contexto realista, una sola solicitud y un modelo o cuantización que deje margen. Si 4 bits es la única forma de cargar el modelo, compare también un modelo menor a 6 u 8 bits. El segundo puede ser más estable y más útil en la tarea concreta, aunque tenga menos parámetros.
En una estación de trabajo con una GPU, hay más margen para elegir entre calidad, contexto y velocidad, pero el límite sigue siendo compartido por pesos, caché y temporales. Es un entorno apropiado para comparar 4, 6 y 8 bits bajo el mismo corpus y para decidir si se reserva VRAM para contextos largos. Si se prevé alternar entre sesiones breves y análisis documental, mida ambos perfiles: el resultado de una conversación corta no dimensiona el segundo.
En un servidor con concurrencia moderada, la unidad de planificación ya no es el archivo de modelo, sino la capacidad total de tokens vivos. El gestor de caché y el planificador de solicitudes son parte de la decisión. Una configuración que funciona para una sola sesión puede agotar memoria al coincidir prefills largos. Defina límites de admisión, longitud máxima, reserva de salida y concurrencia; después pruebe ráfagas y mezclas de solicitudes cortas y largas. Las métricas de cola y percentiles de latencia son más informativas que el mejor resultado aislado.
En cualquiera de los tres perfiles, el offloading es una opción que debe declararse, no una solución invisible. Puede ampliar la capacidad aparente trasladando parte de los datos, pero también puede alterar la latencia y depender del enlace entre CPU y GPU. Decida con mediciones si esa compensación es aceptable para el caso de uso.
Compatibilidad: el formato debe encajar con el motor y la GPU
Elegir 4 bits no es una decisión sobre el modelo, sino sobre el trío modelo, formato y motor. GGUF es un formato binario que guarda los tensores y un conjunto estandarizado de metadatos, diseñado para llama.cpp y herramientas derivadas como LM Studio, GPT4All y Ollama [22]. Los demás formatos viven en otros motores, con requisitos de hardware propios. La tabla recoge lo que documentan sus páginas oficiales consultadas el 17 de octubre de 2026; este campo cambia deprisa y conviene revisar la versión vigente.
Qué documenta cada motor sobre cuantización
| Motor | Formatos documentados | Condiciones señaladas |
|---|---|---|
| llama.cpp y GGUF [21][22] | Cuantizadores de unos 2 a 8 bits por peso en las tablas de Llama 3.1 8B, y tipos F16 y BF16 | Cuantizar desde pesos de 16 bits; evitar la recuantización |
| Transformers con bitsandbytes [23][24] | 8 bits (LLM.int8()) y 4 bits (NF4 y FP4); la visión general lista además AWQ, GPTQ, GGUF, AQLM, HQQ y torchao | LLM.int8() requiere GPU NVIDIA Turing o posterior; NF4 y FP4, Pascal o posterior |
| vLLM [27] | AWQ, GPTQ, FP8 W8A8, INT8 W8A8, INT4 W4A16, bitsandbytes, GGUF y caché KV cuantizada | AWQ no figura como compatible con Volta; FP8 W8A8 con llm-compressor figura en Ada, Hopper y GPU AMD |
| TensorRT-LLM [28] | FP4, FP8 (por tensor, por bloques y por filas), caché KV en FP8 y NVFP4, y AWQ y GPTQ en W4A16 y W4A8 | El backend por defecto admite FP4 y FP8 en Blackwell y Hopper |
| ONNX Runtime [3] | 8 bits dinámica y estática, y 4 bits por bloques en MatMul con RTN, GPTQ o HQQ | En GPU requiere Tensor Cores de enteros de 8 bits, como T4 o A100 |
| PyTorch [29] | API torch.ao.quantization en migración a torchao | La documentación pide migrar a torchao |
Dos consecuencias para la decisión. La primera: en hardware sin las instrucciones adecuadas, cuantizar puede empeorar el rendimiento por el coste de cuantizar y descuantizar, como advierte ONNX Runtime [3]. La segunda: el mejor formato depende del patrón de uso. Kurtic et al. encuentran que W4A16 es el más rentable con peticiones síncronas y que W8A8 domina con procesamiento continuo asíncrono por lotes [10]. Es la misma distinción que este artículo hace entre el portátil de una sola sesión y el servidor con concurrencia.
Registro mínimo de una ejecución
Anote: identificador y revisión del modelo base; artefacto y método de cuantización; versión y backend del runtime; GPU y memoria disponible; tipo de caché KV; límite de contexto; reserva de salida; lote, concurrencia y límite de tokens en vuelo; offloading; parámetros de decodificación; corpus y rúbrica; memoria libre mínima; tiempo al primer token; tasa de generación; errores y resultado de calidad. Sin este registro, una comparación posterior difícilmente será reproducible.
Señales para descartar una configuración y checklist de adopción
Descarte una configuración cuando produzca errores de memoria intermitentes, aunque una demostración breve funcione. La intermitencia suele indicar que la memoria disponible depende de la forma de las solicitudes, del pico de prefill, de fragmentación o de otras cargas del proceso. También es motivo de descarte que el runtime reduzca silenciosamente el contexto efectivo, que el sistema solo sea estable con una carga inferior a la prevista o que el margen observado desaparezca en pruebas repetidas.
La degradación de calidad debe analizarse por patrón. Errores concentrados en extracción, cálculos, campos obligatorios, seguimiento de instrucciones o documentos extensos tienen más peso que cambios estilísticos si esas tareas son críticas. Una salida aparentemente razonable pero con valores inventados no debe aprobarse solo por una puntuación media. Revise además la validez del formato cuando la salida alimente software posterior.
Antes de fijar una opción predeterminada, compruebe privacidad y operación. Ejecutar inferencia en el equipo no garantiza por sí mismo que ningún dato salga de él: descargas de modelos, telemetría, registro de prompts, actualización de dependencias y herramientas de observabilidad son aspectos separados. Qué datos se conservan y qué comunicaciones realiza cada componente debe verificarse en la configuración y en el entorno de red elegido.
El resultado final no tiene por qué ser «la mayor cuantización posible». Puede ser 6 bits para un perfil interactivo, 4 bits para un perfil de análisis con gran contexto, u 8 bits para una tarea donde la pérdida detectada no sea aceptable. Mantenga las alternativas junto con su registro de pruebas. Si cambian el runtime, el hardware, el formato de pesos o la carga de trabajo, vuelva a medir: la conclusión anterior deja de ser una garantía.
Checklist antes de adoptar una cuantización
-
Los pesos, la caché KV, temporales y el margen se han medido o justificado por separado.
-
El contexto, la salida reservada y la concurrencia reflejan carga máxima prevista.
-
Se ha comprobado si la cuantización afecta a pesos, caché KV o ambos.
-
Las configuraciones comparadas usan el mismo modelo base y condiciones equivalentes.
-
El corpus contiene tareas críticas y entradas largas representativas.
-
Existe un umbral de pérdida aceptable definido antes de revisar resultados.
-
La memoria libre mínima, los errores y los percentiles de latencia se han registrado.
-
La configuración de offloading, telemetría, descargas y registros ha sido revisada.
-
La decisión puede reproducirse a partir del registro técnico completo.
Límites de la evidencia y datos sin verificar
Las cifras de GPTQ, AWQ, SmoothQuant y LLM.int8() se midieron en OPT, BLOOM y Llama 2; no se extrapolan a los modelos actuales. Kurtic et al. y Lee et al. usan Llama 3.x y son más cercanos, pero no cubren todos los modelos ni idiomas.
Las mejoras de velocidad de KIVI, KVQuant y AWQ (TinyChat) provienen de núcleos y GPU de sus autores. En el README de llama.cpp no se indica el hardware de las medidas de velocidad ni la precisión de la columna de tamaño original, aunque sus cifras (32,1 GB para 8B) coinciden con unos 4 bytes por parámetro; esta lectura es una inferencia nuestra y no figura en la fuente.
Los parámetros de arquitectura de la tabla de caché KV proceden de réplicas públicas de los archivos config.json en Hugging Face (NousResearch y unsloth), porque los repositorios oficiales de Meta exigen autenticación. La dimensión de cabeza de Llama 3.1 70B (128) se deriva de dividir el tamaño oculto entre el número de cabezas de atención.
La tabla de compatibilidad refleja la documentación consultada el 17 de octubre de 2026, incluida la versión en desarrollo de vLLM. Sin verificar: el efecto del idioma de calibración, el comportamiento de cada cuantizador con arquitecturas de mezcla de expertos y el rendimiento en su GPU concreta; esas tres cosas requieren su propia medición.
Referencias
-
[1] Gholami, A. et al. (2021). A Survey of Quantization Methods for Efficient Neural Network Inference. arXiv:2103.13630. https://arxiv.org/abs/2103.13630
-
[2] Wu, H. et al. (2020). Integer Quantization for Deep Learning Inference: Principles and Empirical Evaluation. arXiv:2004.09602. https://arxiv.org/abs/2004.09602
-
[3] ONNX Runtime. Quantize ONNX models. https://onnxruntime.ai/docs/performance/model-optimizations/quantization.html
-
[4] Dettmers, T., Lewis, M., Belkada, Y., Zettlemoyer, L. (2022). LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale. arXiv:2208.07339. https://arxiv.org/abs/2208.07339
-
[5] Frantar, E., Ashkboos, S., Hoefler, T., Alistarh, D. (2022). GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers. arXiv:2210.17323. https://arxiv.org/abs/2210.17323
-
[6] Lin, J. et al. (2023). AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration. arXiv:2306.00978. https://arxiv.org/abs/2306.00978
-
[7] Xiao, G. et al. (2022). SmoothQuant: Accurate and Efficient Post-Training Quantization for Large Language Models. arXiv:2211.10438. https://arxiv.org/abs/2211.10438
-
[8] Dettmers, T., Pagnoni, A., Holtzman, A., Zettlemoyer, L. (2023). QLoRA: Efficient Finetuning of Quantized LLMs. arXiv:2305.14314. https://arxiv.org/abs/2305.14314
-
[9] Dettmers, T., Zettlemoyer, L. (2022). The case for 4-bit precision: k-bit Inference Scaling Laws. arXiv:2212.09720. https://arxiv.org/abs/2212.09720
-
[10] Kurtic, E. et al. (2024). «Give Me BF16 or Give Me Death»? Accuracy-Performance Trade-Offs in LLM Quantization. arXiv:2411.02355. https://arxiv.org/abs/2411.02355
-
[11] Lee, J. et al. (2024). Exploring the Trade-Offs: Quantization Methods, Task Difficulty, and Model Size in Large Language Models From Edge to Giant. arXiv:2409.11055. https://arxiv.org/abs/2409.11055
-
[12] Jin, R. et al. (2024). A Comprehensive Evaluation of Quantization Strategies for Large Language Models. arXiv:2402.16775. https://arxiv.org/abs/2402.16775
-
[13] Micikevicius, P. et al. (2022). FP8 Formats for Deep Learning. arXiv:2209.05433. https://arxiv.org/abs/2209.05433
-
[14] Rouhani, B. D. et al. (2023). Microscaling Data Formats for Deep Learning. arXiv:2310.10537. https://arxiv.org/abs/2310.10537
-
[15] Ma, S. et al. (2024). The Era of 1-bit LLMs: All Large Language Models are in 1.58 Bits. arXiv:2402.17764. https://arxiv.org/abs/2402.17764
-
[16] Egiazarian, V. et al. (2024). Extreme Compression of Large Language Models via Additive Quantization. arXiv:2401.06118. https://arxiv.org/abs/2401.06118
-
[17] Liu, Z. et al. (2024). KIVI: A Tuning-Free Asymmetric 2bit Quantization for KV Cache. arXiv:2402.02750. https://arxiv.org/abs/2402.02750
-
[18] Hooper, C. et al. (2024). KVQuant: Towards 10 Million Context Length LLM Inference with KV Cache Quantization. arXiv:2401.18079. https://arxiv.org/abs/2401.18079
-
[19] Kwon, W. et al. (2023). Efficient Memory Management for Large Language Model Serving with PagedAttention. arXiv:2309.06180. https://arxiv.org/abs/2309.06180
-
[20] Ainslie, J. et al. (2023). GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints. arXiv:2305.13245. https://arxiv.org/abs/2305.13245
-
[21] ggml-org. llama.cpp, tools/quantize/README.md. https://github.com/ggml-org/llama.cpp/blob/master/tools/quantize/README.md
-
[22] Hugging Face. GGUF (documentación del Hub). https://huggingface.co/docs/hub/gguf
-
[23] Hugging Face. Quantization (documentación de Transformers). https://huggingface.co/docs/transformers/main/en/quantization/overview
-
[24] Hugging Face. bitsandbytes (documentación de Transformers). https://huggingface.co/docs/transformers/main/en/quantization/bitsandbytes
-
[25] Hugging Face. KV cache strategies (documentación de Transformers). https://huggingface.co/docs/transformers/en/kv_cache
-
[26] Hugging Face. Optimizing LLMs for speed and memory. https://huggingface.co/docs/transformers/main/en/llm_tutorial_optimization
-
[27] vLLM. Quantization. https://docs.vllm.ai/en/latest/features/quantization/
-
[28] NVIDIA. TensorRT LLM: Quantization. https://nvidia.github.io/TensorRT-LLM/features/quantization.html
-
[29] PyTorch. Quantization (documentación 2.14). https://docs.pytorch.org/docs/2.14/quantization.html
-
[30] Hugging Face. Archivos config.json de Llama 2 7B, Llama 3 8B, Llama 3.1 8B y 70B (réplicas NousResearch y unsloth). https://huggingface.co/unsloth/Meta-Llama-3.1-8B/blob/main/config.json
-
[31] Liu, Z. et al. (2023). LLM-QAT: Data-Free Quantization Aware Training for Large Language Models. arXiv:2305.17888. https://arxiv.org/abs/2305.17888
Continúa explorando
Fuentes consultadas
Correcciones y transparencia
Si detectas un dato incorrecto o desactualizado, puedes enviarnos una corrección indicando la página y la fuente que debemos revisar.
Proponer una corrección