Prácticas recomendadas para optimizar la inferencia de modelos de lenguaje grandes con GPUs en Google Kubernetes Engine (GKE)


Google Kubernetes Engine (GKE) proporciona un control detallado para la inferencia de modelos de lenguaje grande (LLM) con un rendimiento y un costo óptimos. En esta guía, se describen las prácticas recomendadas para optimizar la inferencia y la entrega de LLM abiertos con GPUs en GKE mediante los frameworks de entrega de vLLM y Inferencia de generación de texto (TGI).

Para obtener una lista resumida de todas las prácticas recomendadas, consulta la Lista de tareas resumida.

Objetivos

Esta guía está dirigida a clientes de IA generativa, usuarios nuevos o existentes de GKE, ingenieros de AA y, también, ingenieros de LLMOps (DevOps) que estén interesados en optimizar sus cargas de trabajo de LLM con GPUs con Kubernetes.

Una vez que finalices esta guía, podrás hacer lo siguiente:

  • Elige técnicas de optimización de LLM posteriores al entrenamiento, como cuantización, paralelismo de tensores y optimización de memoria.
  • Evalúa las compensaciones de alto nivel cuando consideres estas técnicas de optimización.
  • Implementa modelos de LLM abiertos en GKE con frameworks de entrega, como vLLM o TGI con la configuración de optimización habilitada.

Descripción general de las técnicas de optimización de la entrega de LLM

A diferencia de las cargas de trabajo que no son de IA, las cargas de trabajo de LLM suelen tener una latencia más alta y una menor capacidad de procesamiento debido a que se basan en las operaciones de multiplicación de matrices. Para mejorar el rendimiento de la inferencia de LLM, puedes usar aceleradores de hardware especializados (por ejemplo, GPU y TPU) y frameworks de entrega optimizados.

Puedes aplicar una o más de las siguientes prácticas recomendadas para reducir la latencia de la carga de trabajo de LLM y, al mismo tiempo, mejorar la capacidad de procesamiento y la eficiencia de costos:

En los ejemplos de esta guía, se usa el LLM de Gemma 7B junto con los frameworks de entrega de vLLM o TGI para aplicar estas prácticas recomendadas. Sin embargo, los conceptos y las funciones descritos se pueden aplicar a los LLM abiertos más populares.

Antes de comenzar

Antes de probar los ejemplos de esta guía, completa estas tareas de requisitos previos:

  1. Sigue las instrucciones de estas guías para obtener acceso al modelo de Gemma, preparar tu entorno y crear y configurar los recursos de Google Cloud:

    Asegúrate de guardar el token de acceso de Hugging Face en tu Secret de Kubernetes.

  2. Clona el repositorio de muestras https://github.com/GoogleCloudPlatform/kubernetes-engine-samples/ en tu entorno de desarrollo local.

  3. Cambia tu directorio de trabajo a /kubernetes-engine-samples/ai-ml/llm-serving-gemma/:

Práctica recomendada: cuantización

La cuantización es una técnica análoga a la compresión de imágenes con pérdida que reduce el tamaño del modelo representando los pesos en formatos de menor precisión (8 bits o 4 bits), lo que reduce los requisitos de memoria. Sin embargo, al igual que la compresión de imágenes, la cuantización implica una compensación: la disminución del tamaño del modelo puede reducir la exactitud.

Existen varios métodos de cuantificación, cada uno con sus propias ventajas y desventajas únicas. Algunos, como AWQ y GPTQ, requieren cuantificación previa y están disponibles en plataformas como Hugging Face o Kaggle. Por ejemplo, si aplicas GPTQ en el modelo Llama-2 13B y AWQ en el modelo Gemma 7B, puedes entregar los modelos en una sola GPU L4 en lugar de dos GPUs L4 sin cuantificación.

También puedes realizar la cuantificación con herramientas como AutoAWQ y AutoGPTQ. Estos métodos pueden mejorar la latencia y la capacidad de procesamiento. En cambio, las técnicas que usan EETQ y la biblioteca bitsandbytes para la cuantificación no requieren modelos cuantificados previamente, por lo que pueden ser una opción adecuada cuando las versiones cuantificadas previamente no están disponibles.

