Quantization para inferencia LLM: FP8, INT4 (GPTQ, AWQ) y GGUF — el zoom contable del modelo

Este post complementa el de KV cache: la memoria de trabajo de la inferencia LLM, donde la cuantización del cache se menciona como una palanca de ahorro; aquí entramos al método entero —pesos del modelo y cache— y a por qué cada formato hace lo que hace.

TL;DR

Cuantizar es un cambio de representación: en lugar de guardar cada peso del modelo como un float16 o bfloat16 (2 bytes), se guarda como un entero corto (1 byte INT8, medio byte INT4) con un factor de escala que reconstruye un valor aproximado al original. El precio es pérdida de precisión numérica; la recompensa es 2-4× menos VRAM, 2-3× más throughput y, en Hopper y Blackwell, un coste de cómputo radicalmente menor porque las unidades FP8/FP4 ejecutan en menos ciclos que las BF16. Los cuatro formatos dominantes en mayo de 2026 son FP8 (E4M3/E5M2, datacenter), INT4 GPTQ (reconstrucción Hessian-aware), INT4 AWQ (activation-aware) y GGUF (familia llama.cpp). Cada uno tiene un sweet spot: FP8 cuando el datacenter es Hopper/Blackwell y la calidad importa; GPTQ y AWQ cuando el serving es Ampere/Ada y los 4 bits son obligatorios; GGUF cuando el target es edge o consumer GPU. Este post explica la matemática mínima, los algoritmos detrás de cada formato, qué pierde cada uno medido en perplexity y MMLU y cómo se aplica en una 4090 frente a un cluster H100.

Estás aquí: DEPLOY

Estás aquí: DEPLOY · quantization de pesos y de KV cache1 · Data2 · Tune3 · Eval4 · Deploy5 · Observe6 · Retrain

La analogía: el JPEG con detector de bordes

Un JPEG comprime una imagen reduciendo la precisión con la que se almacenan los píxeles, pero no la reduce uniformemente. Donde hay un cielo plano —miles de píxeles muy parecidos—, descarta detalle sin que se note. Donde hay un borde nítido —el contorno de una cara—, conserva la fidelidad. El truco es detectar qué partes son sensibles antes de comprimir.

Quantization de un LLM funciona igual. No tomas todos los pesos del modelo y dices “todos en 4 bits”. Algunos pesos son muy importantes —pesos de proyecciones que mueven mucho la salida cuando cambian— y otros son menos. Las técnicas modernas (GPTQ, AWQ) son básicamente detectores de bordes: identifican qué pesos pueden cuantizarse agresivamente y cuáles necesitan más bits o tratamiento especial, y aplican la cuantización con esa información.

La analogía se sostiene en tres detalles:

  • Calibración con un pequeño dataset = la fase en la que el encoder JPEG analiza la imagen antes de elegir bloques.
  • Bloque de 128 pesos con un scale común = el bloque 8×8 del JPEG con su DCT.
  • Outliers se preservan con más precisión = las altas frecuencias de un borde se preservan más que las planicies.

A partir de ahí, lo que sigue es la matemática y los detalles operativos.

La matemática mínima: scale y zero-point

Cuantizar un vector de pesos w ∈ ℝ^n (en BF16) a INT4 significa encontrar dos cosas:

  • Un scale s ∈ ℝ (en BF16 o FP16).
  • Para cada peso, un código entero q ∈ {0, 1, ..., 15} que cabe en 4 bits.

Y una fórmula de reconstrucción aproximada:

$$\hat{w}_i \approx s \cdot (q_i - z)$$

donde z es el zero-point (entero que define qué código representa el cero original). El zero-point existe en INT4/INT8 asimétrico para no desperdiciar la mitad del rango con valores negativos cuando la distribución de pesos no es simétrica.

La elección de s y z para un bloque de pesos w_block:

$$s = \frac{\max(w_\text{block}) - \min(w_\text{block})}{2^{b} - 1}, \quad z = -\frac{\min(w_\text{block})}{s},$$

con b = número de bits (4 en INT4). Codificación:

$$q_i = \text{clip}!\left(\text{round}!\left(\frac{w_i}{s} + z\right),, 0,, 2^b - 1\right).$$

Y decodificación en inferencia:

