Optimización de inferencia: cómo servir modelos más rápido y más barato
Has elegido el modelo, diseñado el flujo agéntico, y calibrado los evals. Llega el momento de producción, y surge la pregunta: “¿por qué es tan lento? ¿por qué es tan caro?”
La inferencia de modelos grandes es cara y lenta por naturaleza. Pero hay cuatro palancas principales para acelerarla y abaratarla — y lo mejor es que no compiten entre sí. Se pueden combinar todas.
Destilación: entrenar un modelo pequeño para imitar a uno grande
La destilación entrena un modelo más pequeño (el “estudiante”) para imitar el comportamiento de uno más grande y preciso (el “maestro”). El estudiante aprende no solo las respuestas correctas, sino la distribución de probabilidades completa del maestro sobre el vocabulario — el llamado conocimiento blando (soft knowledge).
Cómo funciona: el maestro genera logits para cada token; se aplica una temperatura alta para suavizar esa distribución (el mismo concepto de temperatura de Parte I); el estudiante se entrena para minimizar la diferencia entre su propia distribución y la del maestro, además de la pérdida supervisada estándar. El resultado: el estudiante aprende no solo qué respuesta es correcta, sino por qué el maestro la prefería.
Caso real — familia DeepSeek-R1:
| Modelo | Parámetros | VRAM aproximada | Precisión (MATH-500) |
|---|---|---|---|
| DeepSeek-R1 | 671B | ~1,543 GB | 97.3% |
| Distill-Llama-70B | 70B | ~181 GB | 94.5% |
| Distill-Qwen-32B | 32B | ~82 GB | 92.0% |
| Distill-Qwen-14B | 14B | ~36 GB | 88.0% |
| Distill-Qwen-7B | 7B | ~18 GB | 83.9% |
| Distill-Qwen-1.5B | 1.5B | ~3.9 GB | 72.0% |
La versión 70B retiene ~97% de la precisión original con 8.5× menos memoria. La versión 7B retiene ~86% de la precisión con 85× menos memoria — pero fíjate en la gráfica cómo la caída se acelera notablemente entre 7B y 1.5B: la destilación tiene rendimientos claramente decrecientes, no una pérdida lineal.
Cuándo usarla: el modelo base es demasiado grande para tu hardware, necesitas reducir costo de inferencia, y tienes acceso a un buen modelo maestro. Cuándo no: necesitas máxima precisión para tareas muy complejas, no tienes un buen maestro disponible, o el costo de entrenar al estudiante supera el ahorro esperado en inferencia.
Cuantización: menos precisión numérica, misma arquitectura
Reduce la precisión numérica de los pesos — de 32 o 16 bits a 8 o 4 bits — reduciendo tamaño y acelerando inferencia. Es como convertir una foto de alta resolución a una de menor resolución: se pierden detalles, pero pesa mucho menos y se procesa más rápido.
Tipos: PTQ (post-entrenamiento, rápida y sencilla, con checkpoints pre-cuantizados frecuentemente disponibles), cuantización dinámica (aplicada en tiempo de inferencia, más precisa pero más lenta), y QAT (el modelo se entrena considerando la cuantización desde el inicio — máxima precisión, más caro y lento).
Formatos populares: GGUF (llama.cpp, eficiente en CPU/GPU, hardware limitado), GPTQ (optimizado para GPU NVIDIA), AWQ (mayor precisión que GPTQ a tamaño comparable), bitsandbytes/NF4 (usado en QLoRA, Capítulo 4b).
Benchmarks reales (deepsense.ai):
| Modelo | Hardware | Velocidad base (FP16) | Velocidad cuantizada (AWQ) | Ganancia |
|---|---|---|---|---|
| DeepSeek 7B | RTX 4090 (24GB) | 52 tok/s | 130 tok/s | 2.5× |
| DeepSeek 32B | AWS g5.12xlarge | 22 tok/s | 50 tok/s | 2.3× |
| Mistral 7B | AWS g5.xlarge | 28 tok/s | 88 tok/s | 3.1× |
| Llama 3.3 70B | AWS g5.48xlarge | 23 tok/s | 46 tok/s | 2.0× |
Modelos pequeños ganan 2.5-3× en velocidad; modelos grandes ganan ~2×. Con 8 bits, la pérdida de precisión suele ser mínima; con 4 bits, algo mayor pero generalmente aceptable para la mayoría de tareas, con un ahorro de memoria de aproximadamente 75%.
Cuándo usarla: casi siempre, salvo que necesites máxima precisión numérica (finanzas, matemática compleja) o el modelo ya quepa cómodamente en VRAM con velocidad suficiente en FP16. En términos prácticos, 4 bits suele ser un buen punto de partida para la mayoría de los casos; si aparecen fallos consistentes en tareas específicas, vale la pena escalar a 6 u 8 bits.
Batching continuo: maximizar el uso de la GPU con múltiples usuarios
En lugar de procesar una solicitud a la vez, se agrupan varias para procesarlas en paralelo, aprovechando la capacidad de la GPU.
Batching estático (el problema): tamaño de lote fijo — hay que esperar a que termine la secuencia más larga del lote antes de seguir, un cuello de botella real. Batching continuo (la solución): el tamaño del lote se decide en cada iteración; en cuanto una secuencia termina, se inserta una nueva en su lugar. Frameworks como vLLM y TGI lo manejan internamente.
Analogía: un chef que cocina 4 platos a la vez. Con batching estático, esperas a tener 4 pedidos antes de empezar — si uno es complejo, los otros 3 esperan. Con batching continuo, en cuanto un plato está listo, empiezas el siguiente sin esperar a los demás.
El trade-off, con un ejemplo ilustrativo (no cifras medidas, solo para mostrar la dirección del efecto): sin batching, una latencia baja pero un throughput limitado (pocas solicitudes por segundo); con batching continuo, la latencia individual sube algo, pero el throughput total crece mucho más — la GPU, que tiene un costo fijo, se aprovecha mucho mejor.
Cuándo usarlo: múltiples usuarios concurrentes, el throughput importa más que la latencia individual, quieres maximizar el uso de una GPU cuyo costo ya es fijo. Cuándo no: la latencia de cada solicitud es crítica (chat en vivo con un solo usuario), o tienes muy pocos usuarios concurrentes, donde el overhead de coordinar el batching puede no compensar.
KV Cache: evitar recalcular lo que ya calculaste
Un LLM genera token por token; sin optimización, cada paso recalcularía los scores de atención sobre todo el contexto acumulado — prohibitivamente lento. La KV Cache almacena y reutiliza los cálculos de atención (claves y valores) de tokens anteriores, en vez de recalcularlos.
Técnicas avanzadas:
| Técnica | Qué hace | Beneficio |
|---|---|---|
| PagedAttention (vLLM) | Almacena la KV cache en páginas de memoria no contiguas, como un sistema operativo | Reduce fragmentación, usa la memoria de forma más eficiente |
| KV Cache cuantizada | Guarda claves y valores con menor precisión (Q4, Q8) | Menos memoria, ligera pérdida de precisión |
| Chunked Prefill | Divide el procesamiento del prompt en fragmentos, ejecutados junto con la generación | Mejora throughput con alta concurrencia |
| Prefix Caching | Cachea y reutiliza el cómputo de prefijos compartidos entre prompts (ej. el prompt de sistema) | Reduce drásticamente el TTFT en sistemas con prompts largos y repetidos |
Cuándo usarla: prácticamente siempre — es estándar en todos los frameworks de inferencia modernos, y especialmente valiosa con texto largo (>512 tokens) o contexto extenso (>8K). La compensación es memoria extra a cambio de velocidad — si la VRAM es escasa, puede volverse el propio cuello de botella, y ahí conviene reducir el contexto máximo o cuantizar la caché.
Combinando todo: por qué las técnicas no compiten
| Técnica | Resuelve | Costo (trade-off) |
|---|---|---|
| Destilación | Modelo demasiado grande/lento en general | Pérdida de precisión (variable) |
| Cuantización | Uso de memoria y velocidad de cómputo | Pérdida de precisión (menor que destilación) |
| Batching continuo | Bajo throughput con múltiples usuarios | Mayor latencia por solicitud individual |
| KV Cache | Recómputo redundante en generación larga | Mayor uso de memoria |
Un despliegue típico combina: un modelo destilado y/o cuantizado, servido con un framework (vLLM, TGI) que implementa batching continuo y KV cache automáticamente, con PagedAttention y chunked prefill para maximizar eficiencia de memoria, y prefix caching si el prompt de sistema es largo y repetido — opcionalmente, con decodificación especulativa para acelerar aún más.
Ejemplo ilustrativo de configuración, para un chatbot de RR.HH. (números de referencia, no medidos en un despliegue real): modelo destilado de tamaño mediano, cuantizado a 4 bits, en una sola GPU de consumo alto, con contexto de 16K (suficiente para políticas de RR.HH.), batching continuo, y KV cache a 8 bits. Bajo esta configuración sería razonable esperar una mejora sustancial tanto en velocidad como en costo por solicitud, comparado con depender exclusivamente de una API externa para cada llamada — la magnitud exacta depende enteramente de tu modelo, tu tráfico real, y tu hardware específico, y debe medirse, no asumirse de una tabla genérica.
El marco de decisión
| Pregunta | Si SÍ… | Si NO… |
|---|---|---|
| ¿El modelo no cabe en VRAM? | Cuantización o destilación | La cuantización sigue siendo recomendable, aunque no urgente |
| ¿Es >70B y muy lento? | Destilación (a 32B/14B) + cuantización | Puedes priorizar otras técnicas |
| ¿Tienes >10 usuarios concurrentes? | Batching continuo (vLLM/TGI) | El batching puede no ser necesario todavía |
| ¿Generas texto largo (>512 tokens)? | KV Cache con PagedAttention | La KV cache sigue siendo útil, aunque menos crítica |
| ¿El costo por token es demasiado alto? | Destilación + cuantización | Puedes priorizar velocidad sobre costo |
| ¿La latencia (TTFT) es el problema? | Prefix caching, o reduce el contexto | El cuello de botella es la generación (tok/s), no el arranque |
| ¿Necesitas máxima precisión? | Cuantización a 8 bits o sin cuantizar; evita destilación | 4 bits suele ser suficiente |
Un punto de partida razonable: la cuantización suele ser la optimización más barata de implementar con mejor relación beneficio/costo, así que conviene empezar por ahí. Si el modelo sigue siendo demasiado lento o grande, la destilación es el siguiente paso lógico. Si hay múltiples usuarios concurrentes, el batching continuo se vuelve relevante. Y la KV cache, en la práctica, casi siempre debería estar activa — es una optimización estándar, no una decisión de caso por caso.
Conexión con la Parte II y con seguridad
Con la arquitectura de agentes (Parte II): un agente ReAct que hace 5 vueltas genera varias veces más tokens que una respuesta simple — optimizar cada vuelta importa más que optimizar una sola respuesta aislada. Un coordinador añade una llamada extra solo para clasificar intención, con su propio costo de latencia y tokens — si esa clasificación es lenta, todo el sistema se resiente.
Con seguridad y observabilidad: la cuantización puede afectar la precisión del modelo en tareas sensibles (clasificar una queja como urgente, por ejemplo) — si el modelo pierde demasiada precisión, puede fallar en detectar algo importante. El batching continuo añade latencia por solicitud, lo cual puede importar en sistemas que necesitan responder rápido ante una situación de riesgo. Monitorear estas métricas en producción es central para detectar degradación de rendimiento con el tiempo — algo que veremos a fondo en el capítulo de observabilidad más adelante en esta parte.
Resumen del capítulo
- Destilación: un modelo pequeño imita a uno grande — trade-off entre tamaño y precisión, con rendimientos claramente decrecientes en los tamaños más pequeños.
- Cuantización: menos precisión numérica, menos memoria, más velocidad — casi siempre vale la pena aplicarla en algún grado.
- Batching continuo: procesa múltiples solicitudes en paralelo — mejora throughput, a costa de algo de latencia individual.
- KV Cache: reutiliza cálculos de atención ya hechos — ahorra tiempo, consume memoria.
- Las cuatro técnicas no compiten — un despliegue de producción real las combina.
Cuando una empresa pregunta “¿por qué nuestro chatbot es tan caro o tan lento?”, la respuesta casi nunca es “necesitas un modelo más grande” — con más frecuencia es “hace falta aplicar estas cuatro palancas, en el orden que tu situación específica justifique”.
LinkedIn nelson.zepeda@simov.io SIMOV LABS
¿Encontraste un error? Sugerir una corrección