Cómo Forethought ahorra más del 66% en costos para modelos de IA generativos utilizando Amazon SageMaker.

Forethought ahorra más del 66% en costos para IA generativa con Amazon SageMaker.

Este post fue co-escrito con Jad Chamoun, Director de Ingeniería en Forethought Technologies, Inc. y Salina Wu, Ingeniera Senior de Aprendizaje Automático en Forethought Technologies, Inc.

Forethought es una suite líder en IA generativa para servicio al cliente. En el núcleo de su suite se encuentra la innovadora tecnología SupportGPT™ que utiliza el aprendizaje automático para transformar el ciclo de soporte al cliente, aumentando la deflexión, mejorando la satisfacción del cliente y aumentando la productividad del agente. SupportGPT™ aprovecha los sistemas de recuperación de información (IR) de última generación y los grandes modelos de lenguaje (LLMs) para alimentar más de 30 millones de interacciones con el cliente anualmente.

El caso de uso principal de SupportGPT es mejorar la calidad y eficiencia de las interacciones y operaciones de soporte al cliente. Al utilizar sistemas IR de última generación alimentados por modelos de incrustación y clasificación, SupportGPT puede buscar rápidamente información relevante, entregando respuestas precisas y concisas a las consultas de los clientes. Forethought utiliza modelos ajustados por cliente para detectar las intenciones del cliente para resolver las interacciones con ellos. La integración de grandes modelos de lenguaje ayuda a humanizar la interacción con agentes automatizados, creando una experiencia de soporte más atractiva y satisfactoria.

SupportGPT también ayuda a los agentes de soporte al cliente ofreciendo sugerencias de autocompletado y elaborando respuestas apropiadas a los tickets de los clientes que se alinean con la empresa en función de las respuestas anteriores. Al utilizar modelos de lenguaje avanzados, los agentes pueden abordar las preocupaciones de los clientes de manera más rápida y precisa, lo que resulta en una mayor satisfacción del cliente.

Además, la arquitectura de SupportGPT permite detectar lagunas en las bases de conocimiento de soporte, lo que ayuda a los agentes a proporcionar información más precisa a los clientes. Una vez identificadas estas lagunas, SupportGPT puede generar automáticamente artículos y otros contenidos para llenar estos vacíos de conocimiento, asegurando que la base de conocimiento de soporte siga siendo centrada en el cliente y actualizada.

En este post, compartimos cómo Forethought utiliza los endpoints multi-modelo de Amazon SageMaker en casos de uso de IA generativa para ahorrar más del 66% en costos.

Desafíos de infraestructura

Para ayudar a llevar estas capacidades al mercado, Forethought escala eficientemente sus cargas de trabajo de aprendizaje automático y proporciona soluciones hiper-personalizadas adaptadas a cada caso de uso específico del cliente. Esta hiper-personalización se logra mediante el ajuste fino de modelos de incrustación y clasificadores en los datos del cliente, asegurando resultados precisos de recuperación de información y conocimiento de dominio que se adapta a las necesidades únicas de cada cliente. Los modelos de autocompletado personalizados también se ajustan finamente en los datos del cliente para mejorar aún más la precisión y relevancia de las respuestas generadas.

Uno de los desafíos significativos en el procesamiento de IA es la utilización eficiente de los recursos de hardware, como las GPUs. Para abordar este desafío, Forethought utiliza endpoints multi-modelo de SageMaker (MMEs) para ejecutar múltiples modelos de IA en un único endpoint de inferencia y escalar. Debido a que la hiper-personalización de los modelos requiere que se entrenen y se implementen modelos únicos, el número de modelos se escala linealmente con el número de clientes, lo que puede resultar costoso.

Para lograr el equilibrio adecuado de rendimiento para la inferencia en tiempo real y los costos, Forethought optó por usar los endpoints multi-modelo de SageMaker, que admiten la aceleración de GPU. Los endpoints MMEs de SageMaker permiten a Forethought ofrecer soluciones escalables, rentables y de alto rendimiento con una latencia de subsegundo, abordando múltiples escenarios de soporte al cliente a escala.

SageMaker y Forethought