$$\hat{w}_i = s \cdot (q_i - z).$$

Ejemplo numérico

Tomemos 8 pesos reales de una capa lineal: w = [0.31, -0.12, 0.78, -0.05, 1.42, -0.91, 0.23, 0.66]. Queremos cuantizar a INT4 (16 niveles).

max = 1.42, min = -0.91. Rango = 2.33.

$$s = \frac{2.33}{15} \approx 0.1553, \quad z = -\frac{-0.91}{0.1553} \approx 5.86 \to 6.$$

Codificación de cada peso:

w_iw_i/s + zroundq_iŵ_i = s·(q-z)error
0.318.00880.311+0.001
-0.125.2355-0.155-0.035
0.7811.0211110.776-0.004
-0.055.68660.000+0.050
1.4215.1415151.398-0.022
-0.91-0.0600-0.932-0.022
0.237.48770.155-0.075
0.6610.2510100.621-0.039

Error medio cuadrático: MSE ≈ 0.0015. Para una sola capa con millones de pesos, el efecto agregado es lo que la calibración intenta minimizar.

Storage: en lugar de 8 valores × 2 bytes = 16 bytes (BF16), tenemos 8 × 4 bits = 4 bytes + 2 bytes (scale BF16) + 0.5 byte (zero-point INT4) ≈ 6.5 bytes. 2.5× menos, pero el dato útil sigue siendo recuperable con error pequeño.

PTQ vs QAT: cuándo se cuantiza

Dos regímenes operativos distintos.

Post-Training Quantization (PTQ) se aplica después del entrenamiento, sobre un modelo ya entrenado en BF16/FP16. Lee un dataset pequeño (típicamente 128-512 ejemplos) para calibrar las escalas, ejecuta el algoritmo de cuantización (GPTQ, AWQ, etc.) y produce los pesos cuantizados. Coste: minutos a unas horas. Pérdida típica: 0.05-0.3 PPL en perplexity (~0.5-2 % en MMLU) para INT4 con métodos modernos.

Quantization-Aware Training (QAT) introduce las operaciones de cuantización dentro del bucle de entrenamiento. El modelo “ve” durante el entrenamiento que sus pesos se cuantizan y aprende a ser robusto a ello. Coste: re-entrenar el modelo (caro), pero hace falta poco —fine-tune corto sobre el modelo PTQ ya cuantizado—. Pérdida típica: ~0 (la cuantización se vuelve indistinguible del modelo original).

Cuándo usar cuál:

  • PTQ = default. 90 % de los casos en producción. El modelo viene en BF16, lo cuantizas con 1-2 horas en una GPU, lo despliegas.
  • QAT = cuando PTQ pierde demasiado y la diferencia importa (caso típico: INT2/INT3, o modelos sensibles como reasoning específicos).

Los formatos dominantes en 2026

Mapa de formatos de quantization de pesos (mayo 2026)FP8 (E4M3/E5M2)Datacenter / Hopper-Blackwell— H100, H200, B200— vLLM nativo— hardware FP8 tensor coresPérdida:— PPL: +0.02-0.05— MMLU: -0.3-0.8 ppSweet spot:Modelo serving en datacentermoderno. Calidad casi indistinguiblede BF16, ~2× menos VRAM.Comando vLLM:--quantization=fp8--kv-cache-dtype=fp8INT4 GPTQReconstrucción Hessian-aware— Ampere/Ada/Hopper— vLLM, TensorRT-LLM, ExLlama— calibración: 128 muestrasPérdida:— PPL: +0.15-0.30— MMLU: -1.5-3 ppSweet spot:Servings GPU sin FP8 (Ampere/Ada),modelos medianos (8-70B).~4× menos VRAM.Comando vLLM:--quantization=gptq(modelo *-GPTQ-Int4)INT4 AWQActivation-aware salient weights— Ampere/Ada/Hopper— vLLM, TensorRT-LLM— preserva 1 % outlier channelsPérdida:— PPL: +0.10-0.25— MMLU: -1-2 ppSweet spot:Alternativa preferida a GPTQen 2026. Mejor preservación decalidad con coste similar.Comando vLLM:--quantization=awq_marlin(modelo *-AWQ-INT4)GGUF (llama.cpp)Edge / consumer / CPU-friendly— CPU, Apple Silicon,consumer GPU (4090, AMD)— sub-formatos: Q4_K_M, Q5_K_M…Pérdida (Q4_K_M):— PPL: +0.20-0.40— MMLU: -2-4 ppSweet spot:Cualquier deploy no-CUDA ocon VRAM limitada. Ollama, LMStudio.~4× menos VRAM/RAM.Comando:ollama run llama3:8b-q4_K_Mllama.cpp --model *.gguf

