Guía Llama.cpp: Capítulo 4 — Optimización avanzada de modelos
Instalado el motor y configurada la GPU, toca exprimir el otro componente del binomio: el modelo. En este capítulo tratamos la cuantización con detalle (incluida la fabricación de tus propios GGUF), la KV-cache cuantizada, el speculative decoding y las medidas de rendimiento serias.
1. Familias de cuantización
llama.cpp separa sus cuantizaciones en dos grandes familias:
1.1 K-Quants (por bloques)
Agrupan pesos en super-bloques con escalas por bloque. Son la opción por defecto y el equilibrio calidad/tamaño mejor contrastado:
| Cuantización | Bits/peso | Uso recomendado |
|---|---|---|
Q2_K |
~2.6 | Mirar si funciona; calidad notablemente inferior |
Q3_K_M |
~3.9 | Ajuste apurado en VRAM |
Q4_K_S |
~4.5 | Buena con presupuesto justo |
Q4_K_M |
~4.8 | El punto dulce universal |
Q5_K_M |
~5.7 | Cuando cabe; ganancia perceptible de calidad |
Q6_K |
~6.6 | Casi FP8 en calidad |
Q8_0 |
~8.5 | Referencia "sin pérdida" práctica |
1.2 I-Quants (importance matrix)
IQ1_S … IQ4_XS necesitan una matriz de importancia (imatrix) calculada sobre un corpus de calibración. Brillan en tamaños muy pequeños y en modelos grandes con presupuesto duro:
IQ4_XS— rival directo deQ4_K_M: algo más pequeño con calidad similar.IQ2_XXS/IQ2_XS— la mejor calidad posible en el rango 2-bit (impensable sin imatrix).IQ1_S— experimentos extremos (1.5 bits); solo para modelos muy grandes.
💡 Para elegir entre las distintas variantes y comparar MMLU/perplexidad de cada cuantización, los repositorios de HuggingFace - Bartowski y HuggingFace - MaziyarPanahi publican benchmarks por cuantización.
2. Crear tu propia cuantización con llama-quantize
Si el modelo solo existe en FP16/BF16, o quieres un nivel intermedio no publicado:
2.1 Cuantización estándar
./llama-quantize modelo-f16.gguf modelo-Q4_K_M.gguf Q4_K_M
2.2 Con imatrix (I-Quants)
# 1) Generar la matriz de importancia (usa un corpus representativo, 1-2 MB de texto en el idioma de uso)
./llama-imatrix -m modelo-f16.gguf -f corpus.txt -o imatrix.gguf --chunks 200
# 2) Cuantizar apoyándose en ella
./llama-quantize --imatrix imatrix.gguf modelo-f16.gguf modelo-IQ4_XS.gguf IQ4_XS
Recomendaciones:
- El corpus debe parecerse al uso real (código para modelos de código, español para chat en español).
- Unas pocas MB de texto bastan; más corpus no mejora proporcionalmente.
3. KV-cache cuantizada
La KV-cache escala linealmente con el contexto y multiplica por el número de capas y cabezas; con contextos largos llegó a ser comparable al peso del modelo. Comprimirla es la palanca más rentable:
./llama-server -m models/modelo.gguf -ngl 99 -c 32768 \
--cache-type-k q8_0 --cache-type-v q8_0 -fa
Puntos clave:
q8_0es prácticamente gratis en calidad y reduce a la mitad el footprint.q4_0en K y V permite contextos aún mayores, con algo más de pérdida; evalúa con tus casos.-fa(FlashAttention) es casi obligatorio con KV cuantizada: algunos backends y combinaciones la exigen o se degradan en velocidad.- En modelos nuevos, revisa siempre las notas del proyecto: hay arquitecturas con particularidades en V-cache.
4. Speculative decoding
Idea: un modelo borrador (draft) pequeño propone K tokens y el modelo objetivo los verifica en un paso. Si acierta, generas varios tokens por pasada objetivo.
./llama-server -m models/grande-70b.gguf \
-md models/pequeno-1b.gguf \
-ngl 99 -ngld 99 \
-ha
-md— modelo draft;-ngld— capas del draft en GPU;-ha— KV-cache compartida entre modelos.- Ganancia realista: 1.5×–3× cuando el draft especializado en el dominio (p. ej. un modelo de código pequeño con el objetivo grande).
- Costo fijo: más VRAM para el draft. La calidad matematica no cambia: los tokens siempre salen validados por el modelo grande.
💡 Ideal en generación de código con batches cortos: el draft pequeño propone cadenas típicas (imports, paréntesis, nombres de variables) que el modelo grande valida de golpe.
5. Middleware de rendimiento: llama-bench
llama-bench es el micro-benchmark del proyecto. Mide en condiciones controladas:
./llama-bench -m models/modelo-Q4_K_M.gguf -m models/modelo-Q5_K_M.gguf \
-ngl 99 -p 512 -n 128 -fa
- Mide tg/s (tokens de generación por segundo) y pp/s (prompt processing por segundo).
- pp importa en RAG (ingerir documentos); tg importa en chat (velocidad de lectura).
- Prueba siempre tu nivel de paralelismo real (
-np), no el caso sintético de 1 usuario.
6. Estrategia global de optimización (orden recomendado)
- Disminuye el peso: la mejor cuantización que entra en VRAM (Q4_K_M como inicio).
- Contexto ajustado: el máximo que uses de verdad, no el máximo que puedas poner.
- Activar
-fay KV-cacheq8_0(oq4_0si el contexto es gigante). -ngl 99y comprobar que no hay fuga de la VRAM a memoria compartida (RAM), visible cuando el uso de RAM sube con el modelo.llama-benchpara validar cada cambio; una variante a la vez, conservando las métricas.- Speculative decoding si el caso es generación repetitiva o de código.
- Solo después considerar más slots de concurrencia.
7. Tabla de solución de problemas rápida (rendimiento)
| Problema | Causa | Mitigación |
|---|---|---|
| TG/seg caen al 10 % tras terminar el contexto | KV-cache fuera de VRAM | Bajar -c, cuantizar KV o -ngl menor |
| Prompt processing lento en GPU | -ub demasiado pequeño |
Subir -ub hasta 256–512 (o -b grande) |
| Calidad notablemente peor en Q2 | Cuantización agresiva | Pasar a Q3_K_M, o IQ con imatrix |
| Primera respuesta lenta | Prompt enorme sin -ub suficiente |
-ub 512, prompt pre-cachado con -p o cache prompt del servidor |
| Draft no aporta ganancia | Draft/objetivo muy distintos | Draft de la misma familia o afinado al dominio |
Con el modelo afinado, resta el último peldaño: integrarlo en tu stack y resolver los problemas del mundo real en el capítulo 5.