SageMaker es un servicio completamente administrado que brinda a los desarrolladores y científicos de datos la capacidad de construir, entrenar e implementar modelos de aprendizaje automático rápidamente. Los endpoints multi-modelo de SageMaker proporcionan una solución escalable y rentable para implementar una gran cantidad de modelos para la inferencia en tiempo real. Los MMEs utilizan un contenedor de servicio compartido y una flota de recursos que pueden usar instancias aceleradas como GPUs para alojar todos sus modelos. Esto reduce los costos de alojamiento al maximizar la utilización del endpoint en comparación con el uso de endpoints de un solo modelo. También reduce la sobrecarga de implementación porque SageMaker se encarga de cargar y descargar modelos en memoria y escalarlos en función de los patrones de tráfico del endpoint. Además, todos los endpoints en tiempo real de SageMaker se benefician de capacidades integradas para administrar y monitorear modelos, como variantes de sombra, escalado automático e integración nativa con Amazon CloudWatch (para obtener más información, consulte las métricas de CloudWatch para implementaciones de endpoint multi-modelo).

A medida que Forethought creció para alojar cientos de modelos que también requerían recursos de GPU, vimos una oportunidad de crear una arquitectura más rentable, confiable y manejable a través de los endpoints multi-modelo de SageMaker. Antes de migrar a los endpoints MMEs de SageMaker, nuestros modelos se implementaban en Kubernetes en Amazon Elastic Kubernetes Service (Amazon EKS). Aunque Amazon EKS proporcionaba capacidades de administración, fue evidente de inmediato que estábamos administrando la infraestructura que no estaba específicamente adaptada para la inferencia. Forethought tuvo que administrar la inferencia de modelos en Amazon EKS nosotros mismos, lo que fue una carga para la eficiencia del ingeniero. Por ejemplo, para compartir recursos costosos de GPU entre múltiples modelos, éramos responsables de asignar fracciones rígidas de memoria a los modelos que se especificaban durante la implementación. Queríamos abordar los siguientes problemas clave con nuestra infraestructura existente:

  • Alto costo – Para asegurarnos de que cada modelo tuviera suficientes recursos, éramos muy conservadores en la cantidad de modelos que cabían por instancia. Esto resultó en costos mucho más altos para el alojamiento de los modelos de lo necesario.
  • Baja fiabilidad – A pesar de ser conservadores en la asignación de memoria, no todos los modelos tienen los mismos requisitos y, ocasionalmente, algunos modelos arrojaban errores de falta de memoria (OOM).
  • Gestión ineficiente – Teníamos que gestionar diferentes manifiestos de implementación para cada tipo de modelo (como clasificadores, incrustaciones y autocompletado), lo que consumía mucho tiempo y estaba sujeto a errores. También teníamos que mantener la lógica para determinar la asignación de memoria para diferentes tipos de modelos.

En última instancia, necesitábamos una plataforma de inferencia para asumir el trabajo pesado de gestionar nuestros modelos en tiempo de ejecución para mejorar el costo, la fiabilidad y la gestión del servicio de nuestros modelos. Los MME de SageMaker nos permitieron abordar estas necesidades.

Gracias a su carga y descarga de modelos inteligente y dinámica, y sus capacidades de escalado, los MME de SageMaker proporcionaron una solución significativamente menos costosa y más confiable para alojar nuestros modelos. Ahora podemos ajustar muchos más modelos por instancia sin preocuparnos por los errores OOM, ya que los MME de SageMaker manejan la carga y descarga de modelos de manera dinámica. Además, las implementaciones ahora son tan simples como llamar a las API de SageMaker Boto3 y adjuntar las políticas adecuadas de escalado automático.

El siguiente diagrama ilustra nuestra arquitectura heredada.

Para comenzar nuestra migración a los MME de SageMaker, identificamos los mejores casos de uso para los MME y cuáles de nuestros modelos se beneficiarían más con este cambio. Los MME son mejores para los siguientes casos de uso:

  • Modelos que se espera que tengan baja latencia pero que puedan soportar un tiempo de inicio en frío (cuando se carga por primera vez)
  • Modelos que se llaman a menudo y consistentemente
  • Modelos que necesitan recursos parciales de GPU
  • Modelos que comparten requisitos comunes y lógica de inferencia

Identificamos nuestros modelos de incrustaciones y modelos de lenguaje de autocompletado como los mejores candidatos para nuestra migración. Para organizar estos modelos bajo los MME, crearíamos un MME por tipo de modelo o tarea, uno para nuestros modelos de incrustaciones y otro para los modelos de lenguaje de autocompletado.

Ya teníamos una capa de API en la parte superior de nuestros modelos para la gestión de modelos e inferencia. Nuestra tarea era reformular cómo se estaba implementando y manejando la inferencia en los modelos a través de SageMaker, con cambios mínimos en la forma en que los clientes y los equipos de productos interactuaban con la API. También necesitábamos empaquetar nuestros modelos y lógica de inferencia personalizada para que fueran compatibles con el servidor de inferencia NVIDIA Triton utilizando los MME de SageMaker.

