Entre em contato

Otimizando a implantação do LLM: vLLM PagedAttention e o futuro do serviço eficiente de IA

Inteligência artificial

Otimizando a implantação do LLM: vLLM PagedAttention e o futuro do serviço eficiente de IA

mm
Implante o mecanismo de inferência vLLM para executar modelos de linguagem grandes

A implantação de Large Language Models (LLMs) em aplicações reais apresenta desafios únicos, principalmente em termos de recursos computacionais, latência e custo-benefício. Neste guia abrangente, exploraremos o cenário de serviços de LLM, com foco específico no vLLM (Vector Language Model), uma solução que está remodelando a maneira como implantamos e interagimos com esses modelos poderosos.

Os desafios de servir grandes modelos de linguagem

Antes de nos aprofundarmos em soluções específicas, vamos examinar os principais desafios que tornam o LLM uma tarefa complexa:

Recursos Computacionais

Os LLMs são famosos por sua enorme contagem de parâmetros, variando de bilhões a centenas de bilhões. Por exemplo, o GPT-3 possui 175 bilhões de parâmetros, enquanto modelos mais recentes como GPT-4 estima-se que tenham ainda mais. Esse tamanho se traduz em requisitos computacionais significativos para inferência.

Exemplo:
Consideremos um valor relativamente modesto LLM com 13 bilhões de parâmetros, como LLaMA-13B. Mesmo este modelo requer:

– Aproximadamente 26 GB de memória apenas para armazenar os parâmetros do modelo (assumindo precisão de 16 bits)
– Memória adicional para ativações, mecanismos de atenção e cálculos intermediários
– Poder de computação GPU substancial para inferência em tempo real

Latência

Em muitas aplicações, como chatbots ou geração de conteúdo em tempo real, a baixa latência é crucial para uma boa experiência do usuário. No entanto, a complexidade dos LLMs pode levar a tempos de processamento significativos, especialmente para sequências mais longas.

Exemplo:
Imagine um chatbot de atendimento ao cliente desenvolvido por um LLM. Se cada resposta levar vários segundos para ser gerada, a conversa não parecerá natural e frustrante para os usuários.

Custo

O hardware necessário para executar LLMs em escala pode ser extremamente caro. Freqüentemente, são necessárias GPUs ou TPUs de última geração e o consumo de energia desses sistemas é substancial.

Exemplo:
A execução de um cluster de GPUs NVIDIA A100 (frequentemente usadas para inferência LLM) pode custar milhares de dólares por dia em taxas de computação em nuvem.

Abordagens tradicionais para servir LLM

Antes de explorar soluções mais avançadas, vamos revisar brevemente algumas abordagens tradicionais para atender LLMs:

Implantação simples com Hugging Face Transformers

A biblioteca Hugging Face Transformers fornece uma maneira simples de implantar LLMs, mas não é otimizada para serviços de alto rendimento.

Exemplo de código:

from transformers import AutoModelForCausalLM, AutoTokenizer
import torch

model_name = "meta-llama/Llama-2-13b-hf"
model = AutoModelForCausalLM.from_pretrained(model_name, device_map="auto")
tokenizer = AutoTokenizer.from_pretrained(model_name)

def generate_text(prompt, max_length=100):
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
outputs = model.generate(**inputs, max_length=max_length)
return tokenizer.decode(outputs[0], skip_special_tokens=True)

print(generate_text("The future of AI is"))

Embora essa abordagem funcione, ela não é adequada para aplicativos de alto tráfego devido ao uso ineficiente de recursos e à falta de otimizações para atendimento.

Usando TorchServe ou estruturas semelhantes

Frameworks como o TorchServe oferecem recursos de serviço mais robustos, incluindo balanceamento de carga e versionamento de modelos. No entanto, eles ainda não abordam os desafios específicos do serviço LLM, como o gerenciamento eficiente de memória para modelos grandes.

Compreendendo o gerenciamento de memória no serviço LLM

O gerenciamento eficiente de memória é fundamental para atender modelos de linguagem grandes (LLMs) devido aos extensos recursos computacionais necessários. As imagens a seguir ilustram vários aspectos do gerenciamento de memória, que são essenciais para otimizar o desempenho do LLM.

Memória segmentada vs. memória paginada

