Selecionando e configurando mecanismos de inferência para LLMs

Publicados: 2024-04-02
Mostrar índice
Introdução aos motores de inferência
Análise Comparativa de Motores de Inferência
TGI
vLLM
Afrodite
Métricas e Medidas
Configurando e configurando TGI para alto rendimento
Observações e Métricas
Conclusão
Leia outros artigos relacionados ao modelo de linguagem grande:

Introdução aos motores de inferência

Existem muitas técnicas de otimização desenvolvidas para mitigar as ineficiências que ocorrem nas diferentes etapas do processo de inferência. É difícil dimensionar a inferência em escala com técnicas/transformadores vanilla. Os mecanismos de inferência reúnem as otimizações em um pacote e nos facilitam o processo de inferência.

Para um conjunto muito pequeno de testes ad hoc ou referência rápida, podemos usar o código do transformador vanilla para fazer a inferência.

O cenário dos mecanismos de inferência está evoluindo rapidamente. Como temos diversas opções, é importante testar e listar o melhor dos melhores para casos de uso específicos. Abaixo estão alguns experimentos de motores de inferência que fizemos e os motivos pelos quais descobrimos porque funcionaram em nosso caso.

Para o nosso modelo Vicuna-7B ajustado, tentamos

  • TGI
  • vLLM
  • Afrodite
  • Optimum-Nvidia
  • PowerInfer
  • LAMACPP
  • Ctranslate2

Examinamos a página do github e seu guia de início rápido para configurar esses mecanismos, PowerInfer, LlaamaCPP, Ctranslate2 não são muito flexíveis e não suportam muitas técnicas de otimização, como lote contínuo, atenção paginada e desempenho abaixo da média quando comparado a outros mecanismos mencionados .

Para obter maior rendimento, o mecanismo/servidor de inferência deve maximizar a memória e os recursos de computação e tanto o cliente quanto o servidor devem trabalhar de forma paralela/assíncrona para atender solicitações para manter o servidor sempre em funcionamento. Conforme mencionado anteriormente, sem a ajuda de técnicas de otimização como PagedAttention, Flash Attention, Continuous batching, isso sempre levará a um desempenho abaixo do ideal.

TGI, vLLM e Afrodite são candidatos mais adequados nesse aspecto e, ao fazer vários experimentos indicados abaixo, encontramos a configuração ideal para extrair o desempenho máximo da inferência. Técnicas como lote contínuo e atenção paginada são habilitadas por padrão, a decodificação especulativa precisa ser habilitada manualmente no mecanismo de inferência para os testes abaixo.

Análise Comparativa de Motores de Inferência

TGI

Para usar o TGI, podemos passar pela seção 'Introdução' da página do github, aqui o docker é a maneira mais simples de configurar e usar o mecanismo TGI.

Argumentos do iniciador de geração de texto -> esta lista as diferentes configurações que podemos usar no lado do servidor. Poucos importantes,

  • –max-input-length : determina o comprimento máximo da entrada do modelo, isso requer alterações na maioria dos casos, pois o padrão é 1024.
  • –max-total-tokens: máx. total de tokens, ou seja, comprimento do token de entrada + saída.
  • –speculate, –quantiz, –max-concurrent-requests -> o padrão é apenas 128, o que é obviamente menor.

Para iniciar um modelo local ajustado,

docker run –gpus device=1 –shm-size 1g -p 9091:80 -v /path/to/fine_tuned_v1:/model ghcr.io/huggingface/text-generation-inference:1.4.4 –model-id /model – dtype float16 –num-shard 1 –max-input-length 3600 –max-total-tokens 4000 –especular 2

Para iniciar um modelo do hub,

modelo=”lmsys/vicuna-7b-v1.5″; volume=$PWD/dados; token=”<hf_token>”; docker run –gpus all –shm-size 1g -e HUGGING_FACE_HUB_TOKEN=$token -p 9091:80 -v $volume:/data ghcr.io/huggingface/text-generation-inference:1.4.4 –model-id $model – dtype float16 –num-shard 1 –max-input-length 3600 –max-total-tokens 4000 –especular 2

Você pode pedir ao chatGPT para explicar o comando acima para uma compreensão mais detalhada. Aqui estamos iniciando o servidor de inferência na porta 9091. E podemos usar um cliente de qualquer idioma para enviar uma solicitação ao servidor. API de inferência de geração de texto -> menciona todos os endpoints e parâmetros de carga útil para solicitação.