El siguiente diagrama ilustra nuestra nueva arquitectura.

Lógica de inferencia personalizada

Antes de migrar a SageMaker, el código de inferencia personalizado de Forethought (preprocesamiento y posprocesamiento) se ejecutaba en la capa de API cuando se invocaba un modelo. El objetivo era transferir esta funcionalidad al modelo en sí para clarificar la separación de responsabilidades, modularizar y simplificar su código, y reducir la carga en la API.

Incrustaciones

Los modelos de incrustaciones de Forethought consisten en dos artefactos de modelo PyTorch, y la solicitud de inferencia determina qué modelo llamar. Cada modelo requiere texto preprocesado como entrada. Los principales desafíos fueron integrar una etapa de preprocesamiento y acomodar dos artefactos de modelo por definición de modelo. Para abordar la necesidad de múltiples etapas en la lógica de inferencia, Forethought desarrolló un modelo de conjunto Triton con dos etapas: un proceso de preprocesamiento de backend de Python y una llamada de modelo de backend de PyTorch. Los modelos de conjunto permiten definir y ordenar etapas en la lógica de inferencia, siendo cada etapa representada por un modelo Triton de cualquier tipo de backend. Para garantizar la compatibilidad con el backend de PyTorch de Triton, los artefactos de modelo existentes se convirtieron al formato TorchScript. Se crearon modelos Triton separados para cada definición de modelo, y la capa de API de Forethought fue responsable de determinar el TargetModel apropiado para invocar en función de la solicitud entrante.

Autocompletado

Los modelos de autocompletado (de secuencia a secuencia) presentaron un conjunto distinto de requisitos. Específicamente, necesitábamos habilitar la capacidad de recorrer múltiples llamadas de modelo y almacenar en caché entradas sustanciales para cada llamada, todo manteniendo una latencia baja. Además, estos modelos necesitaron tanto preprocesamiento como posprocesamiento. Para abordar estos requisitos y lograr la flexibilidad deseada, Forethought desarrolló modelos de MME de autocompletado utilizando el backend de Python de Triton, que ofrece la ventaja de escribir el modelo como código Python.

Benchmarks

Una vez que se determinaron las formas de los modelos Triton, los desplegamos en puntos finales de preparación y realizamos pruebas de recursos y rendimiento. Nuestro principal objetivo era determinar la latencia para los modelos en memoria y los de inicio en frío, y cómo la latencia se veía afectada por el tamaño de la solicitud y la concurrencia. También queríamos saber cuántos modelos podían caber en cada instancia, cuántos modelos causarían que las instancias se escalen con nuestra política de escalado automático, y qué tan rápido se produciría la escalada. Siguiendo los tipos de instancias que ya estábamos utilizando, hicimos nuestras pruebas de referencia con las instancias ml.g4dn.xlarge y ml.g4dn.2xlarge.

Resultados

La siguiente tabla resume nuestros resultados.

Tamaño de la solicitud Latencia de inicio en frío Latencia de inferencia en caché Latencia concurrente (5 solicitudes)
Pequeña (30 tokens) 12,7 segundos 0,03 segundos 0,12 segundos
Zepes (250 tokens) 12,7 segundos 0,05 segundos 0,12 segundos
Grande (550 tokens) 12,7 segundos 0,13 segundos 0,12 segundos

Es notable que la latencia para las solicitudes de inicio en frío es significativamente mayor que la latencia para las solicitudes de inferencia en caché. Esto se debe a que el modelo necesita cargarse desde el disco o Amazon Simple Storage Service (Amazon S3) cuando se realiza una solicitud de inicio en frío. La latencia para las solicitudes concurrentes también es mayor que la latencia para las solicitudes individuales. Esto se debe a que el modelo debe compartirse entre las solicitudes concurrentes, lo que puede provocar conflictos.

La siguiente tabla compara la latencia de los modelos heredados y los modelos de SageMaker.

Tamaño de la solicitud Modelos heredados Modelos SageMaker
Pequeña (30 tokens) 0,74 segundos 0,24 segundos
Zepes (250 tokens) 0,74 segundos 0,24 segundos
Grande (550 tokens) 0,80 segundos 0,32 segundos

En general, los modelos de SageMaker son una mejor opción para alojar modelos de autocompletado que los modelos heredados. Ofrecen menor latencia, escalabilidad, confiabilidad y seguridad.