Esses dois diagramas comparam técnicas de gerenciamento de memória segmentada e memória paginada, comumente usadas em sistemas operacionais (SO).

  • Memória Segmentada: Esta técnica divide a memória em diferentes segmentos, cada um correspondendo a um programa ou processo diferente. Por exemplo, num contexto de serviço LLM, diferentes segmentos podem ser atribuídos a vários componentes do modelo, tais como tokenização, incorporação e mecanismos de atenção. Cada segmento pode crescer ou diminuir de forma independente, proporcionando flexibilidade, mas potencialmente levando à fragmentação se os segmentos não forem gerenciados adequadamente.
  • Memória paginada: aqui, a memória é dividida em páginas de tamanho fixo, que são mapeadas na memória física. As páginas podem ser trocadas conforme necessário, permitindo o uso eficiente dos recursos de memória. No serviço LLM, isso pode ser crucial para gerenciar as grandes quantidades de memória necessárias para armazenar pesos de modelo e cálculos intermediários.

Gerenciamento de memória no sistema operacional vs. vLLM

Esta imagem contrasta o gerenciamento de memória do sistema operacional tradicional com a abordagem de gerenciamento de memória usada no vLLM.

  • Gerenciamento de memória do sistema operacional: Em sistemas operacionais tradicionais, processos (por exemplo, Processo A e Processo B) são alocadas páginas de memória (Página 0, Página 1, etc.) na memória física. Essa alocação pode levar à fragmentação ao longo do tempo, à medida que os processos solicitam e liberam memória.
  • Gerenciamento de memória vLLM: a estrutura vLLM usa um cache de valor-chave (KV) para gerenciar a memória com mais eficiência. Solicitações (por exemplo, Solicitação A e Solicitação B) são blocos alocados do cache KV (Bloco KV 0, Bloco KV 1, etc.). Essa abordagem ajuda a minimizar a fragmentação e otimiza o uso de memória, permitindo um atendimento de modelo mais rápido e eficiente.

Mecanismo de Atenção em LLMs

Mecanismo de Atenção em LLM

Mecanismo de Atenção em LLMs

O mecanismo de atenção é um componente fundamental dos modelos de transformadores, comumente usados ​​para LLMs. Este diagrama ilustra a fórmula de atenção e seus componentes:

  • Consulta (Q): Um novo token no etapa do decodificador ou o último token que o modelo viu.
  • Chave (K): Contexto anterior que o modelo deve atender.
  • Valor (V): Soma ponderada sobre o contexto anterior.

A fórmula calcula as pontuações de atenção considerando o produto escalar da consulta com as chaves, dimensionando pela raiz quadrada da dimensão chave, aplicando uma função softmax e, finalmente, calculando o produto escalar com os valores. Este processo permite que o modelo se concentre nas partes relevantes da sequência de entrada ao gerar cada token.

Servindo comparação de rendimento

vLLM: veiculação LLM fácil, rápida e barata com PagedAttention

vLLM: veiculação LLM fácil, rápida e barata com PagedAttention

Esta imagem apresenta uma comparação da taxa de transferência de serviço entre diferentes estruturas (HF, TGI e vLLM) usando modelos LLaMA em diferentes configurações de hardware.

  • LLaMA-13B, A100-40GB: vLLM atinge rendimento 14x – 24x maior do que HuggingFace Transformers (HF) e rendimento 2.2x – 2.5x maior do que HuggingFace Text Generation Inference (TGI).
  • LLaMA-7B, A10G: Tendências semelhantes são observadas, com o vLLM superando significativamente tanto o HF quanto o TGI.

vLLM: uma nova arquitetura de serviço LLM

vLLM, desenvolvido por pesquisadores da UC Berkeley, representa um avanço significativo na tecnologia de atendimento ao LLM. Vamos explorar seus principais recursos e inovações:

Atenção Paginada

No coração do vLLM está o PagedAttention, um novo algoritmo de atenção inspirado no gerenciamento de memória virtual em sistemas operacionais. Veja como funciona:

- Particionamento de cache de valor-chave (KV): em vez de armazenar todo o cache KV de forma contígua na memória, PagedAttention o divide em blocos de tamanho fixo.
- Armazenamento não contíguo: Esses blocos podem ser armazenados de forma não contígua na memória, permitindo um gerenciamento de memória mais flexível.
- Alocação sob demanda: os blocos são alocados somente quando necessário, reduzindo o desperdício de memória.
- Compartilhamento eficiente: Múltiplas sequências podem compartilhar blocos, permitindo otimizações para técnicas como amostragem paralela e busca de feixe.