FP8: el formato del datacenter Hopper/Blackwell

FP8 no es “INT8 + signo”: son dos formatos en coma flotante de 8 bits.

  • E4M3 (4 bits de exponente, 3 de mantisa): rango ±448, precisión razonable en ±1.0. Usado típicamente para pesos y activaciones de la mayoría de capas.
  • E5M2 (5 de exponente, 2 de mantisa): rango ±57 344, precisión menor. Usado para gradientes durante entrenamiento o para activaciones con outliers grandes en inferencia.

Por qué FP8 dejó atrás a INT8 en datacenter: las tensor cores de H100/H200/B200 ejecutan operaciones FP8 nativamente con throughput 2× el de BF16 y 4× el de FP16. Y, como FP8 preserva la dinámica logarítmica (igual que FP16), las matrices con valores dispersos en magnitud (típicas de transformers) se cuantizan con menos error que INT8.

La pérdida medida en producción es mínima: para un Llama 3.1 70B FP8 vs BF16, la perplexity sube ~0.03 y MMLU cae ~0.5 puntos. Es la opción de default en cualquier deploy moderno sobre H100/B200.

Microscaling: NVFP4 y MXFP4

Blackwell (B100/B200, 2025) introduce NVFP4 y MXFP4, formatos de 4 bits con scaling por bloque pequeño (típicamente 16 ó 32 elementos por scale, frente a 128 en INT4 GPTQ). El scale es FP8 en lugar de FP16/BF16, lo que reduce más el storage.

Resultado: 4 bits con calidad cercana a FP8. NVFP4 se está convirtiendo en 2026 en la opción de default para modelos muy grandes (200B+) en clusters Blackwell. Para 4×H100 SXM —Hopper, no Blackwell— sigue siendo FP8 el sweet spot.

INT4: GPTQ vs AWQ

Los dos algoritmos que dominan la cuantización a 4 bits resuelven el mismo problema con estrategias distintas.

GPTQ (Frantar et al. 2022)

La idea: cuantización capa por capa, minimizando explícitamente el error en la salida de cada capa lineal usando información de la matriz Hessiana (segunda derivada de la pérdida). Para cada capa:

  1. Estima la Hessiana H = X^T X donde X son las activaciones de calibración.
  2. Cuantiza un peso a la vez por orden (típicamente el más sensible primero).
  3. Actualiza los pesos restantes para compensar el error del peso que se acaba de cuantizar.

El paso 3 es lo que hace a GPTQ mejor que el round-to-nearest naive: los pesos compensan los errores de sus vecinos. La implementación oficial cuantiza un Llama 3 70B en ~3-4 horas en una H100 con 128 muestras de calibración.

AWQ (Lin et al. 2023)

La observación de AWQ: dentro de una capa, no todos los canales (columnas de pesos) son igual de importantes. Aproximadamente el 1 % de los canales acumulan la mayoría del impacto en las activaciones. AWQ los identifica midiendo la magnitud media de las activaciones que los multiplican, y los escala antes de cuantizar para preservarlos mejor.

Concretamente: si un canal c tiene activaciones medias grandes, AWQ multiplica los pesos de ese canal por un factor s_c antes de cuantizar, y al cuantizar la entrada del siguiente layer la divide por s_c. La matemática se cancela, pero los pesos importantes acaban con más resolución dentro del rango INT4. Sin re-entrenamiento, sin Hessiana, más rápido que GPTQ (~1-2 h para 70B en H100).

Cuál elegir

CriterioGPTQAWQ
Velocidad de cuantizaciónMás lentoMás rápido
Calidad preservada en INT4BuenoLigeramente mejor (~0.05-0.1 PPL)
Hardware soportadoAmplio (Ampere+)Amplio (Ampere+)
EcosistemaMaduro, ampliamente integradoMás reciente, ganando terreno
Default 2026Cuando ya hay artefactos GPTQDefault para nuevas cuantizaciones

