Skip to content

Otimização de inferência

A otimização de inferência pode ser feita nos níveis de modelo, hardware e serviço. No nível do modelo, você pode reduzir o tamanho de um modelo treinado ou desenvolver arquiteturas mais eficientes, como uma arquitetura sem os gargalos de computação no mecanismo de atenção, frequentemente usado nos modelos transformer. No nível do hardware, você pode projetar hardware mais poderoso.

A otimização de inferência é um campo interdisciplinar que geralmente vê a colaboração entre pesquisadores de modelos, desenvolvedores de aplicativos, engenheiros de sistema, designers de compiladores, arquitetos de hardware e até operadores de data center.

Entendendo a otimização de inferência

Existem duas fases distintas no ciclo de vida de um modelo de IA: treinamento e inferência. O treinamento refere-se ao processo de construção de um modelo. Inferência refere-se ao processo de uso de um modelo para calcular uma saída para uma determinada entrada. A menos que você treine ou faça o ajuste fino do modelo, você precisará se importar com a inferência.

Visão geral da inferência

Em produção, o componente que executa a inferência do modelo é chamado de servidor de inferência. Ele hospeda os modelos disponíveis e tem acesso ao hardware necessário. Um servidor de inferência faz parte de um serviço de inferência mais amplo, que também é responsável por receber, rotear e possivelmente pré-processar pedidos antes que eles atinjam o servidor de inferência.

APIs de modelos, como as fornecidas pelo OpenAI e pelo Google, são serviços de inferência. Se você usar um desses serviços, não estará implementando a maioria das técnicas discutidas neste capítulo. No entanto, se você se hospedar um modelo, será responsável pela construção, otimização e gestão de seu serviço de inferência.

Gargalos computacionais

Otimização é sobre identificar gargalos e abordá-los. Existem dois gargalos computacionais principais, de computação e de largura de banda de memória:

  • Computação: refere-se a tarefas cujo tempo de conclusão é determinado pelo cálculo necessário para as tarefas.
  • Largura de banda de memória: essas tarefas são restringidas pela taxa de transferência de dados dentro do sistema, como a velocidade do movimento dos dados entre memória e processadores.

Diferentes técnicas de otimização visam mitigar diferentes gargalos. Por exemplo, uma carga de trabalho de computação pode ser acelerada ao espalhá-la em mais chips ou alavancando chips com mais potência computacional (por exemplo, um número mais alto de FLOP/s). Uma carga de trabalho relacionada à largura de banda pode ser acelerada alavancando chips com largura de banda mais alta.

Arquiteturas e cargas de trabalho diferentes resultam em diferentes gargalos computacionais. Por exemplo, inferência para geradores de imagens como o Stable Diffusion é tipicamente ligada à computação, enquanto a inferência para modelos de linguagem de auto-regressão é tipicamente ligada à largura de banda da memória.

APIs de inferência online e em lote

Muitos fornecedores oferecem dois tipos de APIs de inferência, online e lote:

  • APIs online otimizam a latência. As solicitações são processadas assim que chegam.
  • APIs em lote otimizam o custo. Se o seu aplicativo não tiver requisitos rígidos de latência, você pode enviá-los para APIs em lote para um processamento mais eficiente.

Uma API on-line se concentra na menor latência, enquanto uma API em lote se concentra na maior taxa de transferência.

As APIs geralmente retornam respostas completas por padrão. No entanto, com a decodificação auto-regressiva, pode levar muito tempo para um modelo completar uma resposta, e os usuários são impacientes. Muitas APIs on-line oferecem modo de streaming, que retorna cada token como é gerado. Isso reduz o tempo que os usuários precisam esperar até o primeiro token.

Métricas de desempenho de inferência

Latência, TTFT e TPOT

