Cuantización de modelos locales: cuánta VRAM ahorras y qué pierdes
Cálculo de memoria de pesos y caché KV, formatos GGUF, GPTQ y AWQ, y cómo medir la pérdida de calidad antes de quedarte con un modelo cuantizado.

Un modelo de 8.000 millones de parámetros en FP16 ocupa unos 16 GB solo en pesos. En 4 bits baja a unos 5 GB. Esa diferencia decide si el modelo cabe en una tarjeta de consumo o no, y por eso casi todo el que ejecuta modelos en local termina cuantizando. Lo que no suele hacerse es medir qué se perdió por el camino.
La cuenta que tienes que saber hacer
La memoria de los pesos es, en primera aproximación, parámetros × bits por peso ÷ 8. Para 8.000 millones de parámetros:
| Formato | Bits efectivos por peso | Pesos en memoria |
|---|---|---|
| FP16 / BF16 | 16 | ≈ 16 GB |
| GGUF Q8_0 | ≈ 8,5 | ≈ 8,5 GB |
| GGUF Q5_K_M | ≈ 5,7 | ≈ 5,7 GB |
| GGUF Q4_K_M | ≈ 4,8 | ≈ 4,8 GB |
Los bits efectivos son algo mayores que el nombre del formato porque cada bloque de pesos guarda también sus factores de escala. Un «4 bits» nunca ocupa exactamente la cuarta parte que FP16.
La parte que casi todo el mundo olvida es la caché KV: las claves y valores de atención que el modelo guarda por cada token del contexto. Su tamaño no depende de cuánto cuantices los pesos.
bytes_por_token = 2 × capas × cabezas_kv × dim_cabeza × bytes_por_valor
# Ejemplo: arquitectura tipo Llama 3 8B (32 capas, 8 cabezas KV, dim 128), caché en FP16
2 × 32 × 8 × 128 × 2 = 131.072 bytes ≈ 128 KB por token
128 KB × 8.192 tokens ≈ 1 GB
128 KB × 32.768 tokens ≈ 4 GB
Con 32K de contexto, la caché KV ocupa casi lo mismo que los pesos en Q4. Si el modelo «cabe» con 4K de contexto y se cae por falta de memoria con 32K, este es el motivo. Súmale además unos cientos de MB de buffers de cálculo del motor de inferencia.
Qué formato usar según tu hardware
GGUF es el formato de llama.cpp y de las herramientas construidas sobre él. Funciona en CPU, en GPU y repartiendo capas entre ambas, lo que lo hace la opción por defecto en portátiles, Macs y equipos con poca VRAM. Los sufijos _K_M indican cuantización por bloques con mezcla de precisiones: las capas más sensibles reciben más bits.
GPTQ y AWQ están pensados para servir desde GPU con motores como vLLM. Ambos usan un conjunto de datos de calibración para decidir cómo redondear los pesos, y en GPU suelen dar más rendimiento por consulta que GGUF. El precio: dependes de que exista una versión ya cuantizada del modelo o de hacer tú la calibración, y si los datos de calibración no se parecen a tu uso (por ejemplo, calibrado en inglés y usado en español), la pérdida puede ser mayor de lo esperado.
bitsandbytes en 4 bits (NF4) es lo que se usa en QLoRA para entrenar adaptadores sobre un modelo cuantizado. Para servir en producción suele ser más lento que las alternativas anteriores.
Cuantizar con llama.cpp a partir de un GGUF en FP16:
./llama-quantize modelo-f16.gguf modelo-q4_k_m.gguf Q4_K_M
# Servir con todas las capas en GPU y 16K de contexto
./llama-server -m modelo-q4_k_m.gguf -ngl 99 -c 16384
La velocidad también cambia, y se puede estimar
Generar un token obliga a leer todos los pesos desde memoria. En generación con una sola petición, el límite casi siempre es el ancho de banda de memoria, no la potencia de cálculo. Un techo aproximado:
tokens_por_segundo_máximo ≈ ancho_de_banda ÷ tamaño_de_los_pesos
GPU con 1.000 GB/s y modelo de 4,8 GB → techo ≈ 208 tokens/s
El mismo modelo en FP16 (16 GB) → techo ≈ 62 tokens/s
La cifra real será menor, pero la proporción se mantiene: cuantizar de 16 a ~5 bits multiplica la velocidad de generación aproximadamente por tres en ese escenario. Con muchas peticiones en paralelo el cuello de botella se mueve hacia el cálculo y la ganancia es menor.
Cómo medir lo que pierdes
La perplejidad es la métrica rápida: cuánto «se sorprende» el modelo ante un texto. llama.cpp la calcula directamente:
./llama-perplexity -m modelo-f16.gguf -f textos_de_tu_dominio.txt -ngl 99
./llama-perplexity -m modelo-q4_k_m.gguf -f textos_de_tu_dominio.txt -ngl 99
Usa textos de tu dominio y en tu idioma, no el corpus de ejemplo en inglés. Una diferencia pequeña de perplejidad indica que el modelo cuantizado se parece al original, pero no garantiza que resuelva igual tus tareas.
La prueba que importa es tu conjunto de evaluación: 50 a 100 tareas reales, ejecutadas con ambas versiones, comparando resultados. La degradación no es uniforme. Suele notarse antes en aritmética, generación de código, seguimiento de instrucciones con muchas restricciones y contextos largos que en conversación general o resúmenes. Si tu caso de uso está en el primer grupo, mide antes de bajar de 5 bits.
Los modelos pequeños toleran peor la cuantización agresiva. Un modelo de 70B en 4 bits suele conservar mejor sus capacidades que uno de 3B en 4 bits. Por debajo de 4 bits (Q3, Q2), la pérdida en modelos pequeños deja de ser sutil.
Errores frecuentes y limitaciones
Comparar un modelo grande cuantizado con uno pequeño sin cuantizar solo por memoria, sin medir. Con la misma VRAM, a veces gana el grande en Q4 y a veces el pequeño en Q8. Depende de la tarea, y la única forma de saberlo es evaluarlos con tus datos.
Cuantizar la caché KV sin probar. llama.cpp permite reducirla a 8 bits con --cache-type-k q8_0 --cache-type-v q8_0 (la de valores exige activar flash attention con -fa), lo que divide su memoria por dos aproximadamente. Suele funcionar bien en 8 bits; en 4 bits la calidad en contextos largos puede caer de forma notable.
Descargar una versión cuantizada por un tercero sin revisar cómo se generó. Algunas conversiones usan plantillas de chat incorrectas o tokenizadores desactualizados, y el modelo «parece tonto» por un problema que no tiene nada que ver con los bits.
Y cuándo no cuantizar: si tienes VRAM de sobra para FP16 o BF16 y la latencia te sobra, no ganas nada aceptando una pérdida de calidad. Tampoco conviene entrenar sobre pesos cuantizados salvo con técnicas pensadas para ello, como QLoRA.
Punto de partida razonable
Empieza en Q5_K_M o Q4_K_M, reserva memoria para la caché KV del contexto que realmente usas (no el máximo que admite el modelo) y ejecuta tu conjunto de evaluación contra la versión en Q8_0 como referencia. Si la diferencia en tus tareas es aceptable, baja un nivel y repite; si no, sube.


