Connect with us

SGLang: Execução Eficiente de Programas de Modelos de Linguagem Estruturados

Inteligência artificial

SGLang: Execução Eficiente de Programas de Modelos de Linguagem Estruturados

mm
SGLang: Efficient Execution of Structured Language Model Programs

Os grandes modelos de linguagem (LLMs) estão sendo cada vez mais utilizados para tarefas complexas que exigem várias chamadas de geração, técnicas de prompt avançadas, controle de fluxo e entradas/saídas estruturadas. No entanto, os sistemas eficientes para programar e executar essas aplicações são escassos. O SGLang, um sistema recém-introduzido, visa resolver isso fornecendo execução eficiente de programas de modelos de linguagem complexos. O SGLang compreende uma linguagem de frontend e um tempo de execução. O frontend simplifica a programação com primitivas para geração e controle de paralelismo, enquanto o tempo de execução acelera a execução por meio de otimizações novas, como RadixAttention para reutilização do cache KV e máquinas de estado finito comprimidas para decodificação de saída estruturada mais rápida. Experimentos demonstram que o SGLang alcança até 6,4× mais taxa de transferência em comparação com os sistemas de inferência de estado da arte em vários grandes modelos de linguagem e multimodais, abordando tarefas como controle de agente, raciocínio lógico, benchmarks de aprendizado de poucos disparos, decodificação JSON, pipelines de geração aumentada por recuperação e chat de várias voltas.

Os recentes avanços nas capacidades dos LLMs expandiram sua utilidade, permitindo que eles lidem com uma gama mais ampla de tarefas gerais e funcionem como agentes autônomos. Nesses aplicativos, os LLMs participam do planejamento de multirodadas, raciocínio e interação com ambientes externos. Isso é facilitado por meio do uso de ferramentas, várias modalidades de entrada e várias técnicas de prompt, como aprendizado de poucos disparos, autoconsistência, esqueleto de pensamento e árvore de pensamento. Esses novos casos de uso exigem várias chamadas de geração de LLMs, muitas vezes dependentes, indicando uma tendência de usar estruturas de multi-chamadas para concluir tarefas complexas.

Essa mudança marca uma transição do bate-papo simples para um uso programático mais sofisticado dos LLMs, onde os programas agendam e controlam os processos de geração dos LLMs. Esses programas são referidos como “Programas de Modelo de Linguagem” (LM Programs). Técnicas de prompt avançadas e fluxos de trabalho de agente estão dentro do escopo dos programas LM. Existem duas propriedades comuns dos programas LM: (1) Os programas LM geralmente envolvem várias chamadas de LLMs interspersadas com controle de fluxo para concluir tarefas complexas e melhorar a qualidade geral. (2) Os programas LM recebem entradas estruturadas e produzem saídas estruturadas, permitindo a composição de programas LM e integração em sistemas de software existentes.

Neste artigo, vamos mergulhar mais fundo no framework SGLang, explorando sua arquitetura, analisando seu desempenho e comparando-o com frameworks de estado da arte. Então, vamos começar.

Uma Introdução ao SGLang

Apesar do uso generalizado de programas LM, os sistemas atuais para expressar e executá-los permanecem ineficientes. O SGLang identifica dois desafios principais associados ao uso eficiente de programas LM:

  • Complexidade de programação: Desenvolver programas LM é tedioso e difícil devido à natureza não determinística dos LLMs. Isso envolve manipulação extensiva de strings, ajuste experimental de prompts, análise de saída frágil, manipulação de várias modalidades de entrada e implementação de mecanismos de paralelismo. Essa complexidade reduz significativamente a legibilidade de programas simples.
  • Ineficiência de execução: Executar programas LM é ineficiente devido ao cálculo redundante e uso de memória. Os motores de inferência de estado da arte, otimizados para reduzir a latência e melhorar a taxa de transferência, carecem de conhecimento direto da carga de trabalho, resultando em ineficiências significativas. Um exemplo notável é a reutilização do cache KV, que consiste em tensores intermediários reutilizáveis essenciais para a inferência gerativa. Os sistemas atuais carecem de mecanismos eficazes para facilitar a reutilização do cache KV em várias chamadas de LLM que compartilham um prefixo comum, levando a cálculos desnecessários e memória desperdiçada. Além disso, a decodificação de saída estruturada, como o modo JSON, é subótima, pois os sistemas existentes apenas decodificam um token de cada vez.

Para resolver esses desafios, o SGLang introduz uma Linguagem de Geração Estruturada para LLMs. A ideia central é explorar sistematicamente a estrutura de multi-chamadas nos programas LM para execução eficiente. Como mostrado na figura a seguir, o SGLang tem duas partes: uma linguagem de frontend e um tempo de execução.