La regla práctica en mayo de 2026: AWQ por defecto para INT4 nuevo. GPTQ cuando ya hay un artefacto GPTQ-Int4 publicado por la comunidad que satisface tus requisitos.

GGUF: el ecosistema llama.cpp

GGUF no es un algoritmo de cuantización, es un formato de archivo —y un ecosistema completo de herramientas— alrededor del runtime llama.cpp.

Su valor: compatibilidad universal. Un mismo archivo GGUF se ejecuta en:

  • CPU pura (Intel/AMD x86, ARM).
  • Apple Silicon (M1/M2/M3/M4) con aceleración Metal.
  • Consumer GPU (RTX, AMD Radeon) con offload de capas a VRAM.
  • Edge devices (Jetson, móviles ARM).

Eso es lo que llama.cpp permite y vLLM/TensorRT-LLM no. La contrapartida: menos throughput máximo en GPU datacenter que vLLM.

Sub-formatos GGUF más usados en 2026:

Sub-formatoBits efectivosCalidad relativaUso típico
Q8_08.5Casi sin pérdidaValidación de baseline
Q6_K6.6Pérdida muy pequeñaCalidad alta + ahorro
Q5_K_M5.7Pérdida pequeñaSweet spot calidad/tamaño
Q4_K_M4.8Pérdida moderadaDefault consumer
Q4_K_S4.5Pérdida moderada-altaCuando no cabe Q4_K_M
Q3_K_M3.9Pérdida notableHardware muy restringido
Q2_K3.3Pérdida grandeÚltimo recurso

El subíndice _K_M indica el grado de mixto: dentro del archivo, ciertas capas (típicamente attention.wv, feed_forward.w2) se guardan con más bits que otras. Es el equivalente al “detector de bordes” del JPEG aplicado capa-a-capa por heurística pre-establecida.

KV cache quantization

Cuantizar los pesos del modelo es la mitad del problema. El KV cache —cubierto en detalle en KV cache: la memoria de trabajo— consume típicamente 20-50 % de la VRAM en producción con concurrencia. Cuantizar el cache también es una palanca:

  • --kv-cache-dtype=auto (BF16/FP16, default). 2 bytes por dimensión × num_heads × head_dim × 2 (K y V).
  • --kv-cache-dtype=fp8 (E4M3 o E5M2 según hardware). 1 byte. Divide el cache por 2 con pérdida típica de < 0.5 % en quality benchmarks.
  • --kv-cache-dtype=int4 (con bloques de 128). 0.5 bytes + overhead de scale. Divide el cache por ~3.5. Pérdida medible (1-2 %) pero aceptable en contextos largos.

La cuantización del KV cache es ortogonal a la cuantización de pesos: puedes tener pesos BF16 y cache FP8, o pesos INT4 y cache FP8, etc. La combinación dominante en 2026 sobre H100: pesos FP8 + cache FP8, que casi indistinguible de BF16 en calidad y duplica capacidad de concurrencia.

Pérdida de calidad medida (Llama 3.1 70B Instruct, referencia)

Tabla representativa para Llama 3.1 70B Instruct con dataset de calibración WikiText-2 (128 muestras). Cifras de fuentes públicas agregadas; pueden variar ±0.05 PPL y ±0.5 MMLU según implementación y seed.

FormatoVRAM modeloPerplexity (WikiText-2)MMLU (5-shot)Velocidad relativa (H100)
BF16 (baseline)140 GB4.8582.11.00×
FP8 (E4M3)70 GB4.87 (+0.02)81.6 (-0.5)1.85×
INT8 SmoothQuant70 GB4.92 (+0.07)81.0 (-1.1)1.65×
INT4 AWQ35 GB4.99 (+0.14)80.4 (-1.7)2.50×
INT4 GPTQ35 GB5.05 (+0.20)80.0 (-2.1)2.40×
GGUF Q5_K_M49 GB4.94 (+0.09)81.1 (-1.0)n/a (llama.cpp)
GGUF Q4_K_M42 GB5.08 (+0.23)79.8 (-2.3)n/a (llama.cpp)
GGUF Q3_K_M33 GB5.45 (+0.60)77.5 (-4.6)n/a (llama.cpp)