Por exemplo

carga útil=”<solicite aqui>”

curl -XPOST “0.0.0.0:9091/generate” -H “Tipo de conteúdo: aplicação/json” -d “{“inputs”: $payload, “parâmetros”: {“max_new_tokens”: 400,”do_sample”:false ,”best_of”: null,”repetition_penalty”: 1,”return_full_text”: false,”seed”: null,”stop_sequences”: null,”temperatura”: 0,1,”top_k”: 100,”top_p”: 0,3,” truncar”: nulo,”típico_p”: nulo,”marca d'água”: falso,”decodificador_input_details”: falso}}”

Poucas observações,

  • A latência aumenta com max-token-tokens, o que é óbvio: se estivermos processando texto longo, o tempo geral aumentará.
  • Especular ajuda, mas depende do caso de uso e da distribuição de entrada-saída.
  • A quantização Eetq ajuda ao máximo a aumentar o rendimento.
  • Se você tiver uma multi GPU, executar 1 API em cada GPU e ter essas APIs multi GPU atrás de um balanceador de carga resulta em uma taxa de transferência mais alta do que a fragmentação pelo próprio TGI.

vLLM

Para iniciar um servidor vLLM, podemos usar um servidor/docker REST API compatível com OpenAI. Para começar é muito simples, siga Deploying with Docker — vLLM, se for usar um modelo local, anexe o volume e use o caminho como nome do modelo,

docker run –runtime nvidia –gpus device=1 –shm-size 1g -v /path/to/fine_tuned_v1:/model -v ~/.cache/ -p 8000:8000 –ipc=host vllm/vllm-openai:latest – modelo/modelo

Acima irá iniciar um servidor vLLM na porta 8000 mencionada, como sempre você pode brincar com argumentos.

Faça solicitação de postagem com,

“`concha

carga útil=”<solicite aqui>”

curl -XPOST -m 1200 “0.0.0.0:8000/v1/completions” -H “Tipo de conteúdo: aplicativo/json” -d “{“prompt”: $payload,”model”:”/model” ,”max_tokens ”: 400,”top_p”: 0,3, “top_k”: 100, “temperatura”: 0,1}”

“`

Afrodite

“`concha

pip instalar motor afrodite

python -m afrodite.endpoints.openai.api_server –model PygmalionAI/pygmalion-2-7b

“`

Ou

“`

docker run -v /caminho/para/fine_tuned_v1:/model -d -e MODEL_NAME=”/model” -p 2242:7860 –gpus device=1 –ipc host alpindale/aphrodite-engine

“`

Afrodite fornece instalação pip e docker conforme mencionado na seção de primeiros passos. O Docker geralmente é relativamente mais fácil de rodar e testar. Opções de uso, opções de servidor nos ajudam a fazer solicitações.

  • Afrodite e vLLM usam cargas úteis baseadas em servidor openAI, para que você possa verificar sua documentação.
  • Tentamos o deepspeed-mii, pois está em estado de transição (quando tentamos) do legado para a nova base de código, não parece confiável e fácil de usar.
  • Optimum-NVIDIA não oferece suporte a outras otimizações importantes e resulta em desempenho abaixo do ideal, link de referência.
  • Adicionada uma essência, o código que usamos para fazer as solicitações paralelas ad hoc.

Métricas e Medidas

Queremos experimentar e descobrir:

  1. Ótimo não. de threads para o servidor cliente/mecanismo de inferência.
  2. Como a taxa de transferência aumenta com o aumento da memória
  3. Como a taxa de transferência aumenta em relação aos núcleos do tensor.
  4. Efeito de threads versus solicitação paralela por cliente.

Uma maneira muito básica de observar a utilização é observá-la através dos utilitários linux nvidia-smi, nvtop, isso nos dirá a memória ocupada, utilização de computação, taxa de transferência de dados, etc.

Outra forma é traçar o perfil do processo usando GPU com nsys.

S.Não GPU Memória vRAM Motor de inferência Tópicos Tempo(s) Especular
1 A6000 48/48GB TGI 24 664
2 A6000 48/48GB TGI 64 561
3 A6000 48/48GB TGI 128 554
4 A6000 48/48GB TGI 256 568

Com base nos experimentos acima, o thread 128/256 é melhor do que o número de threads inferior e além de 256 a sobrecarga começa a contribuir para a redução do rendimento. Descobriu-se que isso depende da CPU e da GPU e precisa de um experimento próprio.
5 A6000 48/48GB TGI 128 596 2
6 A6000 48/48GB TGI 128 945 8

Maior valor especulativo causando mais rejeições para nosso modelo ajustado e, assim, reduzindo o rendimento. 1/2 como valor especulado é bom, está sujeito ao modelo e não há garantia de que funcione da mesma forma em todos os casos de uso. Mas a conclusão é que a decodificação especulativa melhora o rendimento.
7 3090 24/24 GB TGI 128 741 2
7 4090 24/24 GB TGI 128 481 2

Embora o 4090 tenha menos vRAM em comparação com o A6000, ele apresenta desempenho superior devido à maior contagem de núcleos tensores e velocidade de largura de banda da memória.
8 A6000 24/48 GB TGI 128 707 2
9 A6000 2 x 24/48 GB TGI 128 1205 2

Configurando e configurando TGI para alto rendimento

Configure a solicitação assíncrona em uma linguagem de script de sua preferência, como python/ruby, e use o mesmo arquivo de configuração que encontramos:

  1. O tempo gasto aumenta em relação ao comprimento máximo de saída da geração de sequência.
  2. 128/256 threads no cliente e no servidor são melhores que 24, 64, 512. Ao usar threads inferiores, a computação está sendo subutilizada e além de um limite como 128, a sobrecarga se torna maior e, portanto, a taxa de transferência é reduzida.
  3. Há uma melhoria de 6% ao saltar de solicitações assíncronas para paralelas usando 'GNU paralelo' em vez de threading em linguagens como Go, Python/Ruby.
  4. O 4090 tem rendimento 12% maior que o A6000. Embora o 4090 tenha menos vRAM em comparação com o A6000, ele apresenta desempenho superior devido à maior contagem de núcleos tensores e velocidade de largura de banda da memória.
  5. Como o A6000 possui 48GB de vRAM, para concluir se a RAM extra ajuda a melhorar o rendimento ou não, tentamos usar frações de memória da GPU no experimento 8 da tabela, vemos que a RAM extra ajuda na melhoria, mas não linearmente. Além disso, quando tentamos dividir, ou seja, hospedar 2 APIs na mesma GPU usando metade da memória para cada API, ele se comporta como 2 APIs sequenciais em execução, em vez de aceitar solicitações paralelamente.

Observações e Métricas

Abaixo estão os gráficos de alguns experimentos e o tempo necessário para concluir um conjunto de entradas fixas. Quanto menor o tempo necessário, melhor.

  • Mencionados são os threads do lado do cliente. Lado do servidor que precisamos mencionar ao iniciar o mecanismo de inferência.

Especular testes:

Teste de vários mecanismos de inferência:

Mesmo tipo de experimentos feitos com outros mecanismos como vLLM e Afrodite, observamos resultados semelhantes, no momento em que escrevemos este artigo, vLLM e Afrodite ainda não suportam decodificação especulativa, o que nos deixa escolher TGI, pois oferece maior rendimento do que o restante devido à decodificação especulativa.

Além disso, você pode configurar criadores de perfil de GPU para melhorar a observabilidade, auxiliando na identificação de áreas com uso excessivo de recursos e otimizando o desempenho. Leia mais: Ferramentas de desenvolvedor Nvidia Nsight - Max Katz

Conclusão

Vemos que o cenário da geração de inferência está em constante evolução e melhorar o rendimento no LLM requer um bom entendimento da GPU, métricas de desempenho, técnicas de otimização e desafios associados às tarefas de geração de texto. Isso ajuda na escolha das ferramentas certas para o trabalho. Ao compreender os componentes internos da GPU e como eles correspondem à inferência LLM, como aproveitar núcleos tensores e maximizar a largura de banda da memória, os desenvolvedores podem escolher a GPU econômica e otimizar o desempenho de forma eficaz.

Diferentes placas GPU oferecem recursos variados, e compreender as diferenças é crucial para selecionar o hardware mais adequado para tarefas específicas. Técnicas como lote contínuo, atenção paginada, fusão de kernel e atenção flash oferecem soluções promissoras para superar os desafios emergentes e melhorar a eficiência. TGI parece a melhor escolha para nosso caso de uso com base nos experimentos e resultados que obtemos.

Leia outros artigos relacionados ao modelo de linguagem grande:

Compreendendo a arquitetura de GPU para otimização de inferência LLM

Técnicas avançadas para melhorar o rendimento do LLM