optimización de inferencia: cómo reducir latencia y coste sin perder calidad
La optimización de inferencia permite servir predicciones de modelos de inteligencia artificial con menor latencia, consumo de recursos y coste operativo. El reto no consiste simplemente en hacer que un modelo responda más rápido: también hay que conservar una calidad aceptable, soportar la demanda real y evitar cambios que vuelvan el sistema difícil de mantener. Una estrategia eficaz empieza por medir el servicio completo y termina con decisiones basadas en el comportamiento observado, no en una lista de trucos aplicada por costumbre.
Esto afecta tanto a sistemas de recomendación y clasificación como a aplicaciones con modelos generativos. En una API conversacional, por ejemplo, el tiempo hasta el primer token y el tiempo total de generación importan de maneras distintas. En un clasificador de fraude, en cambio, la prioridad puede ser mantener una respuesta estable en milisegundos durante picos de tráfico. La optimización debe partir de ese contexto de uso.
El primer trabajo es encontrar dónde se consume el tiempo
La inferencia no ocurre en un único paso. Una petición puede incluir validación, preparación de datos, transferencia a un acelerador, ejecución del modelo, decodificación y comunicación de la respuesta. Si se mide solo el tiempo de cómputo del modelo, es fácil optimizar una parte pequeña mientras el cuello de botella permanece en otra.
Conviene separar las métricas por etapa y observarlas bajo una carga que se parezca a la producción. Como mínimo, se suelen analizar la latencia p50 y p95, el rendimiento en solicitudes o tokens por segundo, el uso de CPU y GPU, la memoria ocupada, la tasa de errores y el coste por predicción. La mediana describe una petición habitual; el percentil 95 ayuda a detectar demoras que afectan a una parte significativa de los usuarios. El promedio, por sí solo, puede ocultar ambas realidades.
También hay que distinguir latencia y rendimiento. Una configuración puede procesar más solicitudes por segundo gracias al batching, pero hacer que cada petición espere más antes de recibir respuesta. En un proceso nocturno de análisis, ese intercambio quizá sea conveniente. En una interfaz interactiva, puede empeorar la experiencia aunque la capacidad agregada aumente.
- Si la GPU está poco utilizada, puede haber esperas por transferencias, lotes demasiado pequeños o límites en la capa de servicio.
- Si la memoria está al límite, el tamaño del modelo, la longitud de las secuencias o las cachés pueden impedir aumentar la concurrencia.
- Si la latencia crece con el tráfico, hay que revisar colas, límites de concurrencia y escalado, no solo el rendimiento de una ejecución individual.
- Si el modelo termina rápido pero la respuesta no, el preprocesamiento, la serialización o la red pueden ser el problema dominante.
Una línea base útil registra estos datos con una versión concreta del modelo, hardware, configuración y conjunto de pruebas. Sin esa referencia, un cambio puede parecer beneficioso mientras desplaza el coste a otra etapa o reduce silenciosamente la precisión.
Elegir la técnica según el cuello de botella
Cuantización: menos precisión numérica, menor coste de ejecución
La cuantización representa pesos y, en algunos métodos, activaciones con formatos numéricos de menor precisión. Pasar de formatos de punto flotante de 32 bits a formatos de 16, 8 o incluso menos bits puede reducir memoria y acelerar operaciones, siempre que el hardware y el motor de inferencia aprovechen ese formato. No existe una mejora automática: una GPU puede ofrecer buen soporte para cierta precisión y otro dispositivo no, y el rendimiento efectivo depende de la arquitectura, el tamaño del modelo y el patrón de uso.
La decisión debe validarse con el conjunto de evaluación de la aplicación. En un modelo de clasificación, se pueden comparar exactitud, precisión y sensibilidad, prestando atención especial a clases poco frecuentes. En generación de texto, además de métricas automáticas, conviene revisar ejemplos representativos de respuestas, consistencia y cumplimiento de instrucciones. Una pérdida pequeña en una puntuación agregada puede concentrarse en los casos de mayor impacto.
Por ejemplo, una empresa que clasifica documentos puede descubrir que cuantizar reduce el consumo de memoria y permite ejecutar más réplicas en el mismo servidor. Si el error añadido afecta sobre todo a documentos ambiguos que ya pasan a revisión humana, puede ser un intercambio razonable. Si, por el contrario, degrada la detección de una categoría crítica, el ahorro no compensa.
Batching, concurrencia y ejecución optimizada
El batching agrupa varias entradas para procesarlas en una misma ejecución. Puede aprovechar mejor el paralelismo del acelerador, aunque acumular solicitudes para completar un lote introduce espera. Por eso es habitual configurar un tamaño máximo y un tiempo máximo de espera, y medir ambos efectos con tráfico realista. Los lotes estáticos funcionan en cargas previsibles; el batching dinámico se adapta a solicitudes que llegan en momentos distintos, pero añade complejidad al planificador.
En modelos generativos, el batching debe tener en cuenta que las entradas pueden tener longitudes muy diferentes. Un lote con una secuencia extensa y varias cortas puede desperdiciar cómputo o memoria, dependiendo de cómo se gestione el relleno. Agrupar por rangos de longitud puede ayudar, aunque no siempre mejora el tiempo de respuesta individual. La métrica de éxito debe reflejar la experiencia del usuario, no solo el uso de la GPU.
Otra opción es utilizar compilación, kernels especializados o motores de ejecución diseñados para el hardware disponible. Estas herramientas pueden fusionar operaciones y reducir transferencias intermedias. Sin embargo, una ruta optimizada para una familia de aceleradores puede limitar la portabilidad. Antes de adoptar un motor específico, hay que comprobar compatibilidad con las operaciones del modelo, facilidad de actualización y esfuerzo de diagnóstico cuando aparece una discrepancia numérica.
Modelos generativos: optimizar el flujo de tokens
En generación de texto, el coste no se limita a cargar el modelo. La fase de prefill procesa el prompt y la fase de decodificación produce tokens de manera secuencial. Un prompt largo puede aumentar mucho el tiempo inicial y la memoria necesaria para la caché de claves y valores, mientras que una respuesta larga prolonga la generación. Por eso es útil medir por separado el tiempo hasta el primer token, la velocidad de generación y el tiempo total.
La caché KV evita recalcular cierta información de tokens anteriores durante la decodificación. Su uso suele mejorar la eficiencia, pero consume memoria y puede restringir el número de conversaciones simultáneas. En sistemas con contexto extenso, el tamaño de esa caché puede convertirse en un límite antes que la propia memoria de los parámetros. Reducir el contexto innecesario, establecer límites razonables y gestionar la memoria de forma eficiente puede resultar más efectivo que cambiar de hardware.
La caché de respuestas es distinta: reutiliza una salida cuando una petición coincide con otra previa o se considera equivalente. Puede funcionar bien para consultas frecuentes y estables, como preguntas sobre políticas internas, pero es arriesgada cuando la respuesta depende de permisos, datos recién actualizados o contexto personal. Una caché debe incluir reglas claras de caducidad, separación por usuario o ámbito de acceso y tratamiento de información sensible. Una respuesta rápida pero desactualizada no es una optimización útil.
También se puede reducir el número de tokens generados mediante límites de salida, instrucciones más precisas o formatos estructurados. No se trata de recortar respuestas sin criterio: hay que identificar qué contenido aporta valor y qué extensión necesita cada tarea. Si una respuesta breve resuelve la consulta, generar varios párrafos añade coste y demora sin mejorar el resultado.
Un proceso de prueba que evita optimizaciones engañosas
La forma más fiable de optimizar es introducir cambios pequeños y reversibles. Primero se define una carga de prueba con solicitudes variadas, incluidos casos frecuentes, entradas largas y condiciones límite. Después se ejecuta la versión base y se registran latencia, rendimiento, consumo y calidad. Cada modificación se prueba por separado siempre que sea posible, para saber qué produjo la diferencia.
- Establecer un objetivo operativo. Definir, por ejemplo, un límite de latencia p95 y un coste máximo por mil solicitudes, junto con un umbral aceptable de calidad.
- Reproducir condiciones representativas. Incluir concurrencia, distribución de longitudes y patrones de tráfico semejantes a los del servicio real.
- Aplicar una intervención concreta. Cambiar precisión numérica, batching, motor o límites de generación, pero no varias cosas a la vez.
- Comparar resultados completos. Medir no solo el promedio, sino percentiles, errores, memoria y calidad por segmentos relevantes.
- Desplegar de forma gradual. Usar una parte pequeña del tráfico, observar incidencias y conservar una vía de reversión.
Un caso habitual ilustra por qué importa esta disciplina. Un equipo puede observar que su API responde lentamente y decidir cambiar a una GPU más potente. Sin embargo, al instrumentar el recorrido descubre que la mayor parte de la demora se produce antes de la ejecución, porque el servicio descarga artefactos con demasiada frecuencia y crea procesos de forma ineficiente. Mejorar el aprovisionamiento puede resolver el problema con menos coste y menor riesgo que renovar toda la infraestructura.
Las pruebas sintéticas también tienen límites. Una carga constante no reproduce necesariamente ráfagas, cancelaciones de solicitudes ni diferencias entre usuarios. Por eso, los resultados de laboratorio deben complementarse con observabilidad en producción y comparación controlada entre versiones. Es recomendable mantener conjuntos de evaluación versionados: los datos y criterios cambian, y una comparación válida necesita saber exactamente qué se midió.
Cuándo no conviene optimizar más
Reducir milisegundos o consumo no siempre aporta valor. Si el servicio cumple sus objetivos, el coste es aceptable y la calidad está protegida, una transformación compleja puede añadir deuda técnica sin beneficio perceptible. Esto es especialmente cierto cuando el volumen de uso es bajo o cuando el modelo se ejecuta en un proceso por lotes con plazos holgados.
Hay que evitar optimizaciones que dificulten las actualizaciones del modelo, creen dependencias exclusivas de un proveedor o hagan opaco el comportamiento ante errores. El ahorro de infraestructura puede quedar superado por el tiempo de ingeniería y mantenimiento. También conviene desconfiar de comparaciones que mezclan versiones distintas del modelo, tamaños de entrada diferentes o hardware no equivalente.
Antes de reducir precisión, cambiar el motor o cachear resultados, debe establecerse qué degradación sería tolerable y quién asume el riesgo. En aplicaciones médicas, financieras o de seguridad, los requisitos de validación pueden ser mucho más estrictos que en una función de recomendación de bajo impacto. La optimización técnica no sustituye la gobernanza ni la revisión del dominio.
Una decisión práctica para el siguiente paso
La optimización de inferencia funciona mejor como un ciclo: medir el servicio, localizar el cuello de botella, elegir una intervención compatible con el objetivo y comprobar sus efectos sobre coste, latencia y calidad. Para una GPU saturada, puede tener sentido explorar batching o menor precisión; para una latencia dominada por prompts extensos, quizá convenga reducir contexto; para un servicio con escasa utilización, mejorar el escalado y la concurrencia puede ser más útil que comprar aceleradores.
El siguiente paso razonable es construir una línea base reproducible y priorizar el problema que más perjudica al usuario o al presupuesto. Solo después conviene comparar técnicas. Así, la optimización de inferencia deja de ser una búsqueda de récords de velocidad y se convierte en una decisión de ingeniería: obtener el nivel de calidad requerido, con tiempos previsibles y recursos proporcionados a la carga real.
