Melhore o desempenho dos modelos Falcon com o Amazon SageMaker | Amazon Web Services

Melhore o desempenho dos modelos Falcon com o Amazon SageMaker | Amazon Web Services

Qual é a estrutura e configuração ideais para hospedar grandes modelos de linguagem (LLMs) para aplicações de IA generativas de geração de texto? Apesar da abundância de opções para atender LLMs, esta é uma pergunta difícil de responder devido ao tamanho dos modelos, às diferentes arquiteturas de modelo, aos requisitos de desempenho dos aplicativos e muito mais. O Amazon Sage Maker Contêiner de Inferência de Modelo Grande (LMI) simplifica o atendimento de LLMs, reunindo uma série de diferentes estruturas e técnicas que otimizam a implantação de LLMs. O contêiner LMI possui uma poderosa pilha de serviços chamada Servindo DJL isso é agnóstico ao LLM subjacente. Ele fornece parâmetros de configuração em nível de sistema que podem ser ajustados para extrair o melhor desempenho da infraestrutura de hospedagem para um determinado LLM. Ele também oferece suporte para otimizações recentes, como lote contínuo, também conhecido como lote iterativo ou lote contínuo, que fornece melhorias significativas no rendimento.

Em um anterior postar, mostramos como você pode usar o contêiner LMI para implantar a família de modelos Falcon no SageMaker. Nesta postagem, demonstramos como melhorar o rendimento e a latência de servir o Falcon-40B com técnicas como lote contínuo. Também fornecemos uma compreensão intuitiva dos parâmetros de configuração fornecidos pelo contêiner SageMaker LMI que pode ajudá-lo a encontrar a melhor configuração para sua aplicação do mundo real.

Fundamentos de inferência geradora de texto para LLMs

Vejamos primeiro alguns fundamentos sobre como realizar inferência para LLMs para geração de texto.

Passagem direta, ativações e cache KV

Dada uma sequência de entrada de tokens, eles são executados em um forward pass em todas as camadas do LLM (como Falcon) para gerar o próximo token. A forward pass refere-se ao processo de dados de entrada que passam por uma rede neural para produzir uma saída. No caso da geração de texto, o avanço envolve alimentar uma semente ou contexto inicial no modelo de linguagem e gerar o próximo caractere ou token na sequência. Para gerar uma sequência de texto, o processo geralmente é feito de forma iterativa, o que significa que é repetido para cada etapa ou posição na sequência de saída. A cada iteração, o modelo gera o próximo caractere ou token, que passa a fazer parte do texto gerado, e esse processo continua até que o comprimento de texto desejado seja gerado.

A geração de texto com modelos de linguagem como Falcon ou GPT é autoregressive. Isso significa que o modelo gera um token por vez enquanto condiciona os tokens gerados anteriormente. Em outras palavras, a cada iteração, o modelo pega o texto gerado anteriormente como entrada e prevê o próximo token com base nesse contexto. Como mencionado em vLLM: veiculação LLM fácil, rápida e barata com PagedAttention, neste processo de decodificação autorregressiva, todos os tokens de entrada para o LLM produzem sua chave de atenção e tensores de valor, e esses tensores são mantidos na memória da GPU para gerar os próximos tokens. Esses tensores de chave e valor armazenados em cache são frequentemente chamados de KV cache.

Fases de pré-preenchimento e decodificação

Em um processo de decodificação autorregressiva, como aquele usado na geração de texto com modelos de linguagem como o Falcon, normalmente existem duas fases principais: a prefill fase e o decode Estágio. Essas fases são cruciais para gerar um texto coerente e contextualmente relevante.

A fase de pré-preenchimento inclui o seguinte:

  • Contexto inicial – A fase de pré-preenchimento começa com um contexto inicial ou texto inicial fornecido pelo usuário. Esse contexto inicial pode ser uma frase, uma frase ou até mesmo uma única palavra. Ele define o ponto de partida para a geração de texto e fornece contexto para o que vem a seguir.
  • Condicionamento de modelo – O contexto fornecido é usado para condicionar o modelo de linguagem. O modelo toma esse contexto como entrada e gera o próximo token (palavra ou caractere) na sequência com base na compreensão do contexto.
  • Geração de token – O modelo gera um token por vez, prevendo o que deve vir a seguir no texto. Este token é anexado ao contexto, estendendo-o efetivamente.
  • Processo interativo – O processo de geração de tokens é repetido iterativamente. A cada etapa, o modelo gera um token considerando o contexto atualizado, que agora inclui os tokens gerados nas etapas anteriores.