Uso de recursos

En nuestra búsqueda para determinar el número óptimo de modelos que podían caber en cada instancia, realizamos una serie de pruebas. Nuestro experimento consistió en cargar modelos en nuestros puntos finales utilizando un tipo de instancia ml.g4dn.xlarge, sin ninguna política de escalado automático.

Estas instancias en particular ofrecen 15,5 GB de memoria, y nuestro objetivo era lograr aproximadamente un 80% de uso de memoria de GPU por instancia. Teniendo en cuenta el tamaño de cada artefacto de modelo codificador, logramos encontrar el número óptimo de codificadores Triton para cargar en una instancia para alcanzar nuestro uso objetivo de memoria de GPU. Además, dado que cada uno de nuestros modelos de incrustaciones corresponde a dos modelos codificadores de Triton, pudimos alojar un número determinado de modelos de incrustaciones por instancia. Como resultado, calculamos el número total de instancias requeridas para servir todos nuestros modelos de incrustaciones. Esta experimentación ha sido crucial en la optimización de nuestro uso de recursos y en la mejora de la eficiencia de nuestros modelos.

Realizamos pruebas de referencia similares para nuestros modelos de autocompletado. Estos modelos tenían un tamaño de alrededor de 292,0 MB cada uno. Al probar cuántos modelos cabrían en una sola instancia ml.g4dn.xlarge, notamos que solo pudimos ajustar cuatro modelos antes de que nuestra instancia comenzara a descargar modelos, a pesar de que los modelos tenían un tamaño pequeño. Nuestras principales preocupaciones fueron:

  • Causa del aumento repentino en la utilización de la memoria de la CPU
  • Causa de la descarga de modelos cuando intentamos cargar un modelo más en lugar del modelo usado menos recientemente (LRU)

Pudimos identificar la causa raíz del aumento repentino de la utilización de la memoria proveniente de la inicialización de nuestro entorno de ejecución CUDA en nuestro modelo Python, lo cual era necesario para mover nuestros modelos y datos hacia dentro y fuera del dispositivo GPU. CUDA carga muchas dependencias externas en la memoria de la CPU cuando se inicializa el entorno de ejecución. Debido a que el backend de Triton PyTorch maneja y abstrae el movimiento de datos hacia dentro y fuera del dispositivo GPU, no encontramos este problema para nuestros modelos de embedding. Para abordar este problema, intentamos utilizar instancias ml.g4dn.2xlarge, que tenían la misma cantidad de memoria GPU pero el doble de memoria CPU. Además, agregamos varias optimizaciones menores en nuestro código backend de Python, incluyendo la eliminación de tensores después de su uso, el vaciado de la caché, la desactivación de gradientes y la recolección de basura. Con el tipo de instancia más grande, pudimos ajustar 10 modelos por instancia y la utilización de la memoria de la CPU y GPU se alineó mucho más.

El siguiente diagrama ilustra esta arquitectura.

Escalado automático

Adjuntamos políticas de escalado automático tanto a nuestros embeddings como a nuestros MMEs de autocompletado. Nuestra política para nuestro punto final de embeddings apuntaba a una utilización promedio del 80% de la memoria GPU utilizando métricas personalizadas. Nuestros modelos de autocompletado vieron un patrón de alto tráfico durante las horas de trabajo y un tráfico mínimo durante la noche. Debido a esto, creamos una política de escalado automático basada en InvocationsPerInstance para que pudiéramos escalar de acuerdo con los patrones de tráfico, ahorrando costos sin sacrificar la confiabilidad. Basados en nuestras pruebas de uso de recursos, configuramos nuestras políticas de escalado con un objetivo de 225 InvocationsPerInstance.

Desplegar lógica y pipeline

Crear un MME en SageMaker es sencillo y similar a crear cualquier otro punto final en SageMaker. Después de que se crea el punto final, agregar modelos adicionales al punto final es tan simple como mover el artefacto del modelo a la ruta de S3 que el punto final apunta; en este punto, podemos hacer solicitudes de inferencia a nuestro nuevo modelo.

Definimos una lógica que tomaría los metadatos del modelo, formatearía el punto final de manera determinística basándose en los metadatos y verificaría si el punto final existía. Si no existía, crearíamos el punto final y agregaríamos el artefacto del modelo Triton a la ruta de S3 para el punto final (también formateado de manera determinística). Por ejemplo, si los metadatos del modelo indicaban que era un modelo de autocompletado, se crearía un punto final para modelos de autocompletado y una ruta de S3 asociada para artefactos de modelos de autocompletado. Si el punto final existía, copiaríamos el artefacto del modelo a la ruta de S3.