La mejor técnica de cuantización que se debe usar depende de tus objetivos específicos y de su compatibilidad con el framework de entrega que deseas usar. Para obtener más información, consulta la guía de cuantización de Hugging Face.

Selecciona una de estas pestañas para ver un ejemplo de cómo aplicar la cuantificación con los marcos de trabajo de TGI o vLLM:

TGI

GKE admite estas opciones de cuantización con TGI:

  • awq
  • gptq
  • eetq
  • bitsandbytes
  • bitsandbytes-nf4
  • bitsandbytes-fp4

Los métodos de cuantización de AWQ y GPTQ requieren modelos previamente cuantificados, mientras que la cuantización de EETQ y bitsandbytes se puede aplicar a cualquier modelo. Para obtener más información sobre estas opciones, consulta este artículo de Hugging Face.

Para usar la cuantificación, configura el parámetro -–quantize cuando inicies el servidor de modelos.

En el siguiente fragmento, se muestra cómo optimizar Gemma 7B con la cuantización bitsandbytes mediante TGI en GKE.

args:
- --model-id=$(MODEL_ID)
- --num-shard=2
- --quantize=bitsandbytes

Para aplicar esta configuración, usa el siguiente comando:

kubectl apply -f tgi/tgi-7b-bitsandbytes.yaml

vLLM

GKE admite estas opciones de cuantización con vLLM:

Para usar la cuantificación de modelos con vLLM, los modelos deben estar cuantificados previamente. Cuando inicies el entorno de ejecución, establece el parámetro –quantization.

En el siguiente fragmento, se muestra cómo optimizar el modelo Gemma 7B con la cuantificación awq usando vLLM en GKE:

args:
- --model=$(MODEL_ID)
- --tensor-parallel-size=1
- --quantization=awq
env:
- name: MODEL_ID
  value: google/gemma-7b-AWQ
resources:
  requests:
    nvidia.com/gpu: 1
  limits:
    nvidia.com/gpu: 1

Para aplicar esta configuración, usa el siguiente comando:

kubectl apply -f vllm/vllm-7b-awq.yaml

Mejora la latencia con la cuantización de caché KV

Puedes usar la cuantización de caché KV FP8 E5M2 para disminuir de forma significativa el alcance de memoria de caché de KV y mejorar la latencia, en especial para tamaños de lotes grandes. Sin embargo, esto reduce la exactitud de la inferencia.

Para habilitar la cuantización de la caché de KV de FP8 E5M2, establece el parámetro --kv-cache-dtype fp8_e5m2:

args:
- --model=$(MODEL_ID)
- --tensor-parallel-size=1
- --kv-cache-dtype=fp8_e5m2
- --max-model-len=1200
resources:
  requests:
    cpu: "2"
    memory: "25Gi"
    ephemeral-storage: "25Gi"
    nvidia.com/gpu: 1
  limits:
    cpu: "2"
    memory: "25Gi"
    ephemeral-storage: "25Gi"
    nvidia.com/gpu: 1

Para aplicar esta configuración, usa el siguiente comando:

kubectl apply -f vllm/vllm-7b-kvcache.yaml

Práctica recomendada: Paralelismo de tensores

El paralelismo de tensores es una técnica que distribuye la carga computacional en varias GPUs, lo que es esencial cuando se ejecutan modelos grandes que superan la capacidad de memoria de una sola GPU. Este enfoque puede ser más rentable, ya que te permite usar varias GPUs asequibles en lugar de una sola costosa. También puede mejorar la capacidad de procesamiento de la inferencia de modelos. El paralelismo de tensores aprovecha el hecho de que las operaciones de tensores se pueden realizar de forma independiente en fragmentos de datos más pequeños.

Para obtener más información sobre esta técnica, consulta la guía de paralelismo de tensores de Hugging Face.