A fase de pré-enchimento continua até que uma condição de parada predeterminada seja atendida. Esta condição pode ser um comprimento máximo para o texto gerado, um token específico que sinaliza o final do texto ou qualquer outro critério definido pelo usuário ou pela aplicação.

A fase de decodificação inclui o seguinte:

  • Realização – Após a fase de pré-preenchimento, você tem um texto parcialmente gerado que pode ficar incompleto ou cortado em algum momento. A fase de decodificação é responsável por completar o texto para torná-lo coerente e gramaticalmente correto.
  • Continuação do último token – Na fase de decodificação, o modelo começa a partir do último token gerado durante a fase de pré-preenchimento. Ele usa esse token como contexto inicial e gera o próximo token para continuar o texto.
  • Conclusão iterativa – Assim como na fase de pré-preenchimento, o processo de geração de tokens é novamente iterativo. O modelo gera um token por vez, condicionado aos tokens anteriores na sequência.
  • Condição de parada – A fase de decodificação também possui uma condição de parada, que pode ser a mesma da fase de pré-preenchimento, como atingir um comprimento máximo ou encontrar um token de fim de texto. Quando esta condição for atendida, o processo de geração é interrompido.

A combinação das fases de pré-preenchimento e decodificação permite que modelos autorregressivos gerem texto que se baseia em um contexto inicial e produz sequências de texto coerentes, contextualmente relevantes e contextualmente consistentes.

Consulte Um sistema de serviço distribuído para modelos generativos baseados em transformadores para obter uma explicação detalhada do processo.

Otimizando o rendimento usando lote dinâmico

Até agora, falamos apenas sobre uma única entrada. Na prática, esperamos lidar com múltiplas solicitações que chegam aleatoriamente dos clientes da aplicação para inferência, simultaneamente ou de forma escalonada. Da forma tradicional, o lote básico pode ser usado para aumentar o rendimento e a utilização dos recursos computacionais da GPU. O lote é combinar efetivamente as representações numéricas de mais de uma solicitação em um lote e executar execuções paralelas dos passes de encaminhamento autoregressivos. Esse lote inteligente é feito no lado da porção. O servidor DJLServing do SageMaker LMI pode ser configurado para agrupar várias solicitações para processá-las em paralelo, definindo os seguintes parâmetros em servindo.propriedades:

  • max_batch_delay = 100 – O atraso máximo para agregação em lote em milissegundos. O valor padrão é 100 milissegundos.
  • tamanho do batch = 32 – O tamanho do lote dinâmico. O padrão é 1.

Isso basicamente mostra que DJLServing enfileirará solicitações por 100 milissegundos por vez ou se o número de solicitações enfileiradas for até o batch_size especificado, o lote será agendado para execução no back-end para inferência. Isso é conhecido como dynamic batching. É dinâmico porque o tamanho do lote pode mudar entre lotes, dependendo de quantas solicitações foram adicionadas nesse período. No entanto, como as solicitações podem ter características diferentes (por exemplo, algumas solicitações podem ter o formato de 20 tokens de entrada e 500 tokens de saída, enquanto outras podem ser invertidas, com 500 tokens de entrada, mas apenas 20 de saída), algumas solicitações podem conclua o processamento mais rápido do que outros no mesmo lote. Isso pode resultar na subutilização da GPU enquanto espera que todas as solicitações em andamento no lote concluam seu estágio de decodificação, mesmo se houver solicitações adicionais aguardando para serem processadas na fila. O diagrama a seguir ilustra esse processo.

Visual de lote dinâmico simples

Visual de lote dinâmico – observe as janelas ociosas no final da solicitação 2 e 3

Otimizando o rendimento usando lotes contínuos

Com o continuous batching, também conhecido como iterative or rolling lote, aproveitamos as diferenças entre os estágios de pré-preenchimento e decodificação. Para ativar o lote contínuo, DJServing fornece as seguintes configurações adicionais conforme servindo.properties:

  • motor=MPI – Recomendamos que você use o mecanismo MPI para lotes contínuos.
  • opção.rolling_batch=auto ou lmi-dist – Recomendamos usar auto porque ele escolherá automaticamente o algoritmo de lote contínuo mais apropriado junto com outras otimizações no futuro.
  • opção.max_rolling_batch_size=32 – Isso limita o número de solicitações simultâneas. O padrão é 32.