Ahora que teníamos nuestras formas de modelo para nuestros modelos MME y la funcionalidad para implementar nuestros modelos en MME, necesitábamos una forma de automatizar la implementación. Nuestros usuarios deben especificar qué modelo quieren implementar; manejamos el empaquetado e implementación del modelo. El código de inferencia personalizado empaquetado con el modelo está versionado y se envía a Amazon S3; en el paso de empaquetado, extraemos el código de inferencia según la versión especificada (o la última versión) y usamos archivos YAML que indican las estructuras de los modelos Triton.

Un requisito para nosotros era que todos nuestros modelos MME se cargaran en memoria para evitar cualquier latencia de inicio en frío durante las solicitudes de inferencia de producción para cargar modelos. Para lograr esto, provisionamos suficientes recursos para ajustar todos nuestros modelos (según las pruebas de referencia anteriores) y llamamos a cada modelo en nuestro MME a una cadencia horaria.

El siguiente diagrama ilustra el pipeline de implementación de modelos.

El siguiente diagrama ilustra el pipeline de calentamiento de modelos.

Invocación del modelo

Nuestra capa de API existente proporciona una abstracción para que los llamadores realicen inferencia en todos nuestros modelos de ML. Esto significó que solo tuvimos que agregar funcionalidad a la capa de API para llamar a SageMaker MME con el modelo objetivo correcto dependiendo de la solicitud de inferencia, sin cambios en el código de llamada. El código de inferencia de SageMaker toma la solicitud de inferencia, formatea las entradas de Triton definidas en nuestros modelos de Triton e invoca los MME utilizando Boto3.

Beneficios de costos

Forethought logró reducir significativamente los costos de alojamiento del modelo y mitigar los errores OOM del modelo gracias a la migración a SageMaker MME. Antes de este cambio, se ejecutaban instancias ml.g4dn.xlarge en Amazon EKS. Con la transición a MME, descubrimos que podía albergar 12 modelos de incrustaciones por instancia mientras se lograba una utilización del 80% de la memoria de la GPU. Esto llevó a una disminución significativa en nuestros gastos mensuales. Para ponerlo en perspectiva, realizamos un ahorro de costos de hasta el 80%. Además, para administrar un mayor tráfico, consideramos escalar las réplicas. Suponiendo un escenario en el que empleamos tres réplicas, encontramos que nuestros ahorros de costos seguirían siendo sustanciales incluso en estas condiciones, rondando el 43%.

La experiencia con SageMaker MME ha resultado financieramente beneficiosa, reduciendo nuestros gastos mientras asegura un rendimiento óptimo del modelo. Previamente, nuestros modelos de lenguaje de autocompletado se implementaron en Amazon EKS, lo que requería un número variable de instancias ml.g4dn.xlarge según la asignación de memoria por modelo. Esto resultó en un costo mensual considerable. Sin embargo, con nuestra reciente migración a SageMaker MMEs, hemos logrado reducir estos costos sustancialmente. Ahora alojamos todos nuestros modelos en instancias ml.g4dn.2xlarge, lo que nos da la capacidad de empacar modelos de manera más eficiente. Esto ha reducido significativamente nuestros gastos mensuales, y ahora hemos logrado ahorros de costos en el rango del 66 al 74%. Este movimiento ha demostrado cómo la utilización eficiente de los recursos puede llevar a ahorros financieros significativos utilizando SageMaker MME.

Conclusión

En esta publicación, revisamos cómo Forethought utiliza puntos finales multi-modelo de SageMaker para disminuir el costo de la inferencia en tiempo real. SageMaker se encarga de la carga pesada no diferenciada, para que Forethought pueda aumentar la eficiencia de la ingeniería. También permite a Forethought reducir drásticamente el costo de la inferencia en tiempo real mientras mantiene el rendimiento necesario para las operaciones críticas del negocio. Al hacerlo, Forethought puede ofrecer una oferta diferenciada para sus clientes utilizando modelos hiperpersonalizados. Utilice SageMaker MME para alojar sus modelos a escala y reducir los costos de alojamiento mejorando la utilización del punto final. También reduce la sobrecarga de implementación porque Amazon SageMaker se encarga de cargar modelos en memoria y escalarlos según los patrones de tráfico en su punto final. Puede encontrar ejemplos de código sobre cómo alojar múltiples modelos utilizando SageMaker MME en GitHub.