Selecciona una de estas pestañas para ver un ejemplo de aplicación del paralelismo de tensor con los frameworks TGI o vLLM:

TGI

Con TGI, el entorno de ejecución de publicación usará todas las GPUs disponibles para el Pod de forma predeterminada. Para configurar la cantidad de GPUs que se usarán, especifica el parámetro --num-shard con la cantidad de GPUs como valor.

Consulta la documentación de Hugging Face para obtener la lista de modelos compatibles con el paralelismo de tensores.

En el siguiente fragmento, se muestra cómo optimizar el modelo ajustado a instrucciones de Gemma 7B con el paralelismo de tensor y dos GPU L4:

args:
- --model-id=$(MODEL_ID)
- --num-shard=2

Para aplicar esta configuración, usa el siguiente comando:

kubectl apply -f tgi/tgi-7b-it-tensorparallelism.yaml

En los clústeres de GKE Autopilot, ejecutar este comando crea un Pod con requisitos de recursos mínimos de 21 vCPU y 78 GiB de memoria.

vLLM

vLLM admite la inferencia distribuida en paralela a los tensores. vLLM habilita la función de forma predeterminada si hay más de una GPU disponible.

En el siguiente fragmento, se muestra cómo puedes optimizar el modelo ajustado a instrucciones de Gemma 7B con el paralelismo de tensor y dos GPU L4:

args:
- --model=$(MODEL_ID)
- --tensor-parallel-size=2

Para aplicar esta configuración, usa el siguiente comando:

kubectl apply -f vllm/vllm-7b-it-tensorparallelism.yaml

En los clústeres de GKE Autopilot, ejecutar este comando crea un Pod con requisitos de recursos mínimos de 21 vCPU y 78 GiB de memoria.

Práctica recomendada: Optimiza la memoria del modelo

Optimizar el uso de memoria de los LLM es fundamental para una inferencia eficiente. En esta sección, se presentan estrategias de optimización de la capa de atención, como la atención paginada y la atención intermitente. Estas estrategias mejoran la eficiencia de la memoria, lo que permite secuencias de entrada más largas y un tiempo de inactividad de la GPU reducido. En esta sección, también se describe cómo puedes ajustar los tamaños de entrada y salida del modelo para que se ajusten a las restricciones de memoria y optimizarlos para frameworks de publicación específicos.

Optimización de la capa de atención

Las capas de autoatención permiten que los modelos comprendan el contexto en las tareas de procesamiento del lenguaje, ya que los significados de las palabras pueden cambiar según el contexto. Sin embargo, estas capas almacenan las ponderaciones de tokens de entrada, las claves (K) y los valores (V) en la vRAM de la GPU. Por lo tanto, a medida que se extiende la secuencia de entrada, esto produce un crecimiento cuadrático en el tamaño y el tiempo de procesamiento.

El uso del almacenamiento en caché de KV es muy útil cuando se trata de secuencias de entrada largas, en las que la sobrecarga de autoatención puede volverse significativa. Este enfoque de optimización reduce el procesamiento de procesamiento a la complejidad lineal.

Entre las técnicas específicas para optimizar los mecanismos de atención en los LLM, se incluyen las siguientes:

  • Atención paginada: La atención paginada mejora la administración de la memoria para modelos grandes y secuencias de entrada largas mediante técnicas de paginación, similares a la memoria virtual del SO. Esto reduce de manera eficaz la fragmentación y la duplicación en la caché de KV, lo que permite secuencias de entrada más largas sin agotar la memoria de GPU.
  • Atención de flash: la atención de flash reduce los cuellos de botella de la memoria de la GPU minimizando las transferencias de datos entre la RAM de la GPU y la caché de L1 durante la generación de tokens. Esto elimina el tiempo de inactividad de los núcleos de procesamiento, lo que mejora de forma significativa el rendimiento de la inferencia y el entrenamiento de las GPU.

Ajusta el tamaño de entrada y salida del modelo