A latência mede o tempo do momento em que os usuários enviam uma consulta até que eles recebam a resposta completa. Para geração auto-regressiva, especialmente no modo de streaming, a latência geral pode ser dividida em várias métricas:

  • Hora de primeiro token: TTFT (Time to first token) mede a rapidez com que o primeiro token é gerado depois que os usuários enviam uma consulta.
  • Tempo por token de saída: o TPOT (Time per output token) mede a rapidez com que cada token de saída é gerado após o primeiro token.
  • Tempo entre os tokens e a latência entre tokens: as variações dessa métrica incluem tempo entre os tokens (TBT – time between tokens) e a latência entre toques (ITL – inter-token latency). Ambos medem o tempo entre os tokens de saída.

A latência total será igual a TTFT + TPOT × (número de tokens de saída).

É mais útil analisar a latência nos percentis, pois eles dizem algo sobre uma certa porcentagem de suas solicitações. O percentil mais comum é o percentil 50, abreviado como P50 (mediana). Se a mediana for 100 ms, metade dos pedidos leva mais de 100 ms para gerar o primeiro token e metade leva menos de 100 ms.

Throughput and goodput

A taxa de transferência (Throughput) mede o número de tokens de saída por segundo que um serviço de inferência pode gerar para todos os usuários e solicitações.

A taxa de transferência é normalmente medida como tokens/s (TPS). Se você serve a vários usuários, tokens/s/usuário também será usado ​​para avaliar como o sistema escala com mais usuários.

O que é considerado um bom rendimento depende do modelo, do hardware e da carga de trabalho. Modelos menores e chips de ponta normalmente resultam em maior taxa de transferência. Cargas de trabalho com comprimentos de entrada e saída consistentes são mais fáceis de otimizar do que cargas de trabalho com comprimentos variáveis.

Devido a esse trade-off, o foco em um serviço de inferência baseado apenas em sua taxa de transferência e seu custo pode levar a uma má experiência do usuário. Em vez disso, algumas equipes se concentram no Goodput, uma métrica adaptada das redes para aplicativos LLM. Goodput mede o número de solicitações por segundo que satisfazem o objetivo do SLO (Software Level Objective).

Utilização, MFU e MBU

As métricas de utilização medem com que eficiência um recurso está sendo usado. Normalmente quantifica a proporção do recurso ativamente usado comparado à sua capacidade total disponível.

A MFU é a razão entre a taxa de transferência observada (tokens/s) em relação à taxa de transferência máxima teórica de um sistema operando no pico de flop/s. Se no pico do flop/s anunciado pelo fabricante de chips, o chip pode gerar 100 tokens/s, mas quando usado para o seu serviço de inferência, ele pode gerar apenas 20 tokens/s, seu MFU é de 20%.

O MBU (Model Bandwidth Utilization – Utilização de largura de banda modelo) mede a porcentagem de largura de banda de memória alcançável usada. Se a largura de banda de pico do chip for 1 TB/s, e sua inferência usa apenas 500 GB/s, seu MBU é 50%.

O que é considerado um bom MFU e MBU, depende do modelo, hardware e carga de trabalho. As cargas de trabalho de computação normalmente têm maior MFU e MBU inferior, enquanto as cargas de trabalho com largura de banda geralmente mostram MFU mais baixa e MBU mais alto.

Como o treinamento pode se beneficiar de uma otimização mais eficiente (por exemplo, melhor lote), graças a ter cargas de trabalho mais previsíveis, MFU para o treinamento é tipicamente maior que a MFU para inferência. Para inferência, como o pré-enchimento é voltado à computação e a decodificação é voltada à largura de banda da memória, o MFU durante o preenchimento, é tipicamente maior que a MFU durante a decodificação. Para o treinamento modelo, até o momento em que este artigo foi escrito, um MFU acima de 50% é geralmente considerado bom, mas pode ser difícil de obter em hardware específico.

As métricas de utilização são úteis para rastrear a eficiência do seu sistema. Taxas de utilização mais altas para cargas de trabalho semelhantes no mesmo hardware geralmente significa que seus serviços estão se tornando mais eficientes. No entanto, o objetivo não é obter os chips com a maior utilização. O que você realmente se importa é como realizar seu trabalho de forma mais rápida e barata. Uma taxa de utilização mais alta não significa nada se o custo e a latência aumentarem.