O frontend simplifica a programação de programas LM, e o tempo de execução acelera a execução. Essas partes podem trabalhar juntas para melhor desempenho ou funcionar independentemente.

O SGLang é uma linguagem específica de domínio incorporada em Python, fornecendo primitivas para geração (por exemplo, estender, gerar, selecionar) e controle de paralelismo (por exemplo, bifurcar, unir). É compatível com o controle de fluxo e bibliotecas do Python, permitindo que os usuários desenvolvam fluxos de trabalho de prompt avançados com facilidade, usando a sintaxe nativa do Python. O SGLang inclui um interpretador e um compilador. O interpretador gerencia o estado do prompt como um fluxo e envia operações primitivas para o fluxo para execução assíncrona, garantindo controle adequado sobre a sincronização e o paralelismo intra-programa. Além disso, os programas SGLang podem ser rastreados e compilados para otimizações adicionais.

O tempo de execução do SGLang propõe várias otimizações novas para acelerar a execução de programas LM:

  • RadixAttention: Essa técnica permite a reutilização automática do cache KV em várias chamadas de geração. Nos motores de inferência existentes, o cache KV de um pedido é descartado após o processamento, impedindo a reutilização em várias chamadas e retardando a execução. O SGLang mantém um cache LRU do cache KV dentro de uma árvore radix, gerenciando o cache KV como um cache tradicional e usando a árvore radix para correspondência, inserção e expulsão eficientes. Isso permite que o tempo de execução lidar com vários padrões de reutilização de forma eficiente.
  • Máquina de Estado Finito Comprimida: Essa técnica permite a decodificação de saída estruturada mais rápida. Os sistemas existentes seguem as restrições apenas para o próximo token, podendo decodificar um token de cada vez. Em vez disso, o SGLang analisa as restrições e constrói uma máquina de estado finito comprimida para representá-las, comprimindo um caminho de vários tokens em um caminho de um passo sempre que possível, permitindo a decodificação de vários tokens de uma vez para uma velocidade mais rápida.
  • Execução Especulativa de API: Para modelos de API, como o OpenAI’s GPT-4, o SGLang introduz a execução especulativa de API para otimizar programas de multi-chamadas.

Usando o SGLang, várias aplicações de LLMs foram implementadas, incluindo controle de agente, raciocínio lógico, benchmarks de aprendizado de poucos disparos, decodificação JSON, pipelines de geração aumentada por recuperação, chat de várias voltas e processamento de multi-modalidade. O desempenho foi testado em modelos, incluindo Llama-7B/70B, Mistral-8x7B, LLaVA-v1.5-7B (imagem) e LLaVA-NeXT-34B (vídeo) em GPUs NVIDIA A10G e A100. Os resultados experimentais mostram que o SGLang alcança até 6,4× mais taxa de transferência em uma ampla gama de cargas de trabalho, modelos e configurações de hardware, em comparação com os sistemas de programação e inferência existentes, incluindo Guidance, vLLM e LMQL.

SGLang: Modelo de Programação e Metodologia

O modelo de programação SGLang é introduzido por meio de um exemplo em execução, descrevendo suas primitivas de linguagem e modos de execução, e esboçando oportunidades de otimização de tempo de execução. Esse modelo simplifica operações tediosas em fluxos de trabalho de multi-chamadas (por exemplo, manipulação de strings, chamadas de API, especificação de restrições, paralelismo) fornecendo primitivas flexíveis e compostas. O SGLang é uma linguagem específica de domínio incorporada em Python.

A função multi_dimensional_judge recebe três argumentos: `s`, `path` e `essay`. s gerencia o estado do prompt, path é o caminho do arquivo de imagem e essay é o texto do ensaio. Novas strings e primitivas SGLang podem ser anexadas ao estado s para execução usando o operador +=. Primeiro, a função adiciona a imagem e o ensaio ao prompt. Em seguida, verifica se o ensaio está relacionado à imagem usando select, armazenando o resultado em s[“related”]. Se relacionado, o prompt é bifurcado em três cópias para avaliação paralela de diferentes dimensões, usando gen para armazenar os resultados em f[“judgment”]. Em seguida, mescla os julgamentos, gera um resumo e atribui uma nota de letra. Finalmente, retorna os resultados no formato JSON, seguindo um esquema definido por uma restrição de expressão regular regex. O SGLang simplifica muito esse programa, pois um programa equivalente usando uma interface de API do OpenAI precisaria de 2,1× mais linhas de código devido à manipulação manual de strings e controle de paralelismo.