Los requisitos de memoria dependen del tamaño de entrada y salida. Una salida más larga y más contexto requieren más recursos, mientras que una salida más corta y menos contexto pueden ahorrar costos con el uso de una GPU más pequeña y económica.

Selecciona una de estas pestañas para ver un ejemplo de ajuste de los requisitos de memoria de entrada y salida del modelo en los frameworks de TGI o vLLM:

TGI

El entorno de ejecución de la entrega de TGI verifica los requisitos de memoria durante el inicio y no se inicia si la huella de memoria máxima posible del modelo no se ajusta a la memoria de la GPU disponible. Esta verificación elimina las fallas de memoria insuficiente (OOM) en las cargas de trabajo que requieren mucha memoria.

GKE admite los siguientes parámetros TGI para optimizar los requisitos de memoria del modelo:

En el siguiente fragmento, se muestra cómo puedes entregar un modelo ajustado a instrucciones de Gemma 7B con una sola GPU L4, con la configuración de parámetros --max-total-tokens=3072, --max-batch-prefill-tokens=512, --max-input-length=512:

args:
- --model-id=$(MODEL_ID)
- --num-shard=1
- --max-total-tokens=3072 
- --max-batch-prefill-tokens=512
- --max-input-length=512
env:
- name: MODEL_ID
  value: google/gemma-7b

Para aplicar esta configuración, usa el siguiente comando:

kubectl apply -f tgi/tgi-7b-token.yaml

vLLM

En vLLM, configura la longitud del contexto del modelo, que afecta directamente el tamaño de la caché de KV y los requisitos de RAM de GPU. Las longitudes de contexto más pequeñas permiten el uso de GPUs más asequibles. El valor predeterminado es la cantidad máxima de tokens que acepta el modelo. Limita la longitud de contexto máxima con --max-model-len MAX_MODEL_LEN si es necesario.

Por ejemplo, el modelo ajustado de instrucciones Gemma 7B, con su longitud de contexto predeterminada de 8192, supera la capacidad de memoria de una sola GPU NVIDIA L4. Para implementar en una L4, limita la longitud combinada de mensajes y resultados mediante la configuración de --max-model-len en un valor inferior a 640. Este ajuste permite ejecutar el modelo en una sola GPU L4 a pesar de su gran longitud de contexto predeterminada.

Para implementar con el límite de tokens modificado, usa el siguiente fragmento:

args:
- --model=$(MODEL_ID)
- --tensor-parallel-size=1
- --max-model-len=600

Para aplicar esta configuración, usa el siguiente comando:

kubectl apply -f vllm/vllm-7b-token.yaml

Resumen de la lista de tareas

Objetivo de optimización Práctica
Latencia
  • Prioriza las GPU potentes: considera actualizar a GPU con mayores capacidades de procesamiento y capacidad de procesamiento de E/S de memoria.
  • Explora la cuantificación: Las técnicas como la AWQ pueden mejorar la latencia, pero ten en cuenta las posibles compensaciones de exactitud.
Capacidad de procesamiento
  • Escalar de forma horizontal: Aumenta la cantidad de réplicas de entrega (Pods) para distribuir la carga de trabajo.
  • Usa el paralelismo de tensor: Para modelos grandes que excedan la capacidad única de GPU, usa el paralelismo de tensor para distribuir los cálculos en varias GPU. Para modelos más pequeños, considera usar varias réplicas con paralelismo de tensores de "1" para evitar la sobrecarga.
  • Solicitudes por lotes y cuantificación: Combina las solicitudes y explora técnicas de cuantificación que mantengan una exactitud aceptable.
Rentabilidad
  • Elige modelos más pequeños: Selecciona modelos dentro de una familia que se ajusten a tus límites de recursos y presupuesto.
  • Aplica la cuantificación: Usa la cuantificación para reducir los requisitos de memoria, en especial cuando se trata de modelos más grandes.
  • Limita la longitud del contexto: Restringe la longitud del contexto para reducir aún más el uso de memoria y habilitar la ejecución en GPU más pequeñas y rentables.

¿Qué sigue?