Aceleradores de IA

O que é um acelerador?

Um acelerador é um chip projetado para acelerar um tipo específico de carga de trabalho computacional. Um acelerador de IA foi projetado para cargas de trabalho de IA. O tipo de acelerador de IA dominante são as GPUs, e o maior direcionador econômico durante o boom da IA ​​no início dos anos 2020 é sem dúvida a Nvidia.

A principal diferença entre CPUs e GPUs é que as CPUs são projetadas para uso de uso geral, enquanto as GPUs são projetadas para processamento paralelo:

  • As CPUs possuem alguns núcleos poderosos, normalmente até 64 núcleos. Enquanto muitos núcleos de CPU podem lidar com cargas de trabalho multi-thread de maneira eficaz, elas se destacam em tarefas que exigem alto desempenho em single-thread.
  • As GPUs têm milhares de núcleos menores e menos poderosos otimizados para tarefas que podem ser divididas em cálculos muitos menores, independentes, como renderização gráfica e aprendizado de máquina. A operação que constitui a maioria das cargas de trabalho de ML é a multiplicação da matrizes, o que é altamente paralelizável.

Embora muitas chips possam lidar com o treinamento e a inferência, um grande tema emergente são chips especializados para inferência. Uma pesquisa de Desislavov et al. (2023) compartilha que a inferência pode exceder o custo do treinamento em sistemas comumente usados, e que a inferência é responsável por até 90% dos custos de aprendizado de máquina para sistemas de IA implantados.

Os chips projetados para inferência são frequentemente otimizados para menor precisão e acesso mais rápido à memória, em vez de grande capacidade de memória.

Diferentes arquiteturas de hardware têm diferentes layouts de memória e unidades de computação especializadas que evoluem ao longo do tempo. Essas unidades são otimizadas para tipos de dados específicos, como escalares, vetores ou tensores.

Um chip pode ter uma mistura de diferentes unidades de computação otimizadas para vários tipos de dados. Por exemplo, GPUs tradicionalmente suportaram as operações de vetor, mas muitas GPUs modernas agora incluem núcleos tensores otimizados para cálculos de matriz e tensor.

As especificações de um chip contêm muitos detalhes que podem ser úteis ao avaliá-lo para cada caso de uso específico. No entanto, as principais características que são importantes nos casos de uso são recursos computacionais, tamanho da memória e largura de banda, e consumo de energia.

Capacidades computacionais

Os recursos computacionais são normalmente medidos pelo número de operações que um chip pode executar em um determinado tempo. A métrica mais comum é FLOP/s, frequentemente escrito como FLOPS, que mede o número de pico de operações de ponto flutuante por segundo. Na realidade, no entanto, é muito improvável que um aplicativo pode atingir esse pico/s. A razão entre o FLOP/s e o FLOP/s teórico é uma métrica de utilização.

O número de operações que um chip pode executar em um segundo depende da precisão numérica – quanto maior a precisão, menos operações o chip pode executar.

Tamanho da memória e largura de banda

Como uma GPU tem muitos núcleos trabalhando em paralelo, os dados geralmente precisam ser movidos da memória para esses núcleos e, portanto, a velocidade de transferência de dados é importante. A transferência de dados é crucial ao trabalhar com modelos de IA que envolvem grandes matrizes de peso e dados de treinamento. Essas grandes quantidades de dados precisam ser movidas rapidamente para manter os núcleos ocupados com eficiência. Portanto, a memória da GPU precisa ter maior largura de banda e menor latência que a memória da CPU e, assim, a memória da GPU requer tecnologias de memória mais avançadas. Este é um dos fatores que tornam a memória da GPU mais cara que a memória da CPU.

Consumo de energia

Os chips confiam nos transistores para executar a computação. Quando um acelerador é usado com eficiência, bilhões de transistores trocam rapidamente os estados, consumindo uma quantidade substancial de energia e gerando uma quantidade não trivial de calor. Este calor requer sistemas de refrigeração, que também consomem eletricidade, adicionando ao consumo geral de energia dos data centers.

O consumo de energia dos chips ameaça ter um impacto impressionante no meio ambiente, aumentando a pressão sobre as empresas para investir em tecnologias para datacenters verdes. Uma placa NVIDIA H100, em seu auge, consome por um ano aproximadamente 7.000 kWh. Para comparação, o consumo médio anual de eletricidade da família dos EUA é de 10.000 kWh. É por isso que a eletricidade é um gargalo para aumentar a computação.

Otimização de inferência

Idealmente, otimizar um modelo para velocidade e custo não deveria alterar a qualidade do modelo. No entanto, muitas técnicas podem causar a degradação do modelo.

Otimização do modelo

A otimização no nível do modelo visa tornar o modelo mais eficiente, geralmente modificando o próprio modelo, o que pode alterar seu comportamento. No momento da escrita deste artigo, muitos modelos de fundação seguem a arquitetura transformer e incluem um componente de modelo de linguagem auto-regressiva. Esses modelos possuem três características que tornam os recursos de inferência intensivos: tamanho do modelo, decodificação auto-regressiva e mecanismo de atenção.

Modelo de compactação

A compactação do modelo envolve técnicas que reduzem o seu tamanho. Tornar um modelo menor também pode torná-lo mais rápido. Já apresentamos duas técnicas de compressão do modelo: quantização e destilação.

A quantização apenas de peso é de longe a abordagem mais popular, pois é fácil de usar, funciona facilmente para muitos modelos e é extremamente eficaz. Reduzir a precisão de um modelo de 32 bits para 16 bits reduz sua pegada de memória pela metade. No entanto, estamos perto do limite de quantização – não podemos ir abaixo de 1 bit por valor. A destilação também é comum porque pode resultar em um modelo menor cujo comportamento é comparativo ao de um modelo muito maior para atendimento a suas necessidades.

Superando o gargalo de decodificação auto-regressiva

Decodificação especulativa

A decodificação especulativa (também chamada de amostragem especulativa) usa um modelo mais rápido, mas menos poderoso para gerar uma sequência de tokens, que são então verificados pelo modelo de destino. O modelo de destino é o modelo que você deseja usar. O modelo mais rápido é chamado de modelo de rascunho ou proposta porque propõe a saída de rascunho.

As taxas de aceitação são dependentes do domínio. Para textos que seguem estruturas específicas como código, a taxa de aceitação é normalmente mais alta. O modelo de rascunho pode ser de qualquer arquitetura, embora, idealmente, deva compartilhar o mesmo vocabulário e tokenizer que o modelo de destino. Você pode treinar um modelo de rascunho personalizado ou usar um modelo mais fraco existente.

Inferência com referência

Freqüentemente, uma resposta precisa fazer referência a tokens da entrada. Por exemplo, se você fizer uma pergunta ao seu modelo sobre um documento anexado, o modelo pode repetir um pedaço de texto literalmente do documento. Outro exemplo é se você pedir ao modelo para corrigir bugs em um pedaço de código, o modelo pode reutilizar a maioria do código original com pequenas alterações. E se, em vez de fazer o modelo gerar esses tokens repetidos, copiássemos esses tokens da entrada para acelerar a geração? Esta é a idéia principal por trás da inferência com referência.

A inferência com referência é semelhante à decodificação especulativa, mas em vez de usar um modelo para gerar tokens de rascunho, ele seleciona tokens de rascunho da entrada. O principal desafio é desenvolver um algoritmo para identificar o texto mais relevante do contexto em cada etapa de decodificação.

Ao contrário da decodificação especulativa, a inferência com referência não requer um modelo extra. No entanto, é útil apenas em cenários de geração onde existe uma sobreposição significativa entre contextos e saídas, como em sistemas de recuperação, codificação ou conversas com várias turnos.

Decodificação paralela

Em vez de tornar a geração auto-regressiva mais rápida com os tokens de rascunho, algumas técnicas visam quebrar a dependência seqüencial.