Com o lote contínuo, a pilha de serviços (DJLServing) não espera que todas as solicitações em andamento em um lote concluam seu estágio de decodificação. Em vez disso, em interrupções lógicas (no final de uma iteração no estágio de decodificação), ele puxa solicitações adicionais que estão aguardando na fila enquanto o lote atual ainda está sendo processado (daí o nome lote rolante). Ele faz essa verificação de solicitações pendentes ao final de cada iteração do estágio de decodificação. Lembre-se, para cada solicitação, precisamos executar o estágio de pré-preenchimento seguido pelo estágio de decodificação sequencial. Como podemos processar todos os tokens do prompt inicial de uma solicitação em paralelo para seu estágio de pré-preenchimento, sempre que uma nova solicitação é recebida, pausamos temporariamente o estágio de decodificação das solicitações em andamento do lote – salvamos temporariamente seu cache KV e ativações na memória e executar a etapa de pré-preenchimento das novas solicitações.

O tamanho deste cache pode ser configurado com a seguinte opção:

Quando o pré-preenchimento é concluído, combinamos as novas solicitações e as antigas solicitações pausadas em um novo lote contínuo, que pode prosseguir com seu estágio de decodificação em paralelo. Observe que as solicitações antigas pausadas podem continuar seu estágio de decodificação de onde pararam e as novas solicitações começarão a partir do primeiro token novo.

Visual de lote contínuo ou iterativo

Visual de lote contínuo ou iterativo – observe que os tempos ociosos são substituídos por solicitações subsequentes

Você já deve ter percebido que o lote contínuo é uma abordagem quase semelhante com a qual paralelizamos naturalmente as tarefas em nossas vidas diárias. Temos mensagens, e-mails, notificações por telefone (potencialmente novas solicitações) chegando em momentos aleatórios (análogo a várias solicitações que chegam de forma escalonada aleatória para GPUs). Tudo isso acontece enquanto concluímos nossas tarefas de voo – redigir e-mails, codificar, participar de reuniões (análogas às tarefas de processamento atuais nas GPUs). Nos intervalos lógicos, pausamos nossas tarefas em voo e verificamos nossas notificações para decidir se há alguma ação necessária de nossa parte e, se houver, adicionamos isso às nossas tarefas em voo (lote contínuo da vida real), ou coloque-o em uma lista de tarefas (a fila).

Juntando tudo: como pensar sobre a utilização de memória das GPUs

Recomenda-se testar a carga do seu modelo para ver qual configuração é mais econômica para o seu caso de uso comercial. Para entender, vamos visualizar o consumo de memória das GPUs à medida que o modelo é carregado e à medida que solicitações sucessivas são processadas em um lote contínuo. Para esta postagem, vamos supor que estamos carregando o modelo Falcon-40B em uma das instâncias do tipo G5 instaladas com GPUs NVIDIA A10G, cada uma com 24 GB de memória. Observe que um entendimento semelhante se aplica aos tipos de instância p3, p4 e p5, que vêm com as séries de GPU V100, A100 e H100.

A seguir está uma visão geral de como obter um valor aproximado da memória total necessária para servir o Falcon-40B:

  • Tamanho do modelo = Número de parâmetros do modelo (40 bilhões para Falcon-40B) x 4 bytes por parâmetro (para FP32) = 160 GB
  • Memória total aproximada necessária para carregar o Falcon-40B para inferência = Tamanho do modelo (=160 GB) + Cache KV (cache de atenção) (=*20 GB) + sobrecarga de memória adicional por ML Frameworks (aproximadamente 2 GB)
MemóriaVisual

Memory Visual – Compreendendo o consumo de memória de um modelo Falcon-40B carregado

Para o Falcon-40B, se compactarmos o modelo quantizando o modelo para o tipo de dados bfloat16 (2 bytes), o tamanho do modelo será de aproximadamente 80 GB. Como você pode ver, isso ainda é maior que a memória suportada por um dispositivo acelerador, então precisamos adotar uma técnica de particionamento de modelo (sharding) com um especial paralelismo tensorial (TP) aborda e fragmenta o modelo em vários dispositivos aceleradores. Vamos supor que escolhemos g5.24xlarge, que possui 4 dispositivos GPU A10G. Se configurarmos DJLServing (serving.properties) com o seguinte, podemos esperar que os 80 GB de peso do modelo serão divididos igualmente entre todas as 4 GPUs:

Com o tensor_parallel_degree definido como 4, cerca de 20 GB da memória GPU de 24 GB (aproximadamente 84%) já são utilizados mesmo antes de uma única solicitação ter sido processada. Os 16% restantes da GPU serão usados ​​para o cache KV para as solicitações recebidas. É possível que, para o seu cenário de negócios e seus requisitos de latência e taxa de transferência, 2 a 3 GB de memória restante sejam mais que suficientes. Caso contrário, você pode aumentar o tamanho da instância para g5.48xlarge, que tem 8 GPUs e usa tensor_parallel_degree definido como 8. Nesse caso, apenas aproximadamente 10 GB dos 24 GB de memória disponíveis de cada GPU são utilizados para pesos de modelo e nós tenha cerca de 60% da GPU restante para as ativações e cache KV. Intuitivamente, podemos ver que esta configuração pode nos permitir ter um rendimento maior. Além disso, como temos um buffer maior agora, podemos aumentar o max_rolling_batch_prefill_tokens e max_rolling_batch_size parâmetros para otimizar ainda mais o rendimento. Juntos, esses dois parâmetros controlarão as pré-alocações dos pré-preenchimentos de ativação e do cache KV para o modelo. Um número maior para esses dois parâmetros estará correlacionado a uma taxa de transferência maior, assumindo que você tenha buffer suficiente para o cache KV na memória da GPU.

Lotes contínuos com PagedAttention

PagedAttention é um novo algoritmo de otimização desenvolvido pela UC Berkeley que melhora o processo de lote contínuo, permitindo que o cache de atenção (cache KV) seja não contíguo, alocando memória em páginas ou blocos de tamanho fixo. Isso é inspirado na memória virtual e nos conceitos de paginação usados ​​pelos sistemas operacionais.

Conforme vLLM No papel, o cache de atenção de cada sequência de tokens é particionado em blocos e mapeado para blocos físicos por meio de uma tabela de blocos. Durante o cálculo da atenção, um kernel PagedAttention pode usar a tabela de blocos para buscar com eficiência os blocos da memória física. Isso resulta em uma redução significativa do desperdício de memória e permite lotes maiores, maior utilização da GPU e maior rendimento.

Comparação de desempenho

Para garantir testes de carga eficazes da sua configuração de implantação, recomenda-se começar por considerar o cenário empresarial e definir claramente as características da entrada e saída da aplicação baseada em LLM. Por exemplo, se você estiver trabalhando em um caso de uso de resumo de call center, a entrada poderá consistir em um texto maior, como uma transcrição de bate-papo de 500 tokens entre um agente de atendimento ao cliente e um cliente, mas a saída poderá ser relativamente menor, em torno de 100 tokens. tokens, representando um resumo da transcrição. Por outro lado, se você estiver trabalhando em um cenário de geração de código, a entrada pode ser tão curta quanto 15 tokens, como “escrever uma implementação eficiente em Python para descrever todos os recursos do EC2, incluindo paginação”, mas a saída pode ser muito maior, chegando a 500 tokens. Também é importante considerar se alcançar uma latência mais baixa ou maximizar o rendimento é a principal prioridade para o seu cenário específico.

Depois de obter uma compreensão abrangente do cenário de negócios, você poderá analisar e determinar a configuração ideal para seu ambiente de hospedagem. Neste contexto, o ambiente de hospedagem abrange vários elementos-chave, incluindo o tipo de instância e outros parâmetros de configuração, como tensor_parallel_degree, max_rolling_batch_size, max_rolling_batch_prefill_tokens, e mais. Nosso objetivo é identificar a configuração mais eficaz para dar suporte aos nossos requisitos de tempo de resposta, rendimento e qualidade de saída do modelo.

Em nossa análise, avaliamos o desempenho para ilustrar os benefícios do lote contínuo em relação ao lote dinâmico tradicional. Usamos as configurações detalhadas na tabela a seguir em servindo.properties para lote dinâmico e lote iterativo, usando um contêiner LMI no SageMaker.

Lote Dinâmico Lotes Contínuos Lotes contínuos com PagedAttention

motor=Python

opção.model_id=tiiuae/falcon-40b

opção.tensor_parallel_degree=8

opção.dtype=fp16

tamanho_do_lote=4

max_batch_delay=100

opção.trust_remote_code = verdadeiro

motor = MPI

opção.model_id = {{s3_url}}

opção.trust_remote_code = verdadeiro

opção.tensor_parallel_degree = 8

opção.max_rolling_batch_size = 32

opção.rolling_batch = automático

opção.dtype = fp16

opção.max_rolling_batch_prefill_tokens = 1024

option.paged_attention = Falso

motor = MPI

opção.model_id = {{s3_url}}

opção.trust_remote_code = verdadeiro

opção.tensor_parallel_degree = 8

opção.max_rolling_batch_size = 32

opção.rolling_batch = automático

opção.dtype = fp16

opção.max_rolling_batch_prefill_tokens = 1024

opção.paged_attention = Verdadeiro