Tres lecciones a retener:

  1. FP8 es casi gratis en calidad. Si tu hardware lo soporta, no hay debate.
  2. INT4 AWQ es notablemente mejor que INT4 GPTQ en calidad preservada, a velocidad comparable.
  3. Q3 ya es zona de pérdida medible; Q2 ya no se debería usar excepto para experimentos o demos extremas.

Implicaciones en hardware on-premise

En una RTX 4090 (24 GB, Ada Lovelace, sin FP8 nativo)

Llama 3.1 8B Instruct entra holgadamente en BF16 (16 GB), pero queda poco margen para KV cache con concurrencia. El sweet spot habitual:

  • Llama 3.1 8B AWQ-INT4: ~5 GB de pesos, 19 GB libres para KV cache → 4-8 sesiones concurrentes con contexto moderado.
  • Llama 3 70B GGUF Q4_K_M: ~42 GB. No cabe en la 4090 entera; requiere offload a CPU con llama.cpp (decode lento pero funcional para single-user).
  • Llama 3 70B AWQ-INT4 con TP=2 (dos 4090): ~17 GB cada GPU → cabe y queda margen.

La 4090 no soporta FP8 nativo (Ada Lovelace tiene la instrucción pero sin el throughput acelerado de Hopper). En la práctica, FP8 en 4090 funciona pero sin la ganancia de velocidad: la elección razonable es INT4 AWQ.

Aquí FP8 brilla:

  • Llama 3.1 70B FP8 con TP=2: ~35 GB/GPU. Holgado, deja espacio enorme para KV cache → docenas de sesiones concurrentes.
  • Llama 3.1 405B FP8 con TP=4: ~200 GB/GPU. Cabe justo, con prefill+decode en el mismo pool.
  • Llama 3.1 405B INT4 AWQ con TP=2: ~100 GB/GPU. Permite serving del modelo grande sin saturar el cluster; queda margen para cache y para servir simultáneamente otro modelo.

La regla de pulgar en cluster H100 en 2026: FP8 si la calidad importa y el modelo cabe; INT4 AWQ si el modelo no cabe en FP8 o si quieres más concurrencia a costa de 1-2 puntos MMLU.

Lo que no hemos cubierto (próximos artículos)

  • Speculative decoding: la otra palanca grande de aceleración en inferencia. Ortogonal a quantization, multiplica el speedup.
  • MoE quantization: los modelos Mixture-of-Experts (Mixtral, DeepSeek V3, Qwen3-235B-A22B) tienen patrones de quantization distintos —los expertos no se cuantizan uniformemente, hay rutado dinámico—.
  • Calibration dataset matters: cómo elegir las 128-512 muestras de calibración. El error común de coger un dataset random de internet y cómo evitarlo.
  • Multimodal quantization: los modelos vision-language tienen capas heterogéneas (vision encoder en CNN, language en transformer) que necesitan tratamiento separado.

Ver también

Referencias

  • Frantar, E., Ashkboos, S., Hoefler, T., Alistarh, D. GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers (ICLR 2023).
  • Lin, J., Tang, J., Tang, H., Yang, S., Dang, X., Han, S. AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration (MLSys 2024).
  • Dettmers, T., Pagnoni, A., Holtzman, A., Zettlemoyer, L. QLoRA: Efficient Finetuning of Quantized LLMs (NeurIPS 2023). Introduce NF4 y double quantization.
  • Xiao, G., Lin, J., Seznec, M., Wu, H., Demouth, J., Han, S. SmoothQuant: Accurate and Efficient Post-Training Quantization for Large Language Models (ICML 2023).
  • NVIDIA. FP8 Formats for Deep Learning — white paper E4M3/E5M2: https://arxiv.org/abs/2209.05433.
  • Rouhani, B. et al. Microscaling Data Formats for Deep Learning — MXFP4/MXFP8: https://arxiv.org/abs/2310.10537.
  • llama.cpp GGUF spec: https://github.com/ggerganov/llama.cpp/blob/master/docs/gguf.md.
  • vLLM quantization docs: https://docs.vllm.ai/en/latest/quantization/.