Isso pode funcionar porque o conhecimento da sequência existente geralmente é suficiente para prever os próximos tokens. Por exemplo, dado “o gato se deita”, sem saber que o próximo token está “em cima”, “embaixo” ou “atrás ”, você ainda pode prever que a palavra depois que é “de”.

Os tokens paralelos podem ser gerados pelo mesmo decodificador, como na decodificação de LookaHead (Fu et al., 2024), ou por diferentes cabeças de decodificação, como em Medusa

Como esses tokens não são gerados sequencialmente, eles precisam ser verificados para garantir que eles se encaixem. Uma parte essencial da decodificação paralela é a verificação e integração.

Otimização do mecanismo de atenção

Muitas técnicas foram desenvolvidas para tornar o mecanismo de atenção mais eficiente. Em geral, eles caem em três baldes: redesenhar o mecanismo de atenção, otimizar o cache KV e escrever kernels para cálculo de atenção.

Redesenhar o mecanismo de atenção

Essas técnicas envolvem alterar como funciona o mecanismo de atenção. Embora essas técnicas ajudem a otimizar a inferência, porque elas alteram diretamente a arquitetura de um modelo, elas podem ser aplicados apenas durante o treinamento ou finetuning.

Por exemplo, ao gerar um novo token, em vez de atender a todos os tokens anteriores, a atenção local da janela atende apenas a um tamanho fixo de janela dos tokens mais próximos (Beltagy et al., 2020). Isso reduz o comprimento da sequência efetiva para uma janela de tamanho fixo, reduzindo o cache KV e o cálculo da atenção.

A atenção local da janela pode ser intercalada com a atenção global, com a atenção local capturando o contexto próximo; a atenção global captura informações específicas da tarefa em todo o documento.

Atenção de camadas cruzadas (Brandon et al., 2024) e a atenção multi-query (Shazeer, 2019) reduzem a pegada de memória do cache KV por reduzir o número de pares de valor-chave. Atenção de camada cruzada compartilha os vetores de chave e valor em camadas adjacentes. Tendo três camadas compartilhando os mesmos vetores de valor-chave significa reduzir o cache KV três vezes. Por outro lado, a atenção multi-query compartilha vetores de valores-chave entre as cabeças de consulta.

A atenção agrupada (Ainslie et al., 2023) é uma generalização da atenção multi-query. Em vez de usar apenas um conjunto de pares de valor-chave para todas as cabeças de consulta, sua atenção em quadros agrupados coloca cabeças de consulta em grupos menores e compartilha pares de valor-chave somente entre cabeças de consulta no mesmo grupo. Isso permite um equilíbrio mais flexível entre o número de cabeças de consulta e o número de pares de valor-chave.

Otimizando o tamanho do cache do KV

Uma das estruturas de inferência que mais cresce, Vllm, ganhou popularidade por introduzir a PagedAttention, que otimiza o gerenciamento de memória dividindo o cache do KV em blocos não contíguos, reduzindo a fragmentação e permitindo um compartilhamento flexível de memória para melhorar a eficiência de servir da LLM.

Outras técnicas incluem quantização do cache KV (Hooper et al., 2024; Kang et al., 2024), compressão adaptativa do cache KV (Ge et al., 2023), e cache KV seletivo (Liu et al., 2024).

Escrevendo kernels para cálculo de atenção

Em vez de alterar o design do mecanismo ou otimizar o armazenamento, essa abordagem analisa como as pontuações de atenção são calculadas e encontra
maneiras de fazer isso de forma mais eficiente. Essa abordagem é a mais eficaz quando leva em consideração o hardware que executa o computação. O código otimizado para um chip específico é chamado de kernel.

Kernels e compiladores

Os kernels são peças especializadas de código otimizadas para aceleradores de hardware específicos, como GPUs ou TPUs. Eles geralmente são escritos para executar rotinas computacionalmente intensivas que precisam ser executadas repetidamente, geralmente em paralelo, para maximizar o desempenho desses aceleradores.

Operações comuns de IA, incluindo multiplicação de matriz, computação de atenção e operação de convolução, possuem kernels especializados para tornar seu cálculo mais eficiente em hardwares diferentes.

