Hola a todos, Nina aquí, de regreso en agntbox.com. Hoy quiero hablar sobre algo que ha estado zumbando en mis canales de Slack y acechando mis sesiones de codificación nocturnas: los marcos de inteligencia artificial. Específicamente, quiero centrarme en un rincón particular que a menudo se pasa por alto cuando todos están ocupados persiguiendo el último LLM: las herramientas para el ajuste fino y la implementación de modelos más pequeños y especializados.
Todos conocemos los grandes nombres: TensorFlow, PyTorch. Son los gigantes, los jugadores establecidos. ¡Y con buena razón! Son increíblemente poderosos y versátiles. Pero seamos realistas, a veces no necesitas un acorazado para cruzar un estanque. A veces, necesitas un ágil bote de velocidad, especialmente cuando estás trabajando con un plazo ajustado, con un conjunto de datos específico y un objetivo claro en mente. Ahí es donde quiero hablar sobre algo con lo que he estado muy cómoda últimamente: Hugging Face Optimum para ONNX Runtime.
Ahora, antes de que tus ojos se nublen con acrónimos, déjame desglosarlo. Hugging Face, por supuesto, es el favorito del mundo de NLP, haciendo que los modelos preentrenados sean accesibles para todos. ONNX (Open Neural Network Exchange) es un estándar abierto para representar modelos de aprendizaje automático, lo que básicamente significa que puedes convertir modelos entrenados en un marco (como PyTorch) y ejecutarlos en otro (como TensorFlow, o en nuestro caso, ONNX Runtime). ¿Y ONNX Runtime? Es el motor de inferencia de alto rendimiento de Microsoft.
Entonces, ¿qué hace Hugging Face Optimum por ONNX Runtime? Básicamente es un puente, un optimizador y un asistente de implementación todo en uno. Te ayuda a tomar tus modelos de Hugging Face, optimizarlos para la inferencia en ONNX Runtime y, a menudo, obtener un aumento significativo en la velocidad sin sacrificar mucho (o ninguna) precisión. Y eso, amigos míos, es oro.
Por qué estoy obsesionada con Optimum ONNX Runtime ahora mismo
Mi viaje en Optimum ONNX Runtime comenzó, como la mayoría de mis obsesiones tecnológicas, con un problema. Estaba trabajando en un proyecto para un cliente que involucraba la implementación de un modelo relativamente pequeño basado en BERT para la clasificación de texto en tickets de soporte al cliente. El modelo se entrenó en un conjunto de datos personalizado y, aunque funcionó muy bien, el tiempo de inferencia era un poco lento para la interacción en tiempo real con el cliente. Hablamos de tal vez 150-200 ms por inferencia en una GPU potente, que no está mal, pero para aplicaciones en tiempo real y de alto volumen, cada milisegundo cuenta.
Probé todos los sospechosos habituales: agrupamiento, optimización de tuberías de entrada, incluso un poco de cuantización básica. Obtuvimos algunas mejoras, pero nada dramático. Luego, un colega mencionó Optimum, y específicamente, su integración con ONNX. Al principio, fui escéptica. ¿Otra capa de abstracción? ¿Más dependencias? Pero estaba desesperada, así que me sumergí.
Lo que encontré fue un flujo de trabajo sorprendentemente sencillo que dio resultados. Logramos reducir nuestro tiempo de inferencia a alrededor de 50-70 ms por inferencia en la misma GPU y vimos un rendimiento decente en una CPU para tareas menos críticas. Eso es un aumento de 2-3 veces, lo que en el mundo de la IA en tiempo real, es una gran victoria. Significó que podíamos escalar nuestro servicio de manera mucho más eficiente y proporcionar respuestas más rápidas a los clientes, impactando directamente en su experiencia.
El problema que resuelve: rendimiento y portabilidad
Seamos honestos, implementar modelos de IA puede ser un dolor de cabeza. Entrenas un hermoso modelo en PyTorch y luego tienes que averiguar cómo hacerlo funcionar de manera eficiente en producción. A veces, te quedas atascado con una configuración de hardware específica o necesitas implementar en un dispositivo de borde con recursos limitados. Aquí es donde entra ONNX. Proporciona un formato común, desacoplando tu modelo del marco de entrenamiento.
Optimum lleva esto un paso más allá. No se trata solo de convertir a ONNX; se trata de optimizar ese modelo ONNX. Puede aplicar técnicas como optimizaciones de gráficos, fusión de operadores e incluso cuantizar tu modelo para reducir su tamaño y acelerar la inferencia, a menudo con un impacto mínimo en la precisión. Esto es especialmente útil para modelos más pequeños o cuando estás limitado por memoria o capacidad de cómputo en tu objetivo de implementación.
El caso de uso de mi cliente fue un ejemplo perfecto. Teníamos un modelo de PyTorch, pero queríamos implementarlo en una instancia de nube con GPUs de NVIDIA y necesitábamos el máximo rendimiento. Optimum ONNX Runtime nos permitió exportar el modelo, aplicar optimizaciones específicas para nuestro hardware objetivo y hacerlo funcionar como un sueño.
Cómo empezar con Optimum ONNX Runtime: un ejemplo práctico
Vamos a recorrer un ejemplo básico de cómo podrías usar Optimum para exportar y optimizar un modelo de Hugging Face para ONNX Runtime. Para esto, utilizaremos un simple modelo de análisis de sentimiento.
Primero, necesitarás instalar las bibliotecas necesarias:
pip install transformers optimum[onnxruntime] onnx
Ahora, escribamos un poco de código en Python para exportar un modelo de análisis de sentimiento preentrenado.
Ejemplo 1: Exportar un modelo de análisis de sentimiento
Aquí, estamos tomando un modelo estándar `distilbert-base-uncased-finetuned-sst-2-english` y exportándolo a formato ONNX utilizando Optimum.
from transformers import pipeline
from optimum.onnxruntime import ORTModelForSequenceClassification
from transformers import AutoTokenizer
model_id = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(model_id)
# Exportar el modelo a ONNX
# El `save_directory` es donde se guardará tu modelo ONNX y el tokenizador.
# `opset` especifica la versión del conjunto de operadores ONNX.
# `input_names` son importantes para definir las entradas de tu gráfico ONNX.
onnx_path = "./onnx_sentiment_model/"
ort_model = ORTModelForSequenceClassification.from_pretrained(model_id, from_transformers=True)
ort_model.save_pretrained(onnx_path)
tokenizer.save_pretrained(onnx_path)
print(f"Modelo y tokenizador exportados a {onnx_path}")
Después de ejecutar esto, tendrás un directorio llamado `onnx_sentiment_model` que contiene tu archivo `model.onnx` y los archivos del tokenizador. Este archivo `model.onnx` es la versión optimizada lista para ONNX Runtime.
Ejemplo 2: Ejecutar inferencia con el modelo ONNX
Ahora, carguemos ese modelo exportado y realicemos algunas inferencias con él. Nota cómo cargamos `ORTModelForSequenceClassification` directamente desde el `onnx_path` al que lo guardamos.
from optimum.onnxruntime import ORTModelForSequenceClassification
from transformers import AutoTokenizer, pipeline
import time
onnx_path = "./onnx_sentiment_model/"
# Cargar el modelo ONNX y el tokenizador
ort_model = ORTModelForSequenceClassification.from_pretrained(onnx_path)
tokenizer = AutoTokenizer.from_pretrained(onnx_path)
# Crear una tubería para inferencias fáciles
onnx_pipeline = pipeline(
"sentiment-analysis",
model=ort_model,
tokenizer=tokenizer,
accelerator="ort" # Esto le dice a la tubería que use ONNX Runtime
)
text_samples = [
"Me encanta este producto, ¡es asombroso!",
"Esta película fue solo aceptable, un poco aburrida.",
"Realmente odio esperar en largas colas.",
"El servicio fue increíblemente rápido y eficiente."
]
print("\n--- Ejecutando Inferencia con el Modelo ONNX ---")
start_time = time.time()
results = onnx_pipeline(text_samples)
end_time = time.time()
for i, res in enumerate(results):
print(f"Texto: '{text_samples[i]}' -> Etiqueta: {res['label']}, Puntaje: {res['score']:.4f}")
print(f"Tiempo de inferencia para {len(text_samples)} muestras: {(end_time - start_time):.4f} segundos")
Cuando ejecutas esto, verás las predicciones de sentimiento. Más importante aún, si comparas el tiempo de inferencia con el modelo original de PyTorch en el mismo hardware, probablemente observarás un aumento notable en la velocidad, especialmente para lotes más grandes o modelos más complejos. El parámetro `accelerator=”ort”` en la tubería es una pequeña pero poderosa bandera que le dice a Hugging Face que use ONNX Runtime para la inferencia, que es donde ocurre la magia.
Ejemplo 3: Controlando las características de optimización (opcional pero potente)
Optimum permite un control detallado sobre el proceso de optimización. Por ejemplo, puedes especificar el nivel de optimización o incluso elegir optimizaciones de gráficos específicas. Esto puede ser crucial cuando intentas exprimir hasta la última gota de rendimiento de tu modelo o cuando necesitas hacer compromisos entre velocidad y precisión (por ejemplo, con la cuantización).
from optimum.onnxruntime import ORTModelForSequenceClassification, ORTConfig
from transformers import AutoTokenizer
from optimum.exporters.tasks import TasksManager
from optimum.onnxruntime.configuration import AutoOptimizationConfig
model_id = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(model_id)
# Definir la configuración de optimización
# Aquí, estamos utilizando la optimización predeterminada, pero puedes personalizar más
# Por ejemplo, para aplicar cuantización: `optimization_config = AutoOptimizationConfig.O2()`
optimization_config = AutoOptimizationConfig.O1() # O1 para optimización básica, O2 para más agresiva, O3 para completa
# Exportar el modelo con la configuración de optimización explícita
onnx_path_optimized = "./onnx_sentiment_model_optimized/"
task = TasksManager.get_task_from_model_or_model_name(model_id)
# Puede que necesites ajustar el parámetro `feature` según la tarea de tu modelo
# Para la clasificación de secuencias, a menudo es 'sequence-classification'
ORTModelForSequenceClassification.from_pretrained(
model_id,
from_transformers=True,
export_feature=task.feature,
optimization_config=optimization_config,
).save_pretrained(onnx_path_optimized)
tokenizer.save_pretrained(onnx_path_optimized)
print(f"Modelo optimizado y tokenizador exportados a {onnx_path_optimized}")
El `AutoOptimizationConfig` es tu aliado aquí. `O1` proporciona optimizaciones básicas del gráfico, `O2` añade fusiones más agresivas y eliminaciones de nodos, y `O3` incluye todas las optimizaciones disponibles, incluida la cuantización si es aplicable. Elegir el nivel correcto depende de tus necesidades específicas y del hardware que estás utilizando. Para mi cliente, experimentamos entre `O1` y `O2` para encontrar el punto óptimo, inclinándonos hacia `O2` para lograr el mejor equilibrio entre velocidad y precisión.
Mis Conclusiones y Lo Que Viene
Mi experiencia con Hugging Face Optimum para ONNX Runtime ha sido abrumadoramente positiva. No es una solución mágica para cada implementación de IA, pero aborda una necesidad muy común y crítica: hacer que tus modelos funcionen más rápido y de manera más eficiente en producción, especialmente cuando estás trabajando con modelos de Hugging Face.
- Aumento de Rendimiento: El beneficio principal es la reducción significativa en el tiempo de inferencia. Para aplicaciones en tiempo real, esto puede ser un cambio considerable, mejorando la experiencia del usuario y reduciendo los costos de infraestructura.
- Portabilidad: Al convertir a ONNX, tus modelos se vuelven más portátiles, ejecutables en diferentes hardware y sistemas operativos sin estar atados a un marco de aprendizaje profundo específico.
- Facilidad de Uso: La integración con la biblioteca `transformers` de Hugging Face es notablemente fluida. Si ya estás familiarizado con Hugging Face, la curva de aprendizaje para Optimum es bastante suave.
- Eficiencia de Recursos: Los modelos optimizados a menudo requieren menos memoria y ciclos de CPU/GPU, lo cual es crucial para implementaciones en dispositivos o entornos en la nube sensibles al costo.
Una cosa que he aprendido es que vale la pena experimentar con diferentes niveles y configuraciones de optimización. No te conformes solo con la configuración predeterminada. Prueba `O1`, `O2`, e incluso `O3` (con cuantización si tu caso de uso lo permite) y mide los resultados en tu hardware objetivo real. ¡Las ganancias pueden ser sorprendentes!
De cara al futuro, creo que herramientas como Hugging Face Optimum se volverán aún más esenciales. A medida que los modelos de IA proliferan y se mueven a entornos de implementación más diversos, la capacidad de optimizar y agilizar su inferencia será fundamental. Estoy especialmente emocionado de ver cómo Optimum evoluciona con nuevos aceleradores de hardware y técnicas de cuantización más avanzadas.
Conclusiones Accionables para Tu Próximo Proyecto de IA:
- Evalúa Tus Necesidades de Inferencia: Antes de sumergirte en la optimización, define claramente tus requisitos de rendimiento. ¿Cuál es una latencia aceptable? ¿Cuál es tu objetivo de rendimiento?
- Considera ONNX Temprano: Si estás utilizando modelos de Hugging Face y el rendimiento es una preocupación, comienza a pensar en la exportación y optimización a ONNX durante tu ciclo de desarrollo, no solo en la implementación.
- Benchmark, Benchmark, Benchmark: Siempre mide la mejora real en el rendimiento (o degradación) después de aplicar optimizaciones. No te fíes de las ganancias teóricas. Utiliza datos reales y hardware real.
- Experimenta con Niveles de Optimización: No uses solo la configuración predeterminada. Juega con `AutoOptimizationConfig.O1()`, `O2()`, y `O3()` para encontrar el mejor equilibrio para tu modelo y caso de uso.
- Mantente Actualizado: La biblioteca Hugging Face Optimum está en desarrollo activo. Mantente atento a sus lanzamientos y documentación para nuevas características y mejoras en el rendimiento.
¡Eso es todo por mí esta semana! Si has tenido problemas con el rendimiento de la implementación de modelos, prueba Hugging Face Optimum para ONNX Runtime. Quizás sea el bote rápido que necesitas. Déjame saber en los comentarios si lo has utilizado o si tienes otras herramientas preferidas para la optimización de modelos. ¡Feliz inferencia!
🕒 Published: