Práticas recomendadas para otimizar a inferência de modelos de linguagem grandes com GPUs no Google Kubernetes Engine (GKE)


O Google Kubernetes Engine (GKE) fornece controle detalhado para modelos de linguagem grandes (LLM) com desempenho e custo ideais. Este guia descreve as práticas recomendadas para otimizar a inferência e a veiculação de LLMs abertos com GPUs no GKE usando os frameworks de veiculação do vLLM e da inferência de geração de texto (TGI, na sigla em inglês).

Para uma lista resumida de todas as práticas recomendadas, consulte o Resumo da lista de verificação.

Objetivos

Este guia é destinado a clientes de IA generativa, usuários novos ou existentes do GKE, engenheiros de ML e engenheiros de LLMOps (DevOps) que estão interessadas em otimizar as cargas de trabalho de LLM usando GPUs com o Kubernetes.

Ao final desta seção, será possível:

  • Escolha técnicas de otimização de LLM pós-treinamento, incluindo quantização, paralelismo de tensor e otimização de memória.
  • Considere as vantagens e desvantagens de alto nível ao considerar essas técnicas de otimização.
  • Implante modelos de LLM abertos no GKE usando frameworks de veiculação, como vLLM ou TGI, com as configurações de otimização ativadas.

Visão geral das técnicas de otimização da disponibilização de LLMs

Ao contrário das cargas de trabalho que não são de IA, as de LLM costumam ter maior latência e menor capacidade de processamento devido à dependência em operações de multiplicação de matrizes. Para melhorar o desempenho de inferência do LLM, é possível usar Aceleradores de hardware (por exemplo, GPUs e TPUs) e veiculação otimizada frameworks.

É possível aplicar uma ou mais das seguintes práticas recomendadas para reduzir o LLM latência da carga de trabalho e, ao mesmo tempo, melhorar a capacidade de processamento e a eficiência de custos:

Os exemplos neste guia usam o LLM Gemma 7B com o estruturas de veiculação vLLM ou TGI para aplicar essas práticas recomendadas; No entanto, a os conceitos e recursos descritos são aplicáveis aos LLMs abertos mais conhecidos.

Antes de começar

Antes de tentar os exemplos neste guia, conclua estas tarefas de pré-requisito:

  1. Siga as instruções nestes guias para acessar o Gemma preparar o ambiente, além de criar e configurar recursos do Google Cloud:

    Salve o token de acesso do Hugging Face no secret do Kubernetes.

  2. Clone o repositório de amostras https://github.com/GoogleCloudPlatform/kubernetes-engine-samples/ no seu local ambiente de desenvolvimento de software.

  3. Altere seu diretório de trabalho para /kubernetes-engine-samples/ai-ml/llm-serving-gemma/:

Prática recomendada: quantização

A quantização é uma técnica análoga à compactação de imagens com perda que reduz tamanho de modelo representando pesos em formatos de menor precisão (8 ou 4 bits), reduzindo assim os requisitos de memória. No entanto, assim como a compactação de imagens, a quantização envolve uma compensação: a redução do tamanho do modelo pode levar a uma redução da precisão.

Existem vários métodos de quantização, cada um com vantagens e desvantagens exclusivas. Alguns, como AWQ e GPTQ, exigem pré-quantização e estão disponíveis em plataformas como Hugging Face ou Kaggle. Por exemplo, se você aplicar o GPTQ ao modelo 13B Llama-2 e o AWQ no Gemma 7B, é possível exibir os modelos em uma única GPU L4 em vez de duas GPUs L4 sem quantização.

Você também pode realizar a quantização usando ferramentas como AutoAWQ e AutoGPTQ. Esses métodos podem melhorar a latência e a taxa de transferência. Por outro lado, as técnicas que usam EETQ e a biblioteca bitsandbytes para quantização não exigem modelos pré-quantizados, então podem ser uma escolha adequada quando as versões pré-quantizadas não estão disponíveis.

A melhor técnica de quantização a ser usada depende das suas metas específicas e da compatibilidade dela com o framework de veiculação que você quer usar. Para saber mais, consulte o Guia de quantização do Hugging Face.

Selecione uma destas guias para ver um exemplo de aplicação da quantização usando as Frameworks TGI ou vLLM:

TGI

O GKE oferece suporte a estas opções de quantização com o TGI:

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

Os métodos de quantização AWQ e GPTQ exigem modelos pré-quantizados, enquanto a quantização EETQ e bitsandbytes podem ser aplicadas a qualquer modelo. Para saber mais sobre essas opções, consulte este artigo Hugging Face.

Para usar a quantização, defina o parâmetro -–quantize ao iniciar o servidor de modelos.

O snippet a seguir mostra como otimizar o Gemma 7B com quantização bitsandbytes usando o TGI no GKE.

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

Para aplicar essa configuração, use o seguinte comando:

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

vLLM

O GKE oferece suporte a estas opções de quantização com a vLLM:

Para usar a quantização de modelos com o vLLM, eles precisam ser pré-quantizados. Ao iniciar o ambiente de execução, defina o parâmetro –quantization.

O snippet a seguir mostra como otimizar o modelo Gemma 7B com quantização awq usando vLLM no 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 essa configuração, use o seguinte comando:

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

Melhorar a latência usando a quantização de cache do KV

É possível usar a quantização do cache KV FP8 E5M2 para reduzir significativamente a pegada de memória do cache KV e melhorar a latência, especialmente para tamanhos de lote grandes. No entanto, isso reduz a precisão da inferência.

Para ativar o cache KV E5M2 do FP8 quantização, defina o 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 essa configuração, use o seguinte comando:

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

Prática recomendada: paralelismo do Tensor

O paralelismo de tensor é uma técnica que distribui a carga computacional entre várias GPUs, o que é essencial para executar modelos grandes que ultrapassam Capacidade de memória da GPU. Essa abordagem pode ser mais econômica, já que permite usar várias GPUs acessíveis em vez de uma única cara. Ela também pode aprimorar a capacidade de inferência do modelo. O paralelismo de tensor aproveita o fato de que as operações de tensor podem ser realizadas de forma independente em porções de dados menores.

Para saber mais sobre essa técnica, consulte o guia de paralelismo de tensor do Hugging Face.

Selecione uma destas guias para conferir um exemplo de aplicação do paralelismo de tensor usando as estruturas TGI ou vLLM:

TGI

Com o TGI, o ambiente de execução de exibição vai usar todas as GPUs disponíveis para o pod padrão. É possível definir o número de GPUs a serem usadas especificando o parâmetro --num-shard com o número de GPUs como valor.

Consulte a documentação do Hugging Face para conferir a lista de modelos com suporte para paralelismo de tensor.

O snippet abaixo mostra como otimizar o Gemma 7B modelo ajustado por instruções usando o paralelismo de tensor e duas GPUs L4:

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

Para aplicar essa configuração, use o seguinte comando:

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

Em clusters do Autopilot do GKE, a execução desse comando cria um pod com requisitos mínimos de recursos de 21 vCPUs e 78 GiB de memória.

vLLM

O vLLM oferece suporte para inferência de tensor-paralelo distribuído. O vLLM permite por padrão, se houver mais de uma GPU disponível.

O snippet a seguir mostra como otimizar o modelo ajustado por instruções do Gemma 7B usando paralelismo de tensor e duas GPUs L4:

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

Para aplicar essa configuração, use o seguinte comando:

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

Nos clusters do GKE Autopilot, executar este comando cria um pod com requisitos mínimos de recursos de 21 vCPUs e 78 GiB de memória.

Prática recomendada: otimização da memória do modelo

Otimizar o uso de memória dos LLMs é crucial para uma inferência eficiente. Esta seção apresenta estratégias de otimização da camada de atenção, como atenção por página e atenção flash. Essas estratégias melhoram a eficiência da memória, para sequências de entrada mais longas e tempo de inatividade da GPU reduzido. Esta seção também descreve como ajustar os tamanhos de entrada e saída do modelo para se adequar às restrições de memória e a otimizar para frameworks de veiculação específicos.

Otimização da camada de atenção

Camadas de autoatenção permitem que os modelos entendam o contexto de processamento de linguagem natural, já que o significado das palavras pode mudar dependendo do contexto. No entanto, essas camadas armazenam pesos de token de entrada, chaves (K) e valores (V) na vRAM da GPU. Assim, à medida que a sequência de entrada aumenta, isso leva a um crescimento quadrático no tamanho e no tempo de computação.

O uso do armazenamento em cache KV é particularmente útil quando você está lidando com longas sequências de entrada, em que o overhead da autoatenção pode se tornar significativo. Essa abordagem de otimização reduz o processamento computacional para complexidade linear.

Técnicas específicas para otimizar mecanismos de atenção em LLMs incluem:

  • Atenção paginada: a atenção paginada melhora o gerenciamento de memória para modelos grandes e sequências de entrada longas usando técnicas de paginação, semelhante à memória virtual do SO. Isso reduz a fragmentação e a duplicação no cache KV, permitindo sequências de entrada mais longas sem esgotar a memória da GPU.
  • Flash de atenção: o Flash de atenção reduz os gargalos de memória da GPU minimizando as transferências de dados entre a RAM da GPU e o cache L1 durante a geração de tokens. Isso elimina o tempo ocioso de núcleos de computação, melhorando bastante o desempenho de inferência e treinamento das GPUs.

Ajuste do tamanho de entrada e saída do modelo

Os requisitos de memória dependem do tamanho de entrada e saída. Uma saída mais longa e mais contexto exigem mais recursos, enquanto uma saída mais curta e menos contexto podem economizar custos usando uma GPU menor e mais barata.

Selecione uma destas guias para ver um exemplo de ajuste de entrada e saída do modelo requisitos de memória nas estruturas TGI ou vLLM:

TGI

O tempo de execução de veiculação de TGI verifica os requisitos de memória durante a inicialização e não é iniciado se a pegada de memória do modelo máxima possível não se encaixar na memória da GPU disponível. Essa verificação elimina falhas de falta de memória (OOM) em cargas de trabalho com uso intensivo de memória.

O GKE aceita os seguintes parâmetros TGI para otimização de memória do modelo:

O snippet a seguir mostra como veicular um modelo ajustado por instrução Gemma 7B com uma única GPU L4, com configurações de parâmetro --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 essa configuração, use o seguinte comando:

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

vLLM

No vLLM, configure o tamanho do contexto do modelo, o que afeta diretamente o tamanho do cache do KV e os requisitos de RAM da GPU. Contextos menores permitem para o uso de GPUs mais acessíveis. O valor padrão é o máximo número de tokens que o modelo aceita. Limitar o tamanho máximo do contexto com --max-model-len MAX_MODEL_LEN, se necessário.

Por exemplo, o modelo ajustado de instrução Gemma 7B, com o comprimento de contexto padrão de 8192, excede a capacidade de memória de uma única GPU NVIDIA L4. Para implantar em um L4, limite o tamanho combinado dos comandos e definindo --max-model-len com um valor abaixo de 640. Esse ajuste permite executar o modelo em uma única GPU L4, apesar do comprimento de contexto padrão grande.

Para implantar com o limite modificado de tokens, use o seguinte snippet:

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

Para aplicar essa configuração, use o seguinte comando:

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

Lista de verificação resumida

Meta de otimização Prática
Latência
  • Priorize GPUs potentes: considere fazer upgrade para GPUs com recursos computacionais mais altos e taxa de transferência de E/S de memória.
  • Conheça a quantização: técnicas como a AWQ podem melhorar a latência, mas é preciso considerar os possíveis comprometimentos na precisão.
Capacidade
  • Escalonamento horizontal: Aumente o número de réplicas de exibição (pods) para distribuir o carga de trabalho do Google Cloud.
  • Usar o paralelismo de tensor: para modelos grandes que excedem a capacidade de uma única GPU, use o paralelismo de tensor para distribuir as computações em várias GPUs. Para modelos menores, considere várias réplicas com paralelismo de tensor de "1" para evitar sobrecarga.
  • Solicitações em lote e quantização: combine solicitações e explore técnicas de quantização que mantenham a precisão aceitável.
Economia
  • Escolher modelos menores: Selecionar modelos de uma família que atendam às suas restrições de recursos e orçamento.
  • Aplicar quantização: use a quantização para reduzir os requisitos de memória, principalmente ao lidar com modelos maiores.
  • Limitar o comprimento do contexto: restrinja o comprimento do contexto para diminuir ainda mais o uso da memória e ative a execução em GPUs menores e mais econômicas.

A seguir