Escrever kernels requer uma compreensão profunda da arquitetura do hardware subjacente. Isso inclui conhecimento sobre como a hierarquia de memória é estruturada (como caches, memória global, memória compartilhada e registros) e como os dados são acessados ​​e movidos entre estes níveis diferentes.

Além disso, os kernels são tipicamente escritos em linguagens de programação de nível inferior como CUDA (para NVIDIA GPUS), Triton (uma linguagem desenvolvida pela OpenAI para escrever kernels personalizados) e ROCM (para GPUs AMD).

Devido a esta barreira de entrada, escrever kernels costumavam ser uma arte escura praticada somente por alguns.

No entanto, com a crescente demanda por otimização de inferência e a onipresença dos aceleradores, mais engenheiros de IA se interessaram por escrever kernels. Existem muitos ótimos tutoriais on-line para a redação do kernel. Aqui, abordaremos quatro técnicas comuns frequentemente usadas para acelerar a computação:

  • Vetorização: Dado um loop ou um loop aninhado, em vez de processar um elemento de dados por vez, execute simultaneamente vários elementos de dadps que são contíguos na memória.
  • Paralelização: Divida uma matriz de entrada (ou matriz n-dimensional) em pedaços independentes que podem ser processados ​​simultaneamente em diferentes núcleos ou threads, acelerando o cálculo.
  • Loop tiling: otimize a ordem de acesso a dados em um loop para o layout e cache de memória do hardware. Esta otimização é hardware-dependente. Um padrão de tiling de CPU eficiente pode não funcionar bem nas GPUs.
  • Fusão de operadores: combine vários operadores em uma única passagem para evitar acesso redundante à memória. Por exemplo, se dois loops operam na mesma matriz, eles podem ser fundidos em um, reduzindo o número de vezes que os dados são lidos e gravados.

Os kernels são otimizados para uma arquitetura de hardware. Isso significa que sempre que uma nova arquitetura de hardware é introduzida, novos kernels precisam ser desenvolvidos.

Otimização do serviço de inferência

A maioria das técnicas de otimização em nível de serviço se concentra no gerenciamento de recursos. Dada uma quantidade fixa de recursos (computação e memória) e cargas de trabalho dinâmicas (solicitações de inferência de usuários que podem envolver diferentes modelos), o objetivo é alocar recursos com eficiência a estas cargas de trabalho para otimizar para latência e custo.

Lote

Uma das maneiras mais fáceis de reduzir seu custo é o lote. Na produção, seu serviço de inferência pode receber várias solicitações simultaneamente. Em vez de processar cada solicitação separadamente, agrupar os pedidos que chegam ao mesmo tempo pode reduzir significativamente a taxa de transferência do serviço.

As três técnicas principais para lotes são: lotes estáticos, lotes dinâmicos e lote contínuo.

A técnica de lote mais simples é o lote estático. O serviço agrupa um número fixo de entradas em um lote. É como um ônibus que espera até que cada assento seja preenchido antes de partir. A desvantagem do lote estático é que todas as solicitações precisam esperar até que o lote esteja cheio a ser executado.

O lote dinâmico, por outro lado, define uma janela de tempo máximo para cada lote. Se o tamanho do lote for quatro e a janela for 100 ms, o servidor processa o lote quando possui quatro solicitações ou quando 100 ms forem passados, o que acontecer primeiro. É como um ônibus que sai um cronograma fixo ou quando está cheio. Essa abordagem mantém a latência sob controle; portanto, as solicitações anteriores não são mantidas pelos posteriores.

Em implementações simples de lotes, todas as solicitações de lote devem ser concluídas antes que suas respostas sejam devolvidas. Para LLMs, alguns pedidos podem levar muito mais tempo do que outros. Se uma solicitação em um lote gerar apenas 10 tokens de resposta e outra solicitação gerar 1.000 tokens de resposta, a resposta curta deve esperar até que a resposta longa seja concluída antes de ser devolvida ao usuário. Isso resulta em latência desnecessária para solicitações curtas.