O SGLang fornece primitivas para controle de estado de prompt, geração e paralelismo, que podem ser usadas com sintaxe e bibliotecas do Python. Aqui estão as primitivas:

gen: Chama um modelo para gerar e armazena os resultados em uma variável com o nome especificado em seu primeiro argumento. Ele suporta um argumento `regex` para restringir a saída a seguir uma gramática definida por uma expressão regular (por exemplo, um esquema JSON).

  • select: Chama um modelo para escolher a opção de maior probabilidade de uma lista.
  • += ou estender: Anexa uma string ao prompt.
  • [nome_da_variável]: Recupera os resultados de uma geração.
  • bifurcar: Cria bifurcações paralelas do estado do prompt.
  • unir: Reune o estado do prompt.
  • imagem e vídeo: Recebem entradas de imagem e vídeo.

A forma mais simples de executar um programa SGLang é por meio de um interpretador, onde um prompt é tratado como um fluxo assíncrono. Primitivas como estender, gen e select são enviadas para o fluxo para execução assíncrona. Essas chamadas não bloqueantes permitem que o código Python continue executando sem esperar a geração ser concluída, semelhante ao lançamento de kernels CUDA de forma assíncrona. Cada prompt é gerenciado por um executor de fluxo em uma thread em segundo plano, permitindo paralelismo intra-programa. A recuperação dos resultados da geração bloqueará até que eles estejam prontos, garantindo sincronização correta. Alternativamente, os programas SGLang podem ser compilados como gráficos computacionais e executados com um executor de gráfico, permitindo mais otimizações.

Os sistemas de programação para LLMs podem ser classificados como de alto nível (por exemplo, LangChain, DSPy) e de baixo nível (por exemplo, LMQL, Guidance, SGLang). Os sistemas de alto nível fornecem prompts pré-definidos ou gerados automaticamente, como o otimizador de prompts do DSPy. Os sistemas de baixo nível geralmente não alteram os prompts, mas permitem a manipulação direta de prompts e primitivas. O SGLang é um sistema de baixo nível semelhante ao LMQL e Guidance.

O SGLang se concentra mais na eficiência do tempo de execução e vem com seu próprio tempo de execução co-projetado, permitindo otimizações novas. As linguagens de alto nível (por exemplo, DSPy) podem ser compiladas para linguagens de baixo nível (por exemplo, SGLang). A integração do SGLang como um backend no DSPy para melhor eficiência de tempo de execução é demonstrada posteriormente.

O exemplo acima ilustra as operações RadixAttention com uma política de expulsão LRU em nove pontos de tempo, mostrando a evolução dinâmica da árvore radix em resposta a vários pedidos. Esses pedidos incluem duas sessões de chat, um lote de perguntas de aprendizado de poucos disparos e amostragem de autoconsistência. Cada aresta da árvore carrega um rótulo denotando uma substring ou uma sequência de tokens. Os nós são coloridos para refletir diferentes estados: verde para nós novos, azul para nós em cache acessados durante o ponto de tempo e vermelho para nós que foram expulsos.

Etapa 1: A árvore radix está inicialmente vazia.

Etapa 2: O servidor processa uma mensagem de usuário de entrada “Olá” e responde com a saída do LLM “Oi”. O prompt do sistema “Você é um assistente útil”, a mensagem do usuário “Olá!” e a resposta do LLM “Oi!” são consolidados na árvore como uma aresta única ligada a um novo nó.

Etapa 3: Um novo prompt chega, e o servidor encontra o prefixo do prompt (ou seja, a primeira rodada da conversa) na árvore radix e reutiliza seu cache KV. A nova rodada é anexada à árvore como um novo nó.

Etapa 4: Uma nova sessão de chat começa. O nó da Etapa 3 é dividido em dois nós para permitir que as duas sessões de chat compartilhem o prompt do sistema.

Etapa 5: A segunda sessão de chat continua. No entanto, devido aos limites de memória, um nó da Etapa 4 deve ser expulsado. A nova rodada é anexada após o nó restante da Etapa 4.

Etapa 6: O servidor recebe uma pergunta de aprendizado de poucos disparos, processa-a e a insere na árvore. O nó raiz é dividido porque a nova pergunta não compartilha nenhum prefixo com os nós existentes.

Etapa 7: O servidor recebe um lote de perguntas de aprendizado de poucos disparos adicionais. Essas perguntas compartilham o mesmo conjunto de exemplos de poucos disparos, então um nó da Etapa 6 é dividido para permitir o compartilhamento.