As duas configurações foram avaliadas para Falcon-40B com o tipo de dados FP16 implantado em ml.g5.48xlarge em alguns cenários diferentes que representam aplicações do mundo real:

  • Um pequeno número de tokens de entrada com um grande número de tokens sendo gerados – Neste cenário, o número de tokens de entrada foi fixado em 32 e 128 novos tokens foram gerados
Estratégia de lote Taxa de transferência (tokens/s) Latência p90 (seg)
Lote Dinâmico 5.53 58.34
Lotes Contínuos 56.04 4.74
Lotes contínuos com PagedAttention 59.18 4.76
  • Uma grande entrada com um pequeno número de tokens sendo gerados – Aqui, fixamos o número de tokens de entrada em 256 e solicitamos ao LLM que resumisse a entrada em 32 tokens
Estratégia de lote Taxa de transferência (tokens/s) Latência p90 (seg)
Lote Dinâmico 19.96 59.31
Lotes Contínuos 46.69 3.88
Lotes contínuos com PagedAttention 44.75 2.67

Podemos ver que o lote contínuo com PagedAttention fornece uma melhoria de rendimento de 10 vezes maior no cenário 1 e 2.3 vezes no cenário 2 em comparação ao uso de lote dinâmico no SageMaker ao usar o contêiner LMI.

Conclusão

Nesta postagem, vimos como os LLMs usam memória e explicamos como o lote contínuo melhora o rendimento usando um contêiner LMI no SageMaker. Demonstramos os benefícios do lote contínuo para Falcon-40B usando um contêiner LMI no SageMaker, mostrando resultados de benchmark. Você pode encontrar o código no GitHub repo.


Sobre os autores

Abhigyan ShivadityaAbhi Shivaditya é Arquiteto de Soluções Sênior na AWS, trabalhando com organizações empresariais globais estratégicas para facilitar a adoção de serviços da AWS em áreas como Inteligência Artificial, computação distribuída, rede e armazenamento. Sua experiência está em Deep Learning nos domínios de Processamento de Linguagem Natural (NLP) e Visão Computacional. A Abhi ajuda os clientes a implantar modelos de aprendizado de máquina de alto desempenho com eficiência no ecossistema da AWS.

Melhore o desempenho dos modelos Falcon com o Amazon SageMaker | Inteligência de dados PlatoBlockchain da Amazon Web Services. Pesquisa vertical. Ai.Dhawal Patel é Arquiteto Principal de Machine Learning na AWS. Ele trabalhou com organizações que vão desde grandes empresas até startups de médio porte em problemas relacionados à computação distribuída e Inteligência Artificial. Ele se concentra em aprendizado profundo, incluindo domínios de PNL e Visão Computacional. Ele ajuda os clientes a obter inferência de modelo de alto desempenho no SageMaker.

Melhore o desempenho dos modelos Falcon com o Amazon SageMaker | Inteligência de dados PlatoBlockchain da Amazon Web Services. Pesquisa vertical. Ai.Pinak Panigrahi trabalha com clientes para criar soluções orientadas por aprendizado de máquina para resolver problemas estratégicos de negócios na AWS. Quando não está ocupado com aprendizado de máquina, ele pode ser encontrado fazendo caminhadas, lendo um livro ou assistindo esportes.

Melhore o desempenho dos modelos Falcon com o Amazon SageMaker | Inteligência de dados PlatoBlockchain da Amazon Web Services. Pesquisa vertical. Ai.Abhi Sodhani ocupa o cargo de arquiteto sênior de soluções de IA/ML na AWS, onde se especializou em oferecer conhecimento técnico e orientação em soluções de IA generativa e ML aos clientes. Seu foco principal é ajudar as empresas nativas digitais a concretizar todo o potencial das tecnologias de IA generativa e ML, permitindo-lhes atingir seus objetivos de negócios de forma eficaz. Além de seus esforços profissionais, Abhi demonstra uma forte paixão por atividades intelectuais, como a leitura, bem como pelo envolvimento em atividades que promovam o bem-estar físico e mental, como ioga e meditação.

Melhore o desempenho dos modelos Falcon com o Amazon SageMaker | Inteligência de dados PlatoBlockchain da Amazon Web Services. Pesquisa vertical. Ai.Qinglan é engenheiro de desenvolvimento de software na AWS. Ele vem trabalhando em vários produtos desafiadores na Amazon, incluindo soluções de inferência de ML de alto desempenho e sistema de registro de alto desempenho. A equipe de Qing lançou com sucesso o primeiro modelo Billion-parameter no Amazon Advertising com latência muito baixa necessária. Qing possui profundo conhecimento sobre otimização de infraestrutura e aceleração de Deep Learning.

Carimbo de hora:

Mais de Aprendizado de máquina da AWS