O lote contínuo permite que as respostas em um lote sejam devolvidas aos usuários assim que forem concluídos. Depois que um pedido em um lote é concluído e sua resposta retornada, o serviço pode adicionar outra solicitação ao lote em seu lugar, tornando o lote contínuo. É como um ônibus que, depois de deixar um passageiro, pode pegar imediatamente outro passageiro para maximizar sua taxa de ocupação.

Dissociar preenchimento e decodificação

A inferência de LLM consiste em duas etapas: pré-preenchimento e decodificação. Porque o pré-preenchimento é voltado à computação e a decodificação à largura de banda da memória ligada, usar a mesma máquina para executar ambos pode fazer com que eles compitam ineficientemente por recursos e diminuam significativamente o TTFT e o TPOT.

Uma técnica de otimização comum para servidores de inferência é desagregar o pré-preenchimento e a decodificação. Atribuir operações de pré-preencher e decodificar a diferentes instâncias (por exemplo, GPUs diferentes) podem melhorar significativamente o volume de solicitações processadas enquanto aderem aos requisitos de latência.

Cache de prompt

Muitos prompts em um aplicações têm segmentos de texto sobrepostos. Um cache de prompt armazena esses segmentos sobrepostos para reutilização, então você só precisa processá-los uma vez. Um segmento de texto sobreposto comum em diferentes avisos é o prompt do sistema. Sem um cache imediato, seu modelo precisa processar o prompt do sistema a cada consulta. Com um cache de prompt, o prompt do sistema precisa ser processado apenas uma vez para a primeira consulta.

Para aplicações com instruções longas do sistema, o cache de prompt pode reduzir significativamente a latência e o custo. No entanto, isso não é totalmente gratuito. Como o cache do KV, o tamanho do cache de prompt pode ser bastante grande e ocupar espaço de memória. A menos que você use uma API de modelo com esta funcionalidade, a implementação do cache de prompt pode exigir um esforço significativo de engenharia.

Paralelismo

Os aceleradores são projetados para processamento paralelo e as estratégias de paralelismo são a espinha dorsal da computação de alto desempenho. Duas famílias de estratégias de paralelização que podem ser aplicadas em todos os modelos são paralelismo de dados e paralelismo do modelo. Uma família de estratégias aplicadas especificamente para o LLMS é o paralelismo de contexto e sequência. Uma técnica de otimização pode envolver várias estratégias de paralelismo.

O paralelismo da réplica é a estratégia mais direta para implementar. Ele simplesmente cria várias réplicas do modelo que você deseja servir. Mais réplicas permitem lidar com mais solicitações ao mesmo tempo, potencialmente ao custo do uso de mais chips.

Muitas vezes, seu modelo é tão grande que não pode se encaixar em uma máquina. O paralelismo do modelo refere-se à prática de dividir o mesmo modelo em várias máquinas.

Existem várias maneiras de dividir um modelo. A abordagem mais comum para a inferência é o paralelismo de tensor, também conhecido como paralelismo intra-operador. A inferência envolve uma sequência de operadores em tensores multidimensionais, como a multiplicação da matriz. Nesta abordagem, tensores envolvidos em um operador são particionados em vários dispositivos, quebrando efetivamente esse operador em pedaços menores para serem executados em paralelo, acelerando assim o cálculo.

Outra maneira de dividir um modelo é o paralelismo do pipeline, que envolve dividir a computação de um modelo em estágios distintos e atribuir cada estágio para um dispositivo diferente.

Embora o paralelismo do pipeline permita servir modelos grandes em várias máquinas, ele aumenta a latência total para cada solicitação devido à comunicação extra entre os estágios do pipeline. Portanto, para aplicações com requisitos de latência estritos, o paralelismo do pipeline é normalmente evitado em
favor do paralelismo da réplica. No entanto, o paralelismo do pipeline é comumente usado no treinamento, pois pode ajudar a aumentar a taxa de transferência.

Referências

Huyen, Chip. AI Engineering: Building Applications with Foundation Models

Comments

Comments (0)

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Previous
Next
Back To Top