Etapa 8: O servidor recebe uma nova mensagem da primeira sessão de chat. Ele expulsa todos os nós da segunda sessão de chat, pois são os menos recentemente usados.

Etapa 9: O servidor recebe um pedido para amostrar mais respostas para as perguntas em um nó da Etapa 8, provavelmente para prompt de autoconsistência. Para fazer espaço para esses pedidos, vários nós são expulsos.

Esse exemplo demonstra como o RadixAttention lida com a alocação dinâmica e expulsão de nós em resposta a diferentes tipos de pedidos, garantindo reutilização eficiente do cache KV e gerenciamento de memória.

SGLang: Avaliação e Resultados

Resultados em Modelos de Peso Aberto

Os resultados de latência e taxa de transferência são mostrados nas figuras a seguir. O SGLang melhora a taxa de transferência em até 6,4× e reduz a latência em até 3,7×. Essas melhorias resultam da reutilização do cache KV, da exploração do paralelismo dentro de um único programa e da decodificação de saída estruturada mais rápida.

Nessas benchmarks, a taxa de acertos do cache varia de 50% a 99%. A Figura 13 (Apêndice) lista as taxas de acertos de cache alcançadas e ótimas para todos eles, mostrando que o agendamento consciente do cache do SGLang se aproxima de 96% da taxa de acertos ótimos em média.

Resultados em Modelos Maiores com Paralelismo de Tensor

Modelos maiores, Mixtral-8x7B e Llama-70B, foram testados com paralelismo de tensor nas mesmas benchmarks, e os resultados são relatados na figura a seguir. O aumento de velocidade nos modelos maiores mostra uma tendência semelhante à observada nos modelos menores, indicando que as otimizações do SGLang se generalizam bem para modelos maiores. O Guidance e o LMQL foram omitidos devido à falta de implementações eficientes de paralelismo de tensor.

Resultados em Modelos Multimodais

O SGLang tem suporte nativo para modelos multimodais com as primitivas de imagem e vídeo. As otimizações neste artigo são compatíveis com modelos multimodais. Para o RadixAttention, o hash da entrada de imagem é computado e usado como a chave na árvore radix, permitindo a reutilização do cache KV dos tokens de imagem da mesma imagem. O LLaVA-v1.5-7B (imagem) foi executado em llava-bench-in-the-wild, e o LLaVA-NeXT-34B (vídeo) foi executado em ActivityNet. Como esses modelos não são bem suportados por outros sistemas de baseline, a implementação original do modelo em Hugging Face Transformers foi usada como baseline. Como mostrado na tabela a seguir, o SGLang fornece uma taxa de transferência de até 6× maior nesses benchmarks. Em llava-bench-in-the-wild, várias perguntas sobre a mesma imagem foram tratadas, e o tempo de execução do SGLang reutilizou o cache KV nesse caso.

Implantação em Produção

O SGLang foi implantado na Chatbot Arena para servir modelos de peso aberto. Devido ao tráfego baixo para alguns modelos, apenas um trabalhador SGLang serve cada um. Após um mês, uma taxa de acertos do RadixAttention de 52,4% para o LLaVA-Next-34B e 74,1% para o Vicuna-33B foi observada. Os acertos do cache vieram de mensagens de sistema comuns, imagens de exemplo frequentemente reutilizadas e históricos de chat de várias voltas. Isso reduziu a latência do primeiro token em média 1,7× para o Vicuna-33B.

Pensamentos Finais

Neste artigo, falamos sobre o SGLang, um sistema recém-introduzido que visa resolver a execução eficiente de programas de modelos de linguagem complexos. O SGLang compreende uma linguagem de frontend e um tempo de execução. O frontend simplifica a programação com primitivas para geração e controle de paralelismo, enquanto o tempo de execução acelera a execução por meio de otimizações novas, como RadixAttention para reutilização do cache KV e máquinas de estado finito comprimidas para decodificação de saída estruturada mais rápida. Experimentos demonstram que o SGLang alcança até 6,4× mais taxa de transferência em comparação com os sistemas de inferência de estado da arte em vários grandes modelos de linguagem e multimodais, abordando tarefas como controle de agente, raciocínio lógico, benchmarks de aprendizado de poucos disparos, decodificação JSON, pipelines de geração aumentada por recuperação e chat de várias voltas.

Um engenheiro por profissão, um escritor por coração. Kunal é um escritor técnico com um amor e compreensão profundos de AI e ML, dedicado a simplificar conceitos complexos nestes campos por meio de sua documentação envolvente e informativa.