Ilustração:

""
Cache KV Tradicional:
[Token 1 KV][Token 2 KV][Token 3 KV]…[Token N KV]
(Alocação de memória contígua)

Cache KV PagedAttention:
[Bloco 1] -> Endereço Físico A
[Bloco 2] -> Endereço Físico C
[Bloco 3] -> Endereço Físico B
...
(Alocação de memória não contígua)
""

Essa abordagem reduz significativamente a fragmentação da memória e permite um uso muito mais eficiente da memória da GPU.

Lotes Contínuos

O vLLM implementa lotes contínuos, que processam solicitações dinamicamente conforme elas chegam, em vez de esperar para formar lotes de tamanho fixo. Isso leva a menor latência e maior rendimento.

Exemplo:
Imagine um fluxo de solicitações recebidas:

""
Tempo 0ms: a solicitação A chega
Tempo 10ms: Início do processamento da Solicitação A
Tempo 15ms: Solicitação B chega
Tempo 20ms: Início do processamento da Solicitação B (em paralelo com A)
Tempo 25ms: Solicitação C chega
...
""

Com lotes contínuos, o vLLM pode começar a processar cada solicitação imediatamente, em vez de esperar para agrupá-las em lotes predefinidos.

Amostragem Paralela Eficiente

Para aplicações que exigem múltiplas amostras de saída por prompt (por exemplo, assistentes de escrita criativa), os recursos de compartilhamento de memória do vLLM se destacam. Ele pode gerar múltiplas saídas enquanto reutiliza o cache KV para prefixos compartilhados.

Código de exemplo usando vLLM:

from vllm import LLM, SamplingParams

llm = LLM(model="meta-llama/Llama-2-13b-hf")
prompts = ["The future of AI is"]

# Generate 3 samples per prompt
sampling_params = SamplingParams(n=3, temperature=0.8, max_tokens=100)
outputs = llm.generate(prompts, sampling_params)

for output in outputs:
print(f"Prompt: {output.prompt}")
for i, out in enumerate(output.outputs):
print(f"Sample {i + 1}: {out.text}")

Este código gera eficientemente múltiplas amostras para o prompt fornecido, aproveitando as otimizações do vLLM.

Comparando o desempenho do vLLM

Para realmente apreciar o impacto do vLLM, vamos analisar algumas comparações de desempenho:

Comparação de rendimento

Com base nas informações fornecidas, o vLLM supera significativamente outras soluções de serviço:

– Taxa de transferência até 24x maior em comparação com Hugging Face Transformers
– Taxa de transferência 2.2x a 3.5x maior do que Hugging Face Text Generation Inference (TGI)

Ilustração:

""
Taxa de transferência (tokens/segundo)
|
| ****
| ****
| ****
| **** ****
| **** **** ****
| **** **** ****
|————————
HF TGI vLLM
""

Eficiência de memória

O PagedAttention do vLLM resulta em uso de memória quase ideal:

– Apenas cerca de 4% de desperdício de memória, em comparação com 60-80% em sistemas tradicionais
– Essa eficiência permite atender modelos maiores ou lidar com mais solicitações simultâneas com o mesmo hardware

Primeiros passos com vLLM

Agora que exploramos os benefícios do vLLM, vamos explicar o processo de configuração e utilização em seus projetos.

Instalação 6.1

Instalar o vLLM é simples usando pip:

!pip install vllm

6.2 Uso Básico para Inferência Offline

Aqui está um exemplo simples de uso do vLLM para geração de texto offline:

from vllm import LLM, SamplingParams

# Initialize the model
llm = LLM(model="meta-llama/Llama-2-13b-hf")

# Prepare prompts
prompts = [
"Write a short poem about artificial intelligence:",
"Explain quantum computing in simple terms:"
]

# Set sampling parameters
sampling_params = SamplingParams(temperature=0.8, max_tokens=100)

# Generate responses
outputs = llm.generate(prompts, sampling_params)

# Print the results
for output in outputs:
print(f"Prompt: {output.prompt}")
print(f"Generated text: {output.outputs[0].text}\n")

Este script demonstra como carregar um modelo, definir parâmetros de amostragem e gerar texto para vários prompts.

6.3 Configurando um servidor vLLM

Para serviços online, o vLLM fornece um servidor de API compatível com OpenAI. Veja como configurá-lo:

1. Inicie o servidor:

python -m vllm.entrypoints.openai.api_server --model meta-llama/Llama-2-13b-hf

2. Consulte o servidor usando curl:

curl http://localhost:8000/v1/completions \
-H "Content-Type: application/json" \
-d '{
"model": "meta-llama/Llama-2-13b-hf",
"prompt": "The benefits of artificial intelligence include:",
"max_tokens": 100,
"temperature": 0.7
}'

Essa configuração permite que você ofereça ao seu LLM uma interface compatível com a API do OpenAI, facilitando a integração em aplicativos existentes.

Tópicos avançados sobre vLLM

Embora o vLLM ofereça melhorias significativas no serviço LLM, há considerações adicionais e tópicos avançados a serem explorados:

7.1 Quantização do Modelo

Para um serviço ainda mais eficiente, especialmente em hardware com memória limitada, técnicas de quantização podem ser empregadas. Embora o vLLM em si não suporte quantização atualmente, ele pode ser usado em conjunto com modelos quantizados:

from transformers import AutoModelForCausalLM, AutoTokenizer
import torch

# Load a quantized model
model_name = "meta-llama/Llama-2-13b-hf"
model = AutoModelForCausalLM.from_pretrained(model_name, device_map="auto", load_in_8bit=True)
tokenizer = AutoTokenizer.from_pretrained(model_name)

# Use the quantized model with vLLM
from vllm import LLM

llm = LLM(model=model, tokenizer=tokenizer)

7.2 Inferência Distribuída

Para modelos extremamente grandes ou aplicações de alto tráfego, pode ser necessária a inferência distribuída entre múltiplas GPUs ou máquinas. Embora o vLLM não ofereça suporte nativo para isso, ele pode ser integrado a sistemas distribuídos usando frameworks como o Ray:

import ray
from vllm import LLM

@ray.remote(num_gpus=1)
class DistributedLLM:
  def __init__(self, model_name):
    self.llm = LLM(model=model_name)

  def generate(self, prompt, params):
    return self.llm.generate(prompt, params)

# Initialize distributed LLMs
llm1 = DistributedLLM.remote("meta-llama/Llama-2-13b-hf")
llm2 = DistributedLLM.remote("meta-llama/Llama-2-13b-hf")

# Use them in parallel
result1 = llm1.generate.remote("Prompt 1", sampling_params)
result2 = llm2.generate.remote("Prompt 2", sampling_params)

# Retrieve results
print(ray.get([result1, result2]))

7.3 Monitoramento e Observabilidade

Ao servir LLMs em produção, o monitoramento é crucial. Embora o vLLM não ofereça monitoramento integrado, você pode integrá-lo com ferramentas como Prometheus e Grafana:

from prometheus_client import start_http_server, Summary
from vllm import LLM

# Define metrics
REQUEST_TIME = Summary('request_processing_seconds', 'Time spent processing request')

# Initialize vLLM
llm = LLM(model="meta-llama/Llama-2-13b-hf")

# Expose metrics
start_http_server(8000)

# Use the model with monitoring
@REQUEST_TIME.time()
  def process_request(prompt):
      return llm.generate(prompt)

# Your serving loop here

Esta configuração permite rastrear métricas como o tempo de processamento de solicitações, que podem ser visualizadas nos painéis do Grafana.

Conclusão

Servir grandes modelos de linguagem com eficiência é uma tarefa complexa, mas crucial na era da IA. O vLLM, com seu algoritmo PagedAttention inovador e implementação otimizada, representa um avanço significativo para tornar a implantação do LLM mais acessível e econômica.

Ao melhorar drasticamente a taxa de transferência, reduzir o desperdício de memória e permitir opções de serviço mais flexíveis, o vLLM abre novas possibilidades para a integração de modelos de linguagem poderosos em uma ampla gama de aplicações. Seja para criar um chatbot, um sistema de geração de conteúdo ou qualquer outra aplicação com tecnologia de PLN, entender e utilizar ferramentas como o vLLM será fundamental para o sucesso.

Passei os últimos cinco anos mergulhando no fascinante mundo do Machine Learning e Deep Learning. Minha paixão e experiência me levaram a contribuir para mais de 50 projetos diversos de engenharia de software, com foco particular em AI/ML. Minha curiosidade contínua também me atraiu para o Processamento de Linguagem Natural, um campo que estou ansioso para explorar mais.