Guia de Engenharia de LLM: 45 conceitos para sistemas de produção

Tradução automática

Este artigo foi traduzido automaticamente a partir da versão original em inglês.

Esta é uma referência para engenheiros que já conhecem os conceitos básicos de ML e de sistemas e precisam de ligar um sintoma em produção à parte relevante da stack. Cobre 45 conceitos de hardware, inferência, treino, deployment, aplicações e operações. Cada entrada apresenta o mecanismo, o que ele muda num sistema em execução e a condição em que o resultado citado se verifica. Se partir de um sintoma, como um primeiro token lento ou OOM sob carga, use a tabela de sintomas abaixo para encontrar as entradas a ler.

Para uma pergunta específica, consulte as respostas rápidas sobre engenharia de LLM. Cada tema tem uma resposta própria e liga à secção correspondente deste guia.

ParteTópicosSecções
I — Fundamentos do hardwareModelo roofline, memória da GPU, glossário de hardware1–3
II — Fundamentos da inferênciaLatência, throughput, KV cache, attention, quantização4–9
III — Otimizações de inferênciaKernels CUDA, FlashAttention, batching, PagedAttention, descodificação especulativa10–17
IV — Arquitetura do modeloComponentes internos do Transformer, decoder-only, MoE, tokenização, janelas de contexto18–22
V — Treino e alinhamentoPretraining, LoRA, precisão mista, ZeRO, leis de escala, RLHF/DPO/GRPO, distillation23–32
VI — Escalonamento e deploymentParalelismo, frameworks de serving, seleção de GPUs, routing33–36
VII — AplicaçõesEmbeddings, RAG, agentes, prompt engineering37–40
VIII — Operações em produçãoRate limiting, modos de falha, monitorização, custo, planeamento de capacidade41–45

Comece por um sintoma

Cada linha indica as entradas a ler primeiro e o que medir antes de alterar a stack.

Sintoma ou decisãoComece porMeça antes de alterar a stack
O primeiro token é lentoPrefill e decode, TTFTTempo em fila, comprimento do prompt, tempo de prefill e TTFT P99
Os tokens chegam devagarRoofline, TPOTTPOT por concorrência, largura de banda de memória e forma do batch
Contextos longos causam preempção ou OOMKV cache, PagedAttentionUso de KV, comprimentos dos pedidos, desperdício do alocador e número de preempções
Um modelo não cabe no orçamentoQuantização, Seleção de GPUsQualidade, throughput dos kernels, reserva de memória e goodput no SLO alvo
Escolher um runtime de servingFrameworks de serving, ParalelismoThroughput e latência P99 no SLO alvo, com os seus próprios prompts
Os pedidos expiram sob cargaModos de falha, Rate limitingProfundidade da fila, preempções, taxa de retries e comprimentos das respostas
Um treino não cabeLoRA e QLoRA, ZeRO, FSDPMemória do estado do modelo, comunicação, throughput e qualidade num conjunto de validação
O custo está a subirRouting, otimização de custosElegibilidade do tráfego, erros de qualidade, taxa de acertos da cache e dados da fatura

Guias mais aprofundados por decisão

Se está a decidir…Comece porDepois leia
Como servir muitos adaptadores fine-tunedFundamentos da inferência e deploymentGuia de serving com LoRAX
Se deve quantizarQuantizaçãoGuia de quantização de modelos
Como dividir um modelo por várias GPUsParalelismoEscalar modelos de linguagem grandes
Se deve fazer fine-tuningTreino e alinhamentoGuia de fine-tuning de LLM
Como a recuperação se encaixa numa aplicaçãoEmbeddings e RAGMétricas de avaliação de RAG
Como funcionam os sistemas de agentesAgentes e prompt engineeringReasoning loops de agentes AI
Como ordenar resultados de pesquisaEmbeddings e rerankingStack de ranking de pesquisa

Parte I — Fundamentos do hardware

A intensidade aritmética, a hierarquia de memória da GPU e os termos de hardware desta parte explicam muitas das escolhas feitas mais adiante no guia.

1. Memory-bound vs compute-bound e o modelo roofline

O ponto de partida para o desempenho de um LLM é a intensidade aritmética: por cada byte de dados que a GPU carrega da memória, quantos cálculos úteis realiza? Essa razão decide se uma operação é compute-bound (à espera do processador) ou memory-bound (à espera de que os dados sejam carregados).

Cada GPU tem uma intensidade “crítica”, o limiar em que o pico de computação iguala a largura de banda de memória. Para uma NVIDIA H100 SXM com Tensor Cores BF16 ou FP16 densos (989 TFLOPS; a especificação de 1979 TFLOPS pressupõe sparsity estruturada):

989 TFLOPS3.35 TB/s≈295 FLOPs/byte\frac{989 \text{ TFLOPS}}{3.35 \text{ TB/s}} \approx 295 \text{ FLOPs/byte}

O gráfico roofline abaixo representa a computação atingível em função da intensidade aritmética. A linha inclinada é a região memory-bound, onde a largura de banda define o limite; a linha horizontal é o throughput máximo de computação; o ponto de encontro das duas é o limiar de 295 FLOPs/byte.

Gráfico roofline de uma H100 SXM: o teto sobe ao longo de uma reta de largura de banda até um ponto de viragem perto de 295 FLOPs por byte e depois fica plano em 989 TFLOPS. O decode com batch de um fica perto de 1 FLOP por byte e de cerca de 3,35 TFLOPS na parte inclinada; uma seta tracejada mostra o batching a subi-lo ao longo da reta. O prefill longo ou em batch fica na parte plana.Gráfico roofline de uma H100 SXM: o teto sobe ao longo de uma reta de largura de banda até um ponto de viragem perto de 295 FLOPs por byte e depois fica plano em 989 TFLOPS. O decode com batch de um fica perto de 1 FLOP por byte e de cerca de 3,35 TFLOPS na parte inclinada; uma seta tracejada mostra o batching a subi-lo ao longo da reta. O prefill longo ou em batch fica na parte plana.

O decode com batch de um e o prefill longo ou suficientemente agrupado em batches ficam, geralmente, em lados opostos deste limiar:

  • O decode é memory-bound. Gerar tokens um a um obriga a carregar da memória a matriz de pesos, com vários gigabytes, para a multiplicar por um único token novo. Numa análise simplificada de um modelo denso de 16 bits com batch de um, a operação tem cerca de 1 FLOP/byte, aproximadamente 295x abaixo do limiar roofline da H100. Esta diferença explica por que razão o decode não consegue aproximar-se do pico de computação neste regime.
  • O prefill longo ou suficientemente agrupado em batches é muitas vezes compute-bound. Processar muitos tokens do prompt reutiliza os pesos em grandes multiplicações de matrizes, o que pode elevar a intensidade aritmética acima do limiar roofline. Prompts curtos e batches pequenos podem, em vez disso, ficar limitados pelo tráfego de memória ou pelo overhead de lançamento de kernels.

Assim, para acelerar o decode, trabalhe a largura de banda de memória: reduza os pesos com quantização, reduza o overhead de memória do KV com GQA e PagedAttention, e aumente a intensidade com batching. Para prefills longos ou bem agrupados em batches, uma computação matricial mais rápida e computação de menor precisão podem ajudar; analise separadamente, com profiling, as cargas de prefill curto.

2. Hierarquia de memória da GPU

Uma GPU tem quatro camadas de memória com capacidades e velocidades de acesso diferentes. Os registos são pequenos e rápidos; a HBM guarda muito mais dados, mas o acesso é mais demorado. Mover dados entre estas camadas é uma restrição importante de desempenho. A figura mostra uma H100 SXM: registos e memória partilhada dentro de cada SM, a L2 no die e 80 GB de HBM3 em pilhas ao lado.

Esboço de uma H100: o die da GPU tem 132 SMs, cada um com registos por thread e até 228 KB de memória partilhada, e uma cache L2 de 50 MB partilhada por todos os SMs. Pilhas de DRAM HBM3 ao lado do die guardam 80 GB, incluindo os pesos e a KV cache, e ligam-se a ele a cerca de 3,35 TB/s no total.Esboço de uma H100: o die da GPU tem 132 SMs, cada um com registos por thread e até 228 KB de memória partilhada, e uma cache L2 de 50 MB partilhada por todos os SMs. Pilhas de DRAM HBM3 ao lado do die guardam 80 GB, incluindo os pesos e a KV cache, e ligam-se a ele a cerca de 3,35 TB/s no total.

Do mais rápido para o mais lento numa H100 SXM:

  1. Os registos são a memória mais rápida e estão ligados diretamente às threads de processamento. No WGMMA da Hopper, a matriz A pode vir dos registos ou da memória partilhada, enquanto a matriz B vem da memória partilhada.
  2. A SRAM (memória partilhada) é uma memória de trabalho rápida, no chip, local a cada SM.
  3. A cache L2 é uma camada partilhada de 50 MB. Pode fornecer dados reutilizados entre SMs sem nova leitura da HBM.
  4. A HBM3 é a memória principal de 80 GB que guarda os pesos do modelo e a KV cache, com ~3,35 TB/s de largura de banda.

A diferença entre os níveis no chip e fora do chip é grande. Numa A100, o artigo do FlashAttention estima a largura de banda da SRAM no chip em cerca de 19 TB/s, contra 1,5–2,0 TB/s da HBM (Dao et al., 2022), ou seja, uma diferença de aproximadamente 10x. O FlashAttention e a fusão de kernels reduzem o tráfego para a HBM ao reter ou combinar trabalho intermédio no chip. O PagedAttention ataca outro problema: mapeia blocos lógicos da KV cache para blocos físicos não contíguos da memória da GPU, o que reduz a fragmentação e permite a partilha de blocos.

3. Glossário do hardware de GPU

Os termos abaixo aparecem ao longo do resto do guia.

HBM (High Bandwidth Memory) empilha dies de DRAM ligados por through-silicon vias (TSVs) junto ao die da GPU. As gerações incluem HBM2e (A100, 2 TB/s), HBM3 (H100, 3,35 TB/s) e HBM3e (H200/B200, 4,8–8 TB/s). A largura de banda da HBM é uma restrição direta do TPOT nos regimes de decode memory-bound.

GDDR (Graphics DDR) é a memória gráfica tradicional usada em GPUs de consumo e de workstation, como a RTX 4090 e a L40S. Estas placas têm menos largura de banda de memória do que a H100 SXM. A GDDR6X da RTX 4090 oferece 1008 GB/s segundo o whitepaper da arquitetura Ada da NVIDIA, contra os 3,35 TB/s de HBM3 da H100 SXM.

Um SM (Streaming Multiprocessor) é o bloco básico de computação de uma GPU NVIDIA. Cada SM contém CUDA cores, Tensor Cores, memória partilhada e um warp scheduler. A H100 SXM tem 132 SMs; a H100 PCIe tem 114. A A100 tem 108.

Os Tensor Cores são unidades especializadas de multiplicação e acumulação de matrizes dentro de cada SM. Aceleram as multiplicações de matrizes de precisão mista que dominam a computação dos transformers. Os Tensor Cores da H100 SXM oferecem 494,5 TFLOPS TF32 densos ou 989 esparsos; inclua sempre a precisão e o modo de sparsity ao comparar este valor.

Os CUDA Cores são unidades de uso geral de vírgula flutuante e de inteiros. Tratam das operações elemento a elemento, das funções de ativação e de outro trabalho que não é matricial, enquanto os Tensor Cores executam as operações matriciais suportadas.

Um warp é um grupo de 32 threads num SM. As threads podem divergir, e as GPUs Volta e posteriores suportam escalonamento independente de threads. A especialização de warps atribui warps diferentes ao movimento de dados e à computação, para que as tarefas se sobreponham.

O NVLink é a interligação de alta velocidade entre GPUs da NVIDIA dentro de um nó. O NVLink 4.0 da H100 oferece 900 GB/s de largura de banda bidirecional; o NVLink 5.0 da B200 chega a 1,8 TB/s. Esta largura de banda é importante para o paralelismo de tensores, porque as GPUs trocam resultados parciais em cada camada do transformer.

A InfiniBand é uma rede de alta velocidade para comunicação entre nós. Os adaptadores InfiniBand da classe ConnectX fornecem a rede entre nós para o tráfego de pipeline paralelo ou de treino distribuído; a largura de banda depende do adaptador e da configuração das portas.

O RDMA (Remote Direct Memory Access) permite que um dispositivo aceda à memória de outra máquina sem que o caminho dos dados passe por qualquer um dos CPUs. O GPUDirect RDMA suporta transferências diretas de GPU para GPU entre nós, incluindo transferências de KV cache em serving desagregado.

O NVMe (Non-Volatile Memory Express) é a interface de SSD usada para o offloading da KV cache e para o offloading de parâmetros do ZeRO-Infinity quando a memória da GPU e do CPU é insuficiente. A largura de banda sequencial depende do disco e da carga de trabalho, e continua muito abaixo da largura de banda da HBM.

TFLOPS e PFLOPS são biliões e mil biliões de operações de vírgula flutuante por segundo. Um TFLOPS equivale a 101210^{12} FLOPS. A H100 chega a 989 TFLOPS TF32 esparsos nos tensor cores, enquanto o FlashAttention-3 reporta cerca de 1,2 PFLOPS em FP8; estes valores usam formatos diferentes e não devem ser comparados como se fossem a mesma métrica.


Parte II — Fundamentos da inferência

A inferência transforma um modelo num serviço visível para o utilizador. Os conceitos seguintes separam o trabalho que atrasa o primeiro token do trabalho que atrasa todos os tokens seguintes, e mostram os limites de memória por trás de ambos.

4. Latência: TTFT, TPOT e percentis

O Time to First Token (TTFT) é o atraso de ponta a ponta desde o envio do pedido até ao primeiro token de saída. Inclui fila, tokenização, escalonamento, a passagem de prefill que produz o primeiro token e a entrega pela rede. Os motores de serving mais comuns amostram o primeiro token de saída a partir dos logits da passagem forward do prefill, pelo que nenhum passo de decode separado corre antes dele. Prompts mais longos aumentam normalmente a componente de prefill, mas qualquer uma destas fases pode dominar. Os objetivos dependem do produto; o MLPerf Inference v5.0 usa um limite de TTFT P99 de ≤\leq 450 ms no seu cenário interativo com Llama 2 70B.

O Time Per Output Token (TPOT) é o intervalo médio entre tokens consecutivos depois do primeiro. Corresponde à fase de decode. O decode com batch de um ou de baixa intensidade é normalmente limitado pela largura de banda de memória, enquanto batches suficientemente grandes podem tornar-se compute-bound:

TPOT=E2E Latency−TTFTOutput Tokens−1\text{TPOT} = \frac{\text{E2E Latency} - \text{TTFT}}{\text{Output Tokens} - 1}

Esta definição requer pelo menos dois tokens de saída; o TPOT não está definido para uma resposta com um único token. Nesta equação, a latência E2E termina no último token de saída. Um temporizador que mede até à conclusão do HTTP pode incluir metadados posteriores ou o encerramento da ligação; registe esse tempo separadamente.

Uma estimativa da velocidade de leitura em inglês dá uma referência aproximada para o streaming. A velocidade média de leitura silenciosa de um adulto em inglês é de cerca de 238 palavras por minuto para não ficção (Brysbaert, 2019), ou seja, cerca de 4 palavras por segundo. Se um token corresponder, em média, a três quartos de uma palavra, isso dá cerca de 5 tokens por segundo, ou 200 ms por token. Esta estimativa não é um limiar de usabilidade: a língua, o tokenizador, a tarefa e o buffering do cliente alteram o resultado. O cenário interativo do MLPerf usa um limite de TPOT P99 muito mais apertado, de ≤\leq 40 ms. Defina o seu próprio objetivo com testes de produto.

A latência P50 vs P99 é relevante porque a mediana esconde a cauda. Um sistema com bom P50 e mau P99 pode ter problemas de batching, preempção, filas ou enviesamento da carga; são necessários traces para os distinguir.

5. Throughput: tokens por segundo e o compromisso com a latência

O throughput mede-se em tokens de saída por segundo entre pedidos concorrentes. Os pedidos por segundo, por si sós, dizem menos, porque uma resposta de 10 tokens e uma de 1000 tokens têm custos muito diferentes. Os números de benchmarks publicados variam com o modelo, a precisão, o hardware, os comprimentos do prompt e da saída, a concorrência e o SLO. Compare vLLM, SGLang e TensorRT-LLM com um único harness, em vez de combinar os resultados de destaque de cada um.

Aumentar um batch pequeno de decode pode elevar o throughput ao partilhar as leituras dos pesos. Batches maiores ou uma carga oferecida mais elevada acabam por aumentar a latência ou atingir limites de memória, computação ou comunicação. Meça essa curva em vez de assumir uma escala linear. O goodput, a taxa de pedidos por segundo que cumprem os objetivos do SLO, liga o throughput bruto àquilo que os utilizadores realmente sentem.

6. KV cache: memória que cresce a cada token

Durante a geração autorregressiva com attention completa, cada novo token atende ao token atual e aos anteriores. A KV cache guarda as projeções Key e Value para que a geração não recalcule as projeções dos tokens anteriores em cada passo. O decode com attention completa continua a ler um histórico crescente; o trabalho total de attention numa sequência gerada continua a ser quadrático no seu comprimento.

Numa cache convencional de MHA ou GQA com attention completa, a memória cresce linearmente com o comprimento da sequência, o tamanho do batch e o número de camadas. A fórmula seguinte não se aplica sem alterações a arquiteturas MLA, de janela deslizante, recorrentes ou híbridas:

KVcache=2×L×hkv×dh×s×B×bytesKVcache = 2 \times L \times h_{kv} \times d_h \times s \times B \times \text{bytes}

em que:

  • LL = número de camadas
  • hkvh_{kv} = número de KV heads
  • dhd_h = dimensão da head
  • ss = comprimento da sequência
  • BB = tamanho do batch
  • bytes\text{bytes} = bytes por elemento em cache, definidos pela precisão da KV cache

Exemplos concretos com FP16 e batch de 1: o Llama 3.1 8B com 8192 tokens usa ~1,07 GB (1 GiB) de KV cache; com 128K tokens, 16 GiB. O Llama 3.1 70B com 128K tokens precisa de 40 GiB (cerca de 43 GB) para uma única sequência, aproximadamente metade dos 80 GB de uma H100. Com alta concorrência ou contexto longo, a KV cache pode exceder a memória dos pesos do modelo; o resultado depende do comprimento da sequência, do tamanho do batch ativo, da precisão da KV e do número de KV heads. A avaliação original do vLLM em 2023 reportou 60–80% de desperdício por fragmentação e pré-alocação nos sistemas que comparou.

As principais otimizações são o GQA (menos KV heads), a quantização da KV cache (FP8/INT8), o PagedAttention (alocação por blocos) e o offloading de dados KV reutilizáveis para CPU ou NVMe. Os benefícios dependem da carga de trabalho e da implementação.

Para responder a “quantas sequências cabem no meu orçamento de KV?” para outro modelo, leia três números do seu config.json: num_hidden_layers, num_key_value_heads e head_dim, quando existir. Nos exemplos Llama, a dimensão da head é hidden_size / num_attention_heads. A memória por dispositivo também depende do sharding da cache e de eventual replicação das KV heads. A função abaixo aplica a fórmula; os asserts reproduzem os números acima e o exemplo de capacidade da Secção 45.

def kv_cache_bytes(layers, kv_heads, head_dim, seq_len, batch=1, bytes_per_elem=2):
    """Bytes of KV cache: a key and a value per layer, KV head, and token."""
    return 2 * layers * kv_heads * head_dim * seq_len * batch * bytes_per_elem

GiB = 2**30

# Llama 3.1 8B: 32 layers, 8 KV heads, head dimension 128, FP16 cache.
llama_8b = kv_cache_bytes(32, 8, 128, seq_len=8_192)
print(f"8B at 8K tokens: {llama_8b / 1e9:.2f} GB")  # 1.07 GB
assert llama_8b == 1 * GiB

# Llama 3.1 70B: 80 layers, 8 KV heads, head dimension 128.
assert kv_cache_bytes(80, 8, 128, seq_len=131_072) == 40 * GiB

# How many 4K-token sequences fit in a 40 GiB KV budget?
per_4k = kv_cache_bytes(80, 8, 128, seq_len=4_096)
print(f"70B at 4K tokens: {per_4k / GiB:.2f} GiB per sequence")  # 1.25 GiB
print("4K sequences in 40 GiB:", 40 * GiB // per_4k)  # 32
assert 40 * GiB // per_4k == 32

# An FP8 KV cache halves the size per sequence and doubles the count.
assert 40 * GiB // kv_cache_bytes(80, 8, 128, 4_096, bytes_per_elem=1) == 64

O resultado é um limite superior. O runtime também precisa de memória para ativações, overhead do alocador e margem para a variação do comprimento dos pedidos.

7. Prefill vs decode: duas fases, dois bottlenecks

A fase de prefill processa o prompt de entrada em paralelo e preenche a KV cache. Os prefills longos ou suficientemente agrupados em batches tornam-se muitas vezes compute-bound porque usam grandes multiplicações de matrizes; os prefills curtos podem continuar limitados pelo tráfego de memória ou pelo overhead de lançamento de kernels. O prefill contribui para o time to first token (TTFT), a par da fila, da tokenização, do escalonamento e da entrega pela rede; a sua passagem forward também produz o primeiro token de saída. A fase de decode gera um token de cada vez. Cada passo lê os pesos do modelo e a KV cache da HBM, o que torna o decode com batch de um limitado pela largura de banda de memória e o principal responsável pelo time per output token (TPOT).

A figura acompanha um pedido: o prefill reutiliza os pesos entre os tokens do prompt e termina com o primeiro token, dentro do TTFT; cada passo posterior de descodificação volta a ler os pesos e o KV cache para um token e contribui para o TPOT.

Cronologia de um pedido: depois da fila, o prefill processa oito tokens do prompt numa passagem, reutiliza os pesos entre eles e emite o token t1, que termina o TTFT. Cada passo de descodificação processa um token, volta a ler os pesos e o KV cache da HBM e emite o token seguinte; o intervalo entre dois tokens é o TPOT.Cronologia de um pedido: depois da fila, o prefill processa oito tokens do prompt numa passagem, reutiliza os pesos entre eles e emite o token t1, que termina o TTFT. Cada passo de descodificação processa um token, volta a ler os pesos e o KV cache da HBM e emite o token seguinte; o intervalo entre dois tokens é o TPOT.

O chunked prefill divide um prompt longo em chunks e escalona-os juntamente com os decodes em curso. O tamanho efetivo dos chunks pode variar com o orçamento de tokens disponível na iteração. No Sarathi-Serve (secção 4.2) e no vLLM, o scheduler coloca primeiro os decodes em curso no batch e preenche o resto de um orçamento de tokens por iteração com tokens de prefill. Assim, um prompt longo deixa de impedir os outros pedidos de receberem tokens. O novo pedido paga por isto: o seu prefill estende-se por várias iterações e cada chunk volta a ler a KV cache dos chunks anteriores. O Sarathi-Serve mediu cerca de 25% de tempo de prefill adicional com o Yi-34B, paralelismo de tensores de grau dois e chunks de 512 tokens (secção 5.4.1), e a documentação do vLLM indica que um orçamento de tokens menor dá melhor latência entre tokens e pior TTFT. O Sarathi-Serve reporta melhores compromissos entre throughput e latência nas cargas testadas, mas o ganho depende do modelo, do hardware, da distribuição de comprimentos dos pedidos, do tamanho do chunk e da base de comparação.

O serving desagregado coloca o prefill e o decode em pools de GPUs separados, o que permite que cada pool ataque um bottleneck diferente. O Splitwise e o DistServe descrevem o padrão. O pool de prefill envia a KV cache de cada pedido para o pool de decode. O DistServe usa NVLink dentro de um nó e InfiniBand entre nós; o Splitwise usa InfiniBand, que suporta RDMA. O custo da transferência passa a fazer parte do desenho.

A figura põe os dois escalonamentos no mesmo eixo de tempo: um prompt longo chega enquanto outros pedidos estão em decode. O chunking mantém a chegada dos tokens dos outros pedidos e adia o primeiro token do novo pedido.

Duas linhas temporais numa GPU com o mesmo eixo de tempo; um prompt longo chega enquanto outros pedidos estão em decode. Sem chunking, o prefill de todo o prompt corre como um único passo longo, os decodes em curso esperam por ele todo e o primeiro token t1 do novo pedido surge no fim do prefill. Com chunked prefill, os chunks P1 a P5 correm cada um numa iteração com um passo de decode, pelo que os decodes nunca param, mas cada iteração demora mais e t1 surge mais tarde do que sem chunking.Duas linhas temporais numa GPU com o mesmo eixo de tempo; um prompt longo chega enquanto outros pedidos estão em decode. Sem chunking, o prefill de todo o prompt corre como um único passo longo, os decodes em curso esperam por ele todo e o primeiro token t1 do novo pedido surge no fim do prefill. Com chunked prefill, os chunks P1 a P5 correm cada um numa iteração com um passo de decode, pelo que os decodes nunca param, mas cada iteração demora mais e t1 surge mais tarde do que sem chunking.

8. GQA e MQA: reduzir a KV cache

A Multi-Head Attention (MHA) padrão dá a cada query head a sua própria head K e V. A Multi-Query Attention (MQA) partilha uma única KV head por todas as query heads, uma redução extrema. A Grouped-Query Attention (GQA) é o meio-termo prático: grupos de query heads partilham uma KV head. A figura desenha oito query heads e as KV heads que cada variante mantém; a KV cache diminui na proporção do número de KV heads.

Três painéis com oito query heads cada. A MHA mantém oito KV heads, uma por query head. A MQA mantém uma KV head partilhada pelas oito, 12,5 por cento da cache da MHA. A GQA mantém duas KV heads, cada uma partilhada por quatro query heads, 25 por cento da cache da MHA.Três painéis com oito query heads cada. A MHA mantém oito KV heads, uma por query head. A MQA mantém uma KV head partilhada pelas oito, 12,5 por cento da cache da MHA. A GQA mantém duas KV heads, cada uma partilhada por quatro query heads, 25 por cento da cache da MHA.

O Llama 3 70B usa 64 query heads mas apenas 8 KV heads, uma redução de 8x da KV cache face à mesma arquitetura com uma KV head por query head. O Llama 3.1 405B usa 128 query heads e 8 KV heads, uma redução de 16x pelo mesmo cálculo (Meta, 2024). Ainslie et al. reportam uma qualidade da GQA próxima da MHA nos modelos que testaram, aproximando-se da velocidade da MQA. Uma KV cache mais pequena permite batches maiores, mas o ganho real de latência e de throughput continua a depender do kernel e da carga.

9. Quantização: trocar bits por velocidade e memória

A quantização reduz a precisão dos pesos e/ou das ativações do modelo. Os compromissos principais:

FormatoBitsMemória dos pesos (modelo de 7B)Nota sobre a qualidade
FP16/BF1616~14 GBReferência para comparação
FP88~7 GBNativo em hardware na Hopper; avalie o modelo
INT88~7 GBDepende da calibração e do kernel
INT44~3,5 GBCompressão mais elevada; avalie com cuidado

O AWQ (Activation-Aware Weight Quantization) identifica canais de pesos relevantes a partir das magnitudes das ativações e aplica uma escala por canal para os proteger. As necessidades de calibração dependem do modelo e da configuração; numa comparação OPT-6.7B INT3-g128, o AWQ usou 16 sequências de calibração enquanto o GPTQ usou 192. Numa configuração reprodutível, indique tanto o número de sequências como o seu comprimento. O GPTQ usa informação aproximada de segunda ordem para quantização camada a camada. O bitsandbytes pode quantizar durante o carregamento do modelo, sem uma passagem separada de pré-processamento; o seu formato NF4 sustenta o fine-tuning com QLoRA. O FP8 em hardware da classe Hopper reduz para metade a memória dos pesos em relação ao FP16/BF16, mas a qualidade e a velocidade continuam a depender do modelo, da calibração e do kernel.

A tabela associa cada método à tarefa a que normalmente se adequa. O guia de quantização de modelos trata cada método em profundidade.

MétodoDados de calibraçãoUso típico
AWQ ou GPTQ (INT4 só pesos)Sim, uma pequena amostraServir um modelo grande em GPUs com pouca memória, através de um kernel INT4 suportado
bitsandbytes NF4NãoFine-tuning com QLoRA e experiências rápidas
FP8Pequena amostra ou escala dinâmicaServir em GPUs Hopper ou mais recentes, com verificação de qualidade na tarefa alvo
INT8 (W8A8)Sim, para as escalas das ativaçõesGPUs sem FP8, como a A100 ou a A10G, quando o kernel de serving o suporta

O kernel de serving pode ser tão importante como o algoritmo de quantização. A Secção 10 apresenta um resultado delimitado do Marlin e explica por que razão o ganho depende da configuração de serving.


Parte III — Otimizações de inferência

As otimizações desta parte resolvem restrições diferentes. O FlashAttention reduz o tráfego de HBM da attention; o PagedAttention melhora a alocação da KV; o batching contínuo impede que sequências terminadas ocupem capacidade do batch.

10. Kernels CUDA e fusão de kernels

Um kernel CUDA é uma função escrita para a GPU que corre em paralelo em milhares de threads. Quando o CPU chama um kernel, a GPU distribui o trabalho pelos seus SMs: cada SM executa vários warps de 32 threads e cada thread processa uma fatia dos dados. Uma implementação de serving em CUDA executa o trabalho da GPU através de kernels. O número de lançamentos depende do grafo do modelo, do compilador, do runtime e da fusão. A implementação do kernel pode decidir se o sistema cumpre o seu SLO de latência.

As principais categorias de kernels no serving de LLMs:

  • Kernels GEMM para a multiplicação de matrizes, que dominam a computação tanto do prefill como do decode.
  • Kernels de attention, como o FlashAttention, que dividem a computação em tiles para ficarem na SRAM em vez de transbordarem para a HBM.
  • Kernels fundidos que combinam várias operações (como add + layer norm ou a projeção QKV) num único lançamento, para evitar as idas e voltas intermédias à HBM.
  • Kernels de amostragem que convertem logits em IDs de tokens por amostragem top-k, top-p ou com temperature.

A qualidade do kernel pode determinar se os pesos comprimidos se traduzem em ganho de velocidade. O artigo do Marlin reporta um speedup de ponta a ponta até 2,8x sobre a sua referência FP16 em configurações de vLLM testadas com INT4 só de pesos. Esse resultado é específico dos modelos, GPUs, tamanhos de batch e configuração de serving do artigo, pelo que não é um ganho universal do INT4.

O Triton baixa a barreira à escrita de kernels personalizados ao expor a programação de GPUs através de Python em vez de CUDA C++ puro, o que coloca a otimização ao nível do kernel ao alcance dos engenheiros de ML e não apenas dos especialistas em GPUs. A maioria das otimizações mais adiante nesta parte (FlashAttention, kernels fundidos, PagedAttention) são melhores kernels ou formas mais inteligentes de orquestrar lançamentos de kernels.

A fusão de kernels combina operações sequenciais num único kernel de GPU e evita escritas intermédias na HBM. As fusões comuns incluem a projeção QKV, attention mais softmax, adição residual mais RMSNorm. O FlashNorm combina, em vez disso, o reescalamento RMSNorm com a projeção linear seguinte e adia a normalização. O DeepFusionKernel funde um MLP SwiGLU completo, incluindo as multiplicações de matrizes. Os ganhos exatos em número de lançamentos e em utilização dependem do grafo do modelo, do compilador, da GPU e do framework de serving, por isso faça profiling da stack em produção em vez de confiar numa percentagem universal.

11. FlashAttention: dividir a attention em tiles para viver na SRAM

A attention padrão materializa a matriz de attention completa N×NN \times N na HBM, o que custa O(N2)O(N^2) de memória e gera muito tráfego de memória. A ideia do FlashAttention é nunca materializar essa matriz. Divide as matrizes Q, K, V em blocos que cabem na SRAM, calcula a attention parcial dentro de cada tile e junta os resultados com um softmax online (que acompanha de forma incremental o máximo e a soma correntes ao longo dos blocos). O armazenamento intermédio desce de O(N2)O(N^2) para O(N)O(N), com menos leituras e escritas na HBM nas formas testadas no artigo. A aritmética da attention densa continua a ser quadrática no comprimento da sequência.

Cada versão ataca o bottleneck da sua geração de GPU:

  • O FlashAttention v1 (A100, 2022) provou que a ideia de tiling com softmax online funciona. O artigo reportou um speedup de 2–4x sobre a attention padrão, mas apenas 25–40% de utilização da GPU. O artigo do FA2 atribui isso ao trabalho que não é multiplicação de matrizes, à comunicação pela memória partilhada e ao paralelismo limitado.
  • O FlashAttention v2 (A100, 2023) reformulou o paralelismo para dividir ao longo da dimensão da sequência bem como do batch e das heads. Atingiu 50–73% de utilização na A100, cerca de 2x mais rápido do que a v1.
  • O FlashAttention v3 (H100 Hopper, 2024) acrescentou a especialização de warps (warps separados para movimento de dados e para cálculo) e o pipelining GEMM-softmax para sobrepor os carregamentos de memória à computação. O artigo reporta até 740 TFLOPS em FP16 (75% de utilização) e perto de 1,2 PFLOPS em FP8 na H100.
  • O FlashAttention v4 (B200 Blackwell, 2026) ataca um novo bottleneck: na Blackwell, o throughput dos tensor cores cresce tão depressa que as operações que não são multiplicações de matrizes (exponenciais do softmax, reescalonamento) passam a ser o limite. O FA4 emula por software a exponencial com aproximações polinomiais em unidades FMA, usa reescalonamento condicional para reduzir o overhead e guarda os valores intermédios na memória dedicada de tensores da Blackwell (TMEM) em vez de nos registos. O artigo reporta cerca de 1,6 PFLOPS na B200 em BF16, 1,3x mais rápido do que o cuDNN 9.13 e 2,7x mais rápido do que o Triton nos seus testes.

12. FlashDecoding: paralelizar o bottleneck do decode

O FlashAttention padrão mantém a GPU ocupada dividindo o trabalho pelo tamanho do batch e pelo comprimento da query. Durante o decode, o modelo gera exatamente 1 token de cada vez (comprimento da query = 1). Se o tamanho do batch vezes o número de attention heads for muito inferior ao número de SMs da GPU (108 numa A100), muitos SMs podem ficar inativos enquanto os restantes processam o histórico de tokens.

O FlashDecoding resolve isto acrescentando uma nova dimensão de paralelização: o próprio comprimento da sequência de KV. Divide a KV cache em chunks mais pequenos e distribui-os por todos os processadores da GPU que de outro modo estariam inativos, para os avaliar em paralelo, e depois junta os cálculos parciais com uma redução log-sum-exp.

No benchmark do Stanford com o CodeLlama-34B e batch de um, em quatro GPUs A100, com comprimentos de sequência de 512 a 64K tokens, o FlashDecoding atingiu um speedup de ponta a ponta até 8x sobre as referências testadas e melhorou a escalabilidade da attention nos contextos longos testados. O resultado está limitado a esse hardware e a esse benchmark, e não é uma garantia geral para o decode.

13. Batching contínuo vs batching estático

O batching estático espera que todas as sequências de um batch terminem antes de começar o seguinte, pelo que as sequências curtas desperdiçam ciclos de GPU em espera depois de atingirem o fim de sequência. O batching contínuo (introduzido pelo artigo do Orca, OSDI 2022) opera com granularidade ao nível da iteração: em cada passo de decode, as sequências concluídas são removidas e inseridas novas. A figura mostra quatro slots de batch ao longo de dez passos de decode em cada política.

Duas grelhas de quatro slots de batch ao longo de dez passos de decode. No batching estático, os slots dos pedidos B, C e D ficam inativos depois de terminarem até o pedido A acabar: 6, 4 e 7 passos inativos, 17 de 40 slot-passos. No batching contínuo, novos pedidos E, F e G ocupam os slots livres nos passos 4, 5 e 7.Duas grelhas de quatro slots de batch ao longo de dez passos de decode. No batching estático, os slots dos pedidos B, C e D ficam inativos depois de terminarem até o pedido A acabar: 6, 4 e 7 passos inativos, 17 de 40 slot-passos. No batching contínuo, novos pedidos E, F e G ocupam os slots livres nos passos 4, 5 e 7.

No benchmark OPT-13B de 2023 da Anyscale numa A100-40GB, com 1000 pedidos, prompts de 512 tokens e saídas com uma média de 128 tokens, o batching estático otimizado atingiu 4x a sua referência ingénua, o batching contínuo atingiu 8x e o vLLM com batching contínuo mais PagedAttention atingiu 23x (Anyscale, 2023). O batching contínuo também aumenta a pressão sobre a alocação da KV, razão pela qual é habitualmente combinado com gestão de memória paginada.

14. PagedAttention: memória virtual para a KV cache

O PagedAttention do vLLM aplica à gestão da KV cache a ideia de memória virtual dos sistemas operativos. A KV cache é dividida em blocos de tamanho fixo (16 tokens nesta ilustração; o tamanho dos blocos varia entre runtimes), os blocos são alocados a pedido à medida que os tokens são gerados e as posições lógicas (sequenciais) são mapeadas para localizações físicas (dispersas) da memória através de block tables. Vários pedidos que partilham um prefixo (system prompts, beam search) podem apontar para os mesmos blocos físicos. A reutilização de prefixos também exige chaves de cache compatíveis e uma política de consulta da cache. A figura mostra uma block table para dois pedidos que partilham um system prompt.

O pedido A tem os blocos lógicos A0 a A2 e o pedido B tem B0 e B1. Entradas da block table mapeiam-nos para blocos físicos dispersos na memória da GPU; A0 e B0 mapeiam ambos para o bloco 7, que guarda o system prompt partilhado. Cada bloco tem espaço para 16 tokens: os últimos blocos de A e de B estão parcialmente preenchidos, e os blocos 0, 2, 4 e 6 estão livres.O pedido A tem os blocos lógicos A0 a A2 e o pedido B tem B0 e B1. Entradas da block table mapeiam-nos para blocos físicos dispersos na memória da GPU; A0 e B0 mapeiam ambos para o bloco 7, que guarda o system prompt partilhado. Cada bloco tem espaço para 16 tokens: os últimos blocos de A e de B estão parcialmente preenchidos, e os blocos 0, 2, 4 e 6 estão livres.

A avaliação do vLLM de junho de 2023 reportou 60–80% de desperdício de memória KV nos sistemas anteriores testados e menos de 4% no seu alocador paginado. Reportou um throughput 2–4x superior ao TGI e até 24x superior ao Hugging Face Transformers em determinadas cargas de trabalho. São comparações históricas de ponta a ponta entre sistemas de serving, e não um speedup ou um limite de desperdício universal do PagedAttention. Um último bloco parcialmente preenchido pode desperdiçar uma proporção maior em sequências curtas.

15. Descodificação especulativa: vários tokens por passagem forward

Na descodificação especulativa, um modelo draft pequeno gera KK tokens candidatos e depois o modelo alvo grande avalia as KK posições numa única passagem forward. O decoder aceita os tokens do draft da esquerda para a direita, com probabilidades derivadas das distribuições do alvo e do draft. Após a primeira rejeição, amostra uma correção a partir da distribuição residual do alvo e descarta os restantes tokens do draft. Este passo modificado de amostragem por rejeição preserva a distribuição de saída do modelo alvo, dentro da numérica do hardware; a correspondência exata de tokens, por si só, não o faz. A figura percorre uma ronda com quatro tokens de draft.

Uma ronda com K = 4: o modelo draft propõe d1 a d4, o modelo alvo avalia os quatro numa passagem forward, d1 e d2 são aceites, d3 é rejeitado e substituído por uma correção c3 amostrada do alvo, e d4 é descartado, pelo que a ronda produz três tokens.Uma ronda com K = 4: o modelo draft propõe d1 a d4, o modelo alvo avalia os quatro numa passagem forward, d1 e d2 são aceites, d3 é rejeitado e substituído por uma correção c3 amostrada do alvo, e d4 é descartado, pelo que a ronda produz três tokens.

O ganho é mais plausível com batches de serving pequenos e drafts curtos, quando a avaliação do draft é dominada pelo tráfego de pesos, de KV cache ou de comunicação e não pela computação dos tokens adicionais. O artigo original de speculative sampling reportou um speedup de descodificação de 2–2,5x na sua configuração testada com o Chinchilla de 70B; o EAGLE-3 v3 reportou até 6,5x nos seus testes. As variantes incluem o Medusa (heads de predição adicionais, sem modelo separado), o prompt lookup decoding (correspondência de n-gramas com a entrada, sem uma passagem forward de um modelo draft separado) e o EAGLE. O EAGLE original extrapola features; o EAGLE-3 combina vários níveis de features e prevê tokens diretamente.

Com batches grandes, o trabalho adicional de draft e verificação pode anular o ganho. A descodificação especulativa é mais promissora quando o batch de serving é suficientemente pequeno e a aceitação do draft é elevada; faça benchmark do ciclo de serving completo e não apenas do kernel de verificação.

16. Cache de prefixos e reutilização da KV cache

Em vez de deitar fora a KV cache quando um pedido termina, o prefix caching mantém-na para reutilização em novos pedidos que partilham os mesmos tokens de prefixo. Isso reduz o prefill redundante de system prompts, exemplos few-shot, contexto de RAG e histórico de conversas com vários turnos.

A Automatic Prefix Caching do vLLM calcula o hash dos blocos de KV e usa uma tabela de hash global para consulta. O RadixAttention do SGLang mantém uma árvore radix de tensores KV em cache com granularidade ao nível do token. Ambos exigem prefixos com tokens idênticos e chaves de cache compatíveis. A identidade do modelo ou adaptador, as entradas multimodais e as definições de isolamento também podem afetar a reutilização. Reporte a taxa de acertos juntamente com a latência ou o throughput.

17. Streaming na prática

O streaming envia os tokens ao cliente à medida que são gerados, em vez de esperar pela resposta completa. Muitos frameworks de serving expõem-no por Server-Sent Events: o cliente abre uma ligação HTTP de longa duração e o servidor envia cada token, ou grupo de tokens, como um evento data:. O TTFT determina quando o utilizador vê pela primeira vez a saída; o TPOT ajuda a determinar a fluidez da experiência. Defina o objetivo através de testes de produto e do modelo de interação escolhido.

Do lado do cliente, o streaming obriga a decisões de buffering. Renderizar token a token pode causar tremor visual, sobretudo com markdown ou blocos de código que precisam de contexto de vários tokens para serem formatados corretamente. Os padrões comuns são o buffering ao nível da palavra (acumular tokens até uma fronteira de espaço em branco), o buffering ao nível da linha (esperar por uma nova linha antes de renderizar) e o buffering adaptativo (renderizar de imediato para prosa e fazer buffer para blocos de código). Na API Chat Completions da OpenAI, stream_options: {"include_usage": true} acrescenta um chunk final de utilização antes da mensagem data: [DONE]. Os streams interrompidos da OpenAI podem omitir o chunk final de utilização. Os servidores compatíveis com a OpenAI podem diferir, por isso verifique a implementação escolhida.

O chunked prefill é uma forma de impedir que prefills longos bloqueiem a entrega de tokens a utilizadores concorrentes. O escalonamento com prioridade ao decode pode proteger os pedidos em curso, enquanto o serving desagregado isola o prefill e o decode em pools de GPUs separados.


Parte IV — Arquitetura do modelo

A arquitetura define a pegada de memória, o comportamento da attention e a dinâmica de treino em torno dos quais as secções de serving e de treino trabalham.

18. Fundamentos da arquitetura Transformer

Um transformer decoder-only moderno (GPT, Llama) é uma pilha de camadas idênticas, cada uma com dois sub-blocos: attention e feed-forward. Cada sub-bloco é envolvido por uma ligação residual e por normalização. A figura mostra uma camada ao estilo Llama, incluindo os dois caminhos que saltam um sub-bloco, e como os pesos das projeções se dividem entre a attention e a rede feed-forward; os componentes são descritos abaixo.

À esquerda: uma camada de decoder do Llama 3 8B, repetida 32 vezes: RMSNorm, grouped-query attention com 32 query heads e 8 KV heads, e uma soma que recebe também a entrada da camada; depois RMSNorm, uma rede feed-forward SwiGLU e uma segunda soma que recebe também o resultado da attention. À direita: pesos das projeções por camada, cerca de 42M para a attention e cerca de 176M, 81 por cento, para a rede feed-forward.À esquerda: uma camada de decoder do Llama 3 8B, repetida 32 vezes: RMSNorm, grouped-query attention com 32 query heads e 8 KV heads, e uma soma que recebe também a entrada da camada; depois RMSNorm, uma rede feed-forward SwiGLU e uma segunda soma que recebe também o resultado da attention. À direita: pesos das projeções por camada, cerca de 42M para a attention e cerca de 176M, 81 por cento, para a rede feed-forward.

Os componentes principais:

A Multi-Head Attention permite que cada token pondere a informação dos tokens visíveis sob a máscara de attention. A entrada é projetada em três matrizes: Queries (o que procuro?), Keys (o que contenho?) e Values (que informação transporto?). As pontuações de attention são depois calculadas como:

Attention(Q,K,V)=softmax(QKTdk)V\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V

O produto escalar QKTQK^T mede a similaridade entre todos os pares de tokens. Dividir por dk\sqrt{d_k} impede que os produtos escalares cresçam demasiado (o que empurraria o softmax para regiões com gradientes que desaparecem). O softmax converte as pontuações em probabilidades e a multiplicação por VV produz uma combinação ponderada dos vetores de valores. Executar isto em várias heads em paralelo permite ao modelo atender a relações diferentes em simultâneo (as heads podem aprender relações diferentes; os seus papéis não são fixos).

A Feed-Forward Network (FFN) transforma cada representação de token de forma independente depois de a attention misturar informação entre tokens. Os LLMs modernos usam muitas vezes SwiGLU em vez da FFN original de duas matrizes com ReLU:

SwiGLU(x)=(Swish(xWgate)⊙xWup)Wdown\text{SwiGLU}(x) = \left(\text{Swish}(xW_{gate}) \odot xW_{up}\right)W_{down}

O SwiGLU tem três matrizes de pesos, contra duas numa FFN com ReLU, e usa o Swish, que é suave. O Llama, o Mistral e o Qwen usam-no; o Gemma usa uma GeGLU aproximada. A conhecida regra dos dois terços aplica-se a um bloco convencional com MHA completa e dff≈4dd_{ff} \approx 4d: as suas duas matrizes da FFN contribuem com cerca de 8d28d^2 parâmetros, contra cerca de 4d24d^2 da attention. Não é uma estimativa geral para arquiteturas SwiGLU/GQA. No Llama 3 8B, d=4,096d=4{,}096, dff=14,336d_{ff}=14{,}336 e oito key/value heads fazem com que a FFN tenha 3d×dff≈1763d \times d_{ff} \approx 176M parâmetros por camada, contra cerca de 42M parâmetros de projeção da attention: cerca de 81% desses pesos de projeção, antes dos embeddings e da normalização.

As ligações residuais somam a saída de cada sub-bloco à sua entrada: Output=Input+Sublayer(Input)\text{Output} = \text{Input} + \text{Sublayer}(\text{Input}). O caminho de salto melhora a propagação do sinal e dos gradientes em pilhas profundas.

O RMSNorm é comum nas famílias modernas de LLMs. O LayerNorm recentra subtraindo a média e reescala pelo desvio-padrão. O RMSNorm dispensa a subtração da média e apenas reescala; o seu artigo reporta reduções de 7–64% no tempo de execução nos modelos e frameworks testados, sem reduzir o desempenho medido nas tarefas. A colocação pre-norm, que normaliza antes da attention ou da FFN, também é comum porque melhora a estabilidade dos gradientes.

A estimativa do número de parâmetros de um decoder convencional com MHA completa, uma FFN de duas matrizes com largura 4d4d e embeddings de entrada e saída partilhados:

Total≈V×d+12×L×d2\text{Total} \approx V \times d + 12 \times L \times d^2

em que VV é o tamanho do vocabulário, dd é a dimensão oculta e LL é o número de camadas. O termo V×dV \times d é a matriz de embeddings de entrada; o termo 12×d212 \times d^2 aproxima os pesos da attention e da FFN em cada camada. Para o Llama 3 8B (V=128,256V=128{,}256, d=4,096d=4{,}096, L=32L=32), a estimativa é de cerca de 0.53B+6.44B=6.97B0.53B + 6.44B = 6.97B parâmetros. O total publicado de 8,03B é superior porque a aproximação omite detalhes arquiteturais, como a largura exata da FFN e a projeção de saída separada.

19. Modelos decoder-only para geração de uso geral

O Transformer original (2017) tinha um encoder e um decoder. Desde então, a área dividiu-se em três famílias arquiteturais, e uma tornou-se a predefinição para a AI generativa.

FamíliaAttentionObjetivo de treinoUsada hoje paraExemplos
Encoder-onlyBidirecionalPredição de tokens mascaradosEmbeddings, rerankers, classificadoresBERT, RoBERTa
Encoder-decoderEncoder bidirecional; decoder causal com cross-attentionSequência para sequência (span corruption, T5)Tradução, reconhecimento de falaT5, BART, Whisper
Decoder-onlyCausalPredição do token seguinteGeração de texto geral, chat, códigoGPT, Llama

Os modelos encoder-only usam attention bidirecional: cada token atende a todos os outros nas duas direções. Isso produz representações para tarefas de compreensão. Os modelos encoder-only padrão não são normalmente usados como decoders autorregressivos; fornecem habitualmente a arquitetura de base para modelos de embeddings, rerankers e classificadores leves (por exemplo, os routers baseados em BERT do RouteLLM).

Os modelos encoder-decoder separam a compreensão da geração. O encoder processa toda a entrada com attention bidirecional e depois o decoder gera a saída de forma autorregressiva, atendendo às representações do encoder através de cross-attention. Isto adequa-se a tarefas em que a entrada e a saída são sequências diferentes, como a tradução e o reconhecimento de fala.

Os modelos decoder-only, como GPT, Llama e Mistral, usam attention causal: cada token atende a si próprio e aos tokens anteriores. Quatro propriedades úteis para a geração são:

  • Reutilização da KV cache. Com attention causal, os tokens anteriores nunca atendem aos posteriores, pelo que as suas keys e values continuam válidas à medida que se geram novos tokens. Uma cache por sequência cresce por acréscimo (Secção 6). Um encoder-decoder mantém também o estado de cross-attention sobre a saída do encoder.
  • Treino com texto em bruto. A predição do token seguinte não precisa de pares de entrada e saída, pelo que qualquer corpus de texto serve de dados de treino.
  • Um bloco repetido. O modelo é uma pilha de camadas de decoder idênticas, sem pilha de encoder separada nem caminho de cross-attention para construir, fazer sharding e otimizar.
  • In-context learning. As instruções, os exemplos few-shot e a consulta são todos tokens num único prefixo, pelo que um só modelo pode assumir uma nova tarefa a partir do prompt.

20. Mixture of experts

O MoE substitui determinadas FFNs densas por várias FFNs de especialistas, mais um router de gating leve. O router calcula uma pontuação para cada especialista (tipicamente um softmax sobre projeções lineares aprendidas) e seleciona os top-kk especialistas por token. Apenas os especialistas ativados fazem computação, pelo que um modelo pode ter uma capacidade total enorme mantendo baixo o custo por token. Trata-se de computação condicional esparsa. Os parâmetros ativos aproximam a aritmética por token, mas todos os pesos dos especialistas precisam de armazenamento. A comunicação, o routing, a ocupação do batch e a memória KV também afetam o custo de execução. A figura segue um token através de uma camada MoE com seis especialistas encaminhados e um especialista partilhado.

Um router pontua seis especialistas encaminhados para um token e fica com os dois melhores; os especialistas 2 e 5 executam e os outros quatro ficam inativos. Um especialista partilhado executa para todos os tokens. As saídas dos especialistas 2 e 5, ponderadas pelas pontuações do router, e a saída do especialista partilhado são somadas e seguem para a camada seguinte.Um router pontua seis especialistas encaminhados para um token e fica com os dois melhores; os especialistas 2 e 5 executam e os outros quatro ficam inativos. Um especialista partilhado executa para todos os tokens. As saídas dos especialistas 2 e 5, ponderadas pelas pontuações do router, e a saída do especialista partilhado são somadas e seguem para a camada seguinte.

ModeloParâmetros totaisParâmetros ativosEspecialistas (encaminhados + partilhados)Top-kk
Mixtral 8x7B47B~13B8 + 02
DeepSeek-V3671B37B256 + 18

O especialista partilhado do DeepSeek-V3 é ativado para todos os tokens. Fornece uma representação de base sobre a qual os especialistas encaminhados se podem especializar.

O treino de MoE tem três problemas recorrentes: desequilíbrio de carga, colapso de especialistas e overhead de comunicação para o paralelismo de especialistas. Os modelos MoE tradicionais acrescentam uma perda auxiliar para penalizar o encaminhamento desequilibrado, mas essa perda pode competir com o objetivo principal. O DeepSeek-V3 usa principalmente uma estratégia ao nível do batch, sem perda auxiliar: termos de bias fora da retropropagação baixam a pontuação dos especialistas sobrecarregados e aumentam a dos subutilizados. Aplica também uma perda complementar de equilíbrio ao nível da sequência, extremamente pequena, para evitar um desequilíbrio extremo dentro de uma sequência. O artigo reporta um melhor equilíbrio do encaminhamento sem o compromisso principal da perda auxiliar na sua configuração.

21. Tokenização: BPE, SentencePiece e tiktoken

Os LLMs não veem texto. Veem sequências de IDs inteiros de tokens. Um tokenizer divide o texto em bruto em tokens (fragmentos de subpalavras) e mapeia cada um para um ID. A escolha do tokenizer afeta a qualidade do modelo, a velocidade de inferência e a equidade multilingue.

O Byte Pair Encoding (BPE) é o algoritmo comum. Funde iterativamente os pares adjacentes mais frequentes no corpus de treino. Um exemplo simplificado:

  1. Começar com um vocabulário ao nível do carácter: [l, o, w, e, r, _]
  2. O par mais frequente é (l, o) → fundir em lo → vocabulário: [l, o, w, e, r, _, lo]
  3. O par mais frequente seguinte é (lo, w) → fundir em low → o vocabulário passa a incluir low
  4. Continuar até o vocabulário atingir o tamanho alvo (por exemplo, 128K tokens)

Palavras comuns como “the” tornam-se tokens únicos, enquanto palavras raras como “defenestration” são divididas em fragmentos de subpalavras como ["def", "en", "est", "ration"] numa ilustração inventada. A divisão real depende do tokenizador e da versão. O compromisso é entre o tamanho do vocabulário e o comprimento da sequência.

Três implementações de tokenizers cobrem a maior parte do uso em produção:

  • O SentencePiece treina diretamente sobre texto Unicode em bruto, sem um pré-tokenizer específico de cada língua. Preserva os espaços em branco com o meta-símbolo ▁ e pode, opcionalmente, recorrer a tokens de bytes UTF-8. Suporta modelos BPE e unigram e é usado pelo Llama 1/2, T5 e pelos primeiros tokenizadores Mistral. Os tokenizadores Mistral Tekken mais recentes usam BPE ao estilo tiktoken.
  • O tiktoken é o tokenizer da OpenAI, escrito em Rust, que usa BPE ao nível do byte. No seu benchmark publicado com o GPT-2, correu 3–6x mais depressa do que a configuração testada de GPT2TokenizerFast. O Llama 3 passou do SentencePiece para o algoritmo do tiktoken.
  • Os Hugging Face Tokenizers são uma biblioteca amplamente usada, escrita em Rust, que suporta BPE, WordPiece e Unigram.

O tamanho do vocabulário varia muito entre famílias de modelos:

ModeloTokenizerTamanho do vocabulário
GPT-2BPE ao nível do byte50,257
Llama 2SentencePiece BPE32,000
Llama 3BPE ao estilo tiktoken128,256
GPT-4otiktoken o200k_base~200K

Numa configuração comparável de treino de tokenizadores, um vocabulário maior pode significar menos tokens para o mesmo texto, mas uma matriz de embeddings maior (o termo V×dV \times d da Secção 18).

A fertilidade mede quantos tokens um tokenizer produz por palavra ou por outra unidade de texto escolhida. Varia com o tokenizer exato, a língua, a escrita, a normalização, o domínio e a amostra. Meça-a em tráfego representativo em vez de extrapolar a partir de um único tokenizer ou língua.

22. Janelas de contexto e codificações posicionais

A janela de contexto limita a sequência de tokens disponível para um pedido de geração. O chunked prefill e o decode com cache processam apenas parte dessa sequência numa passagem. As APIs também podem definir limites separados para a entrada e a saída. Os limites publicados incluem:

ModeloLimite de tokens publicadoAno
Llama 12,0482023
Llama 3.1128K2024
GPT-4.11,047,5762025
Gemini 2.5 Pro1,048,576 de entrada2025

O GPT-4.1 também limita a saída a 32 768 tokens; o Gemini 2.5 Pro limita-a a 65 536. A linha do Gemini indica um limite de entrada, enquanto o GPT-4.1 indica uma janela de contexto, pelo que estes campos não são idênticos.

A attention compara todos os pares de tokens visíveis, mas nada nessa comparação diz a que distância estão. Sem informação posicional, a self-attention completa não distingue ordens diferentes das palavras do mesmo conjunto de tokens; os estados por token seguem a permutação da entrada. A máscara causal de um decoder dá algum sinal de ordem, porque cada token vê apenas o seu prefixo, mas não fornece posições relativas explícitas. As codificações posicionais acrescentam explicitamente a posição e a distância relativa.

Três abordagens comuns:

  • O RoPE (Rotary Position Embeddings) roda os vetores de query e de key por ângulos que dependem da posição, de modo que o seu produto escalar depende da posição relativa. O conteúdo do token continua a determinar a pontuação de attention; o RoPE acrescenta informação de posição sem embeddings de posição absoluta aprendidos. É usado por famílias de modelos abertos, incluindo o Llama, o Mistral e o Qwen.

  • O ALiBi (Attention with Linear Biases) dispensa modificações dos embeddings e soma uma penalização diretamente às pontuações de attention: quanto mais afastados estão dois tokens, maior é o bias negativo. Não tem parâmetros posicionais aprendidos. No artigo original, um modelo de 1,3B treinado com sequências de 1024 tokens teve, aos 2048 tokens, um desempenho comparável ao de um modelo de posições sinusoidais treinado a 2048. O comportamento para além dos modelos e comprimentos testados no artigo depende do modelo.

  • O YaRN (Yet another RoPE extensioN) estende um modelo RoPE para além do seu contexto de treino. Agrupa as dimensões de frequência em três categorias e escala cada uma de forma diferente. Na comparação do artigo com a mesma configuração com interpolação de posições, o YaRN usou 400 em vez de 1000 passos de treino (Apêndice B.2). A afirmação mais ampla do resumo sobre 10x menos dados usa uma comparação diferente.


Parte V — Treino e alinhamento

Esta parte distingue o objetivo que cria capacidades, as técnicas que fazem o treino caber no hardware disponível e os métodos que moldam o comportamento de um modelo depois disso.

23. Pretraining, fine-tuning e alinhamento

O pretraining de um LLM autorregressivo usa a predição autossupervisionada do token seguinte sobre um grande corpus. Os modelos de encoder podem usar outros objetivos, como a predição de tokens mascarados. A sua computação abrange muitas ordens de grandeza; o Llama 3 405B, por exemplo, usou 3.8×10253.8 \times 10^{25} FLOPs. O fine-tuning supervisionado (SFT) adapta o modelo pré-treinado a dados rotulados específicos de uma tarefa. O RLHF / RLAIF usa dados de preferências para moldar o comportamento: um pipeline RLHF convencional recolhe comparações, treina um modelo de recompensa e depois otimiza a política. O RLAIF substitui alguns juízos humanos por feedback gerado por AI. A Secção 31 trata da distillation, um caminho separado em que um professor alinhado gera dados de treino para um aluno mais pequeno.

A computação depende do tamanho do modelo, do comprimento da sequência, do volume de dados, do optimizer e do método. O PPO também carrega mais estado do modelo do que o SFT, porque uma configuração típica inclui modelos de política, de referência, de recompensa e crítico. Tratei o framework completo de decisão sobre fine-tuning no Guia de fine-tuning de LLM.

24. LoRA e QLoRA: fine-tuning eficiente em parâmetros

O LoRA congela os pesos pré-treinados e injeta matrizes de baixo rank treináveis AA (r×kr \times k) e BB (d×rd \times r), de modo que o peso atualizado é W0+BAW_0 + BA. O artigo do LoRA reduziu o GPT-3 175B para cerca de 18 milhões de parâmetros treináveis na sua configuração. O rank é um parâmetro de ajuste e não uma regra de complexidade da tarefa; escolha-o com uma varredura de qualidade e memória. Os adaptadores LoRA podem ser fundidos nos pesos base depois do treino, para evitar um caminho de adaptador separado na inferência.

O QLoRA carrega o modelo base com quantização NF4 de 4 bits enquanto treina adaptadores LoRA em BF16. O NormalFloat4 coloca mais níveis de quantização perto de zero, onde a densidade dos pesos é mais elevada. O artigo fez fine-tuning de um modelo de 65B numa única GPU de 48 GB e reportou resultados próximos das suas referências de 16 bits. Os compromissos de runtime e de memória são específicos da stack testada.

25. Treino com precisão mista

Todos os formatos de vírgula flutuante distribuem os seus bits por três campos: sinal (sempre 1 bit), expoente (define o intervalo dinâmico) e mantissa (define a precisão). Mais bits de expoente significam um intervalo mais largo de magnitudes representáveis; mais bits de mantissa significam distinções mais finas entre valores próximos. Os formatos inteiros não têm expoente e representam apenas números inteiros uniformemente espaçados dentro de um intervalo fixo.

FormatoBitsLayout (S / E / M)IntervaloPrecisãoUso comum
FP32321 / 8 / 23±3.4×1038\pm 3.4 \times 10^{38}~7 dígitos decimaisPesos mestre, estados do optimizer (momento e variância do Adam)
BF16161 / 8 / 7±3.4×1038\pm 3.4 \times 10^{38}~2 dígitos decimaisFormato de treino preferido; mesmo intervalo do FP32 e, normalmente, sem loss scaling
FP16161 / 5 / 10±65,504\pm 65{,}504~3 dígitos decimaisTreino com loss scaling (GPUs mais antigas); inferência em hardware anterior à Hopper
FP8 E4M381 / 4 / 3±448\pm 448~1 dígito decimalPassagem forward na Hopper (H100) — mais precisão para pesos e ativações
FP8 E5M281 / 5 / 2±57,344\pm 57{,}344~0,6 dígitos decimaisPassagem backward na Hopper — intervalo mais largo para os gradientes
INT88inteiro com sinal−128-128 a 127127Inteiros exatosQuantização de pesos pós-treino para inferência (W8A8); quantização da KV cache
INT44inteiro com sinal−8-8 a 77Inteiros exatosQuantização agressiva só de pesos (AWQ, GPTQ) para inferência em hardware com pouca memória

O BF16 tem o mesmo intervalo do FP32 porque o intervalo é definido pelo campo do expoente, e o BF16 mantém os 8 bits de expoente do FP32. Cede, em vez disso, bits de mantissa (7 contra 23), trocando precisão por uma redução de 2x na memória e evitando muitos dos problemas de intervalo que afetam o treino em FP16. O FP16 tem apenas 5 bits de expoente, o que limita o seu intervalo finito a cerca de 65K. Muitos gradientes são, pelo contrário, demasiado pequenos para o FP16 e sofrem underflow para zero. O loss scaling multiplica a loss antes da retropropagação para que esses gradientes continuem representáveis e depois desfaz a escala antes do passo do optimizer; o scaling dinâmico reduz o fator se ocorrer overflow. O intervalo de expoente mais largo do BF16 evita normalmente este requisito.

Os formatos inteiros são pouco comuns na aritmética principal do treino porque a retropropagação precisa de um intervalo dinâmico largo. São muito usados na inferência, onde os pesos congelados podem ser mapeados para escalas calibradas. A quantização de pesos em INT4 reduz um modelo de 7B de cerca de 14 GB para 3,5 GB antes do overhead do runtime; a qualidade tem de ser medida para o modelo e o método escolhidos.

O treino em FP8 na H100 através do Transformer Engine usa E4M3 onde a precisão importa e E5M2 onde importa um intervalo mais largo. O artigo do FP8-LM reporta que o seu framework de precisão mista treinou o GPT-175B 75% mais depressa do que a sua referência BF16 com Megatron-LM e 37% mais depressa do que o NVIDIA Transformer Engine na configuração H100 testada. O DeepSeek-V3 usou precisão mista FP8 e reportou cerca de $5.6 milhões em computação equivalente ao aluguer para a sua execução final de treino, excluindo I&D e infraestrutura.

26. Gradient checkpointing

Cada camada da passagem forward produz uma saída intermédia chamada ativação:

Input→[Layer 1]→activation1→[Layer 2]→activation2→[Layer 3]→output\text{Input} \rightarrow [\text{Layer 1}] \rightarrow \text{activation}_1 \rightarrow [\text{Layer 2}] \rightarrow \text{activation}_2 \rightarrow [\text{Layer 3}] \rightarrow \text{output}

Normalmente todas as ativações têm de ficar em memória porque a retropropagação precisa delas para calcular os gradientes. Num transformer profundo, as ativações guardadas podem ocupar mais memória do que os próprios pesos do modelo.

O gradient checkpointing troca computação por memória, descartando a maioria dessas ativações e recalculando-as em tempo real durante o backprop. A estratégia padrão (Chen et al., 2016) divide uma rede de nn camadas em n\sqrt{n} segmentos uniformemente espaçados e guarda apenas a ativação de fronteira de cada segmento. Essas fronteiras guardadas são os “checkpoints”. Todas as ativações intermédias dentro de um segmento são descartadas de imediato.

Quando a passagem backward chega a uma camada dentro de um segmento, as suas ativações são recalculadas a partir do checkpoint mais próximo. Na estratégia de segmentos uniformes, a memória de ativações guardadas desce de O(n)O(n) para O(n)O(\sqrt{n}). Cada segmento é recalculado uma vez, o que custa cerca de uma passagem forward adicional por passo de treino (Chen et al., 2016). A poupança real de memória e o overhead de recomputação dependem do modelo, das fronteiras dos checkpoints, do comprimento da sequência, do framework e da implementação, por isso meça ambos na execução de treino alvo. O FlashAttention aplica o mesmo princípio dentro da attention, ao não materializar a matriz de attention completa. Nas execuções normais do Hugging Face Trainer, ative-o com gradient_checkpointing=True. Com FSDP, use a integração de activation checkpointing do framework, quando suportada; a opção genérica pode causar all-gathers redundantes na passagem backward.

27. Fases do DeepSpeed ZeRO

No paralelismo de dados padrão, cada GPU guarda uma cópia completa dos pesos do modelo, dos gradientes e dos estados do optimizer. No Adam, cada parâmetro ocupa 2 bytes para o peso FP16 + 4 bytes para o peso mestre FP32 + 4 bytes para o momento + 4 bytes para a variância + 2 bytes para o gradiente, ou seja, 16 bytes por parâmetro. Um modelo de 7,5B parâmetros precisa de ~120 GB por GPU, e todas as GPUs guardam o mesmo. Com 64 GPUs, são 64 cópias idênticas de 120 GB.

O DeepSpeed ZeRO (Zero Redundancy Optimizer) elimina esta duplicação fazendo sharding destes componentes entre as GPUs em vez de os replicar:

  • Fase 1 — particionar os estados do optimizer. Cada GPU guarda apenas 1/N dos estados do optimizer (os pesos mestre FP32 mais o primeiro e o segundo momentos do Adam, 12 bytes/parâmetro). Quando uma GPU precisa de atualizar um peso, atualiza apenas a sua fatia e difunde o resultado. Sob os pressupostos abaixo, a memória desce de ~120 GB para ~41,3 GB por GPU.
  • Fase 2 — particionar também os gradientes. Os gradientes (2 bytes/parâmetro) deixam de sofrer all-reduce para todas as GPUs. Cada GPU recebe apenas a fatia de gradiente de que precisa através de reduce-scatter. Sob os mesmos pressupostos, a memória desce para ~28,1 GB por GPU.
  • Fase 3 — particionar também os pesos do modelo. Cada GPU guarda apenas 1/N dos pesos FP16. Antes da passagem forward ou backward de cada camada, a GPU chama all-gather para reconstruir temporariamente os pesos completos da camada a partir de todas as outras GPUs, calcula e descarta os pesos reunidos. Sob os mesmos pressupostos, a memória desce para ~15,0 GB por GPU.

A figura desenha, à escala, o estado do modelo por GPU em cada fase, com o custo de comunicação de cada fase.

Barras empilhadas à escala para 7,5B parâmetros em 8 GPUs. Sem ZeRO, cada GPU guarda 90 GB de estados do optimizer, 15 GB de gradientes e 15 GB de pesos, 120 GB. A fase 1 particiona os estados do optimizer, ficando com 41,3 GB, a fase 2 também os gradientes, com 28,1 GB, e a fase 3 também os pesos, com 15,0 GB, ao custo de um all-gather antes de cada camada.Barras empilhadas à escala para 7,5B parâmetros em 8 GPUs. Sem ZeRO, cada GPU guarda 90 GB de estados do optimizer, 15 GB de gradientes e 15 GB de pesos, 120 GB. A fase 1 particiona os estados do optimizer, ficando com 41,3 GB, a fase 2 também os gradientes, com 28,1 GB, e a fase 3 também os pesos, com 15,0 GB, ao custo de um all-gather antes de cada camada.

Estes são valores aproximados de estado do modelo para um modelo de 7,5B parâmetros em 8 GPUs (world size N=8N=8), com pesos e gradientes em FP16 e pesos mestre, momento e variância do Adam em FP32. Excluem ativações, buffers temporários de all-gather, fragmentação do alocador e overhead do framework/runtime. O limite do cálculo é 7.5B×7.5B \times bytes por parâmetro, com cada estado dividido por NN apenas nas fases que o particionam.

Para repetir o cálculo com outro tamanho de modelo ou número de GPUs:

def zero_model_state_gb(params_b, n_gpus, stage):
    """Model-state GB per GPU for Adam with FP16 weights and gradients.

    Optimizer states (FP32 master weights, momentum, variance) take 12 bytes
    per parameter, gradients 2, and weights 2. Stage 1 shards the optimizer
    states, stage 2 also the gradients, and stage 3 also the weights.
    """
    optimizer, gradients, weights = 12, 2, 2
    if stage >= 1:
        optimizer /= n_gpus
    if stage >= 2:
        gradients /= n_gpus
    if stage >= 3:
        weights /= n_gpus
    return params_b * (optimizer + gradients + weights)

for stage in range(4):
    print(f"stage {stage}: {zero_model_state_gb(7.5, 8, stage):g} GB per GPU")

assert [zero_model_state_gb(7.5, 8, s) for s in range(4)] == [120.0, 41.25, 28.125, 15.0]
# 70B parameters: Stage 3 on 8 GPUs still needs 140 GB of model state per GPU;
# 16 GPUs bring it to 70 GB, before activations.
assert zero_model_state_gb(70, 8, 3) == 140.0
assert zero_model_state_gb(70, 16, 3) == 70.0

O compromisso é a comunicação. A Fase 1 acrescenta um overhead mínimo e a Fase 2 substitui o all-reduce por reduce-scatter com custo semelhante. A Fase 3 precisa de chamadas all-gather antes de cada camada, tanto na passagem forward como na backward, o que dá aproximadamente 1,5x o volume de comunicação do paralelismo de dados padrão.

O ZeRO-Infinity estende a Fase 3 com o offloading dos estados particionados para a RAM do CPU e até para SSDs NVMe, o que pode tornar possível treinar modelos com biliões de parâmetros em clusters de GPUs limitados. O offload para armazenamento acrescenta custo de PCIe e de transferência de armazenamento; o seu impacto medido depende do disco, da topologia PCIe, do particionamento, do prefetch, da sobreposição das transferências e da carga de trabalho. Use-o para satisfazer requisitos de capacidade em vez de assumir uma lentidão fixa, e faça profiling da configuração alvo.

28. FSDP: sharding nativo do PyTorch

O Fully Sharded Data Parallel (FSDP) é a resposta incorporada do PyTorch ao DeepSpeed ZeRO-3. Faz sharding dos parâmetros, dos gradientes e dos estados do optimizer entre as GPUs, com a mesma ideia central. Com sharding completo e resharding ativados, as operações para cada unidade envolvida são:

  1. All-gather dos parâmetros completos de todas as GPUs (reconstruir temporariamente a camada completa).
  2. Calcular a passagem forward ou backward dessa camada.
  3. Libertar de imediato os parâmetros reunidos. Cada GPU guarda apenas o seu próprio shard.
  4. Reduce-scatter dos gradientes para que cada GPU receba apenas a fatia de gradiente que lhe foi atribuída.

Como o FSDP é nativo do PyTorch, integra-se diretamente com as ferramentas de debugging e os profilers do PyTorch e com o torch.compile. O desempenho relativamente ao DeepSpeed ZeRO-3 depende da política de wrapping, da topologia de comunicação, das definições de offload e do tamanho do modelo, por isso compare-os no mesmo cluster.

CritérioFSDP (PyTorch)DeepSpeed ZeRO
Estilo de controloSharding completo através das APIs do PyTorchFases do ZeRO selecionáveis
OffloadingOffloading para CPUCPU + NVMe com ZeRO-Infinity
Integração com o frameworkPyTorch nativo, caminhos torch.compileBiblioteca e sistema de configuração separados
Teste de seleçãoFaça profiling da carga PyTorch alvoFaça profiling das funcionalidades e do offload necessários

O FSDP2 (2024–2025) é uma reescrita que melhora a integração com o torch.compile para uma melhor fusão de kernels, acrescenta suporte a treino em FP8 através do TorchAO e simplifica a API. Tanto o FSDP como o DeepSpeed são acessíveis através do Hugging Face Accelerate, que suporta ambos através de configuração para scripts de treino compatíveis e preparados. As definições necessárias de wrapping, checkpointing e offloading continuam a ser diferentes.

29. Leis de escala e custo de inferência

O scaling Chinchilla (DeepMind, 2022) encontrou uma alocação ótima de computação perto de 20 tokens de treino por parâmetro, sob os seus pressupostos. Esse objetivo não inclui o custo de serving a jusante. Se um modelo mais pequeno treinado com mais dados atingir a qualidade exigida, pode custar menos ao longo de um ciclo de vida de inferência de alto volume.

Uma estratégia de custo do ciclo de vida é treinar um modelo mais pequeno com muito mais dados:

ModeloParâmetrosTokens de treinoTokens/parâmetroTokens/parâmetro ÷ 20 (derivado)
Chinchilla70B1.4T20:11×
Llama 165B1.4T22:11×
Llama 270B2.0T29:11.4×
Llama 3 8B8B15T1,875:194×
Qwen3-0.6B0.6B36T60,000:13,000×

Este é o rácio de tokens por parâmetro apresentado, dividido pelo ponto aproximado de 20 tokens por parâmetro do artigo do Chinchilla. É um rácio descritivo, não um multiplicador medido de qualidade ou de custo.

Para um modelo servido em grande volume, gastar mais computação de treino num modelo mais pequeno pode reduzir o custo do ciclo de vida. O Llama 3 8B ilustra a estratégia, mas saber se compensa depende da qualidade exigida e do volume de inferência previsto. “Chinchilla-optimal” refere-se à eficiência da computação de treino, que é um objetivo diferente do custo do ciclo de vida.

30. RLHF, DPO, GRPO e o panorama do alinhamento

O alinhamento orienta um modelo pré-treinado para as instruções, preferências e políticas de segurança desejadas. Por si só, não garante veracidade nem comportamento seguro. Os métodos abaixo trocam complexidade de implementação, requisitos de dados, exploração e estabilidade do treino.

O pipeline RLHF clássico: SFT → recolher pares de preferências humanas → treinar um modelo de recompensa com esses pares → fazer fine-tuning da política com PPO (Proximal Policy Optimization). Uma configuração convencional de PPO tem quatro papéis de modelos: política, referência, crítico/modelo de valor e modelo de recompensa. O seu uso de memória depende da partilha, do sharding, da precisão, do offloading e da colocação; quatro papéis não implicam quatro cópias completas residentes na GPU. O PPO também é sensível aos hiperparâmetros. Também é propenso a reward hacking, em que o modelo explora peculiaridades do modelo de recompensa, como respostas verbosas e com ar confiante, em vez de melhorar genuinamente a qualidade.

O DPO (Direct Preference Optimization) dispensa o modelo de recompensa aprendido e o ciclo de RL online, otimizando diretamente uma loss sobre pares de preferências. Isto simplifica o pipeline de treino. O DPO padrão é offline: treina com um dataset fixo e não explora novas respostas durante o ciclo de atualização. Saber se essa limitação importa depende da tarefa e da cobertura dos dados.

O GRPO (Group Relative Policy Optimization, DeepSeek) remove o crítico aprendido do PPO, gerando várias completions por prompt e usando as recompensas relativas ao grupo como referência. Isto reduz o peso do estado do modelo face a uma configuração típica de PPO. Ao contrário do DPO, o GRPO é on-policy: o modelo gera respostas novas durante o treino. O DeepSeek-R1 combina o GRPO com RLVR (reinforcement learning from verifiable rewards), usando verificações como respostas a problemas de matemática, compilação de código e testes unitários. Estas recompensas são mais fáceis de auditar do que uma pontuação de preferência aprendida, mas testes incompletos e objetivos proxy continuam a poder ser explorados.

MétodoPapéis dos modelos numa configuração convencionalSinal de recompensaOnline/offlineLimitação principal
PPOPolítica, referência, crítico, recompensaModelo de recompensa aprendidoOnlineReward hacking, ajuste complexo
DPOPolítica e pontuações de referência, que podem ser pré-calculadasImplícito (pares de preferências)OfflineSem exploração, dados fixos
GRPOPolítica; referência opcional para KL e modelo de recompensa aprendido opcionalExplícito (regras/verificador ou aprendido)OnlineDepende da qualidade da recompensa e de variação informativa dentro do grupo

A política de referência no GRPO depende da implementação. Por exemplo, o TRL GRPOTrainer não carrega um modelo de referência quando o seu coeficiente KL beta é zero. Inclua a colocação dos modelos e a precisão ao comparar a memória de treino.

31. Distillation: comprimir conhecimento entre modelos

A distillation de conhecimento transfere capacidades de um professor grande para um aluno mais pequeno. A distillation baseada em logits treina o aluno para reproduzir a distribuição de saída do professor. A distillation baseada em dados faz o professor gerar exemplos com os quais o aluno é afinado. Os métodos baseados em dados são comuns em LLMs porque podem funcionar entre arquiteturas diferentes e com professores acessíveis só por API, mas o seu valor é limitado pela qualidade do professor, pela cobertura dos dados, pela filtragem e pelo custo de geração.

O DeepSeek-R1 selecionou uma mistura de aproximadamente 800 000 exemplos — cerca de 600 000 amostras relacionadas com raciocínio e 200 000 sem raciocínio — e usou-a para fazer distillation de modelos Qwen2.5 e Llama 3 de 1,5B a 70B parâmetros. Na avaliação do artigo:

  • O DeepSeek-R1-Distill-Qwen-32B obtém 72,6% no AIME 2024 e 94,3% no MATH-500, acima dos números do OpenAI o1-mini reportados pelo artigo.
  • O DeepSeek-R1-Distill-Qwen-7B obtém 55,5% no AIME 2024, acima do resultado do QwQ-32B-Preview reportado pelo artigo, com um modelo mais pequeno.

Nas experiências com modelos pequenos do DeepSeek-R1, a distillation superou o GRPO direto nos modelos base testados. Esse resultado apoia a distillation nesta configuração; não estabelece uma classificação universal entre distillation e RL.

32. Geração de dados sintéticos

Os dados de treino gerados por LLMs são usados em vários padrões recorrentes:

  • O Self-Instruct parte de um pequeno conjunto de sementes de instruções escritas por humanos: o LLM gera novas instruções, entradas e saídas, que são filtradas e devolvidas ao conjunto. O projeto Alpaca usou 52 000 exemplos gerados a partir de 175 sementes. A Stanford reportou uma geração de dados abaixo de $500 e um fine-tuning abaixo de $100, o que põe o custo da reprodução inicial abaixo de $600; a comparação com o GPT-3.5 foi uma avaliação limitada do projeto, não uma equivalência ampla.
  • O Evol-Instruct (WizardLM) pega em instruções existentes e evolui-as iterativamente ao longo de eixos de complexidade (acrescentando restrições, aprofundando o raciocínio, tornando os problemas mais concretos) para produzir exemplos de treino progressivamente mais difíceis.
  • O Phi-4 (14B) da Microsoft usou dados sintéticos para grande parte do pretraining, incluindo geração, crítica, autorrevisão e inversão de instruções. O seu relatório técnico compara o desempenho resultante em STEM e em programação com o de modelos maiores nos benchmarks selecionados.

Um dos riscos é o colapso do modelo quando os dados reais são substituídos recursivamente por dados gerados. Nas configurações estudadas, as partes raras da distribuição original desaparecem progressivamente (Shumailov et al., publicado na Nature em 2024). Este não é um resultado inevitável de todo o uso de dados sintéticos. Um estudo de acumulação separado encontrou um comportamento diferente quando os dados reais e gerados eram mantidos entre gerações. Um estudo de classificador da Ahrefs recolheu uma página inglesa recém-detetada por domínio, de entre 900 000 páginas em abril de 2025, e classificou 74,2% como contendo algum texto gerado por AI. Esse estudo de um fornecedor não é um censo da web. A mitigação começa por misturar dados sintéticos e reais, filtrar e rastrear a linhagem, para que o material gerado recursivamente possa ser medido.


Parte VI — Escalonamento e deployment

Quando uma carga de trabalho deixa de caber, ou de cumprir o seu SLO, num único dispositivo, as escolhas são como dividir o trabalho, que runtime expõe os controlos necessários e se todos os pedidos precisam do mesmo modelo.

33. Quatro formas de paralelismo

As quatro formas diferem na parte do modelo que cada GPU guarda e no que atravessa a ligação entre GPUs. A figura mostra cada uma em duas GPUs.

Quatro cartões, cada um com GPU 0 e GPU 1 e um modelo de quatro camadas. Paralelismo de dados: ambas guardam o modelo completo e nada atravessa a ligação por pedido. Paralelismo de tensores: cada uma guarda metade de cada matriz de pesos e fazem all-reduce depois de cada camada. Paralelismo de pipeline: a GPU 0 guarda as camadas 1 e 2 e a GPU 1 as camadas 3 e 4, e as ativações atravessam na fronteira. Paralelismo de especialistas: cada uma guarda metade dos especialistas e os tokens movem-se em all-to-all. Cada cartão indica também quando começar por ele.Quatro cartões, cada um com GPU 0 e GPU 1 e um modelo de quatro camadas. Paralelismo de dados: ambas guardam o modelo completo e nada atravessa a ligação por pedido. Paralelismo de tensores: cada uma guarda metade de cada matriz de pesos e fazem all-reduce depois de cada camada. Paralelismo de pipeline: a GPU 0 guarda as camadas 1 e 2 e a GPU 1 as camadas 3 e 4, e as ativações atravessam na fronteira. Paralelismo de especialistas: cada uma guarda metade dos especialistas e os tokens movem-se em all-to-all. Cada cartão indica também quando começar por ele.

O Paralelismo de Tensores (TP) divide matrizes de pesos individuais entre GPUs e comunica normalmente após cada camada. Ligações rápidas dentro do nó, como o NVLink, tornam-no mais prático dentro de um nó. Mais shards reduzem a memória e a computação por dispositivo, mas aumentam a comunicação, por isso escolha o grau com um benchmark de latência.

O Paralelismo de Pipeline (PP) divide as camadas sequencialmente entre GPUs, passando ativações entre fases. O seu padrão de comunicação pode funcionar entre nós, mas as bolhas do pipeline e os tempos desiguais das fases reduzem a utilização. As implementações grandes combinam muitas vezes TP dentro de um nó e PP entre nós.

O Paralelismo de Dados (DP) replica o modelo de serving, de modo que cada réplica trata pedidos independentes sem comunicação entre réplicas por pedido. É eficiente quando o modelo cabe e o tráfego pode ser equilibrado. No treino, o DP é habitualmente combinado com ZeRO ou FSDP para fazer sharding do estado.

O Paralelismo de Especialistas (EP) distribui os especialistas do MoE pelas GPUs, usando comunicação all-to-all para o encaminhamento de tokens. O seu desempenho depende do equilíbrio de tokens, da colocação dos especialistas e da topologia da interligação; o tráfego all-to-all pode tornar-se o bottleneck dominante.

Uma heurística inicial de paralelismo:

  • O modelo cabe numa GPU: comece com réplicas independentes e meça o escalonamento do DP.
  • O modelo cabe num nó: teste TP dentro do nó e depois replique o grupo se o tráfego o exigir.
  • O modelo abrange vários nós: teste uma combinação de TP e PP face à interligação e ao objetivo de latência.
  • Mixture of experts: acrescente EP apenas quando a colocação dos especialistas o exigir.

34. Comparação de frameworks de serving

O runtime decide quais das otimizações da Parte III obtém sem escrever kernels. Use a tabela para escolher o primeiro a testar e depois compare-o com uma alternativa nos mesmos prompts, comprimentos de saída, hardware e SLO.

RuntimeUse-o quandoVerifique primeiro
vLLMServing geral em GPU com uma API compatível com a OpenAIO modelo e a GPU alvo na matriz de compatibilidade atual
SGLangO tráfego partilha prefixos longos (system prompts, chat com vários turnos, agentes) ou precisa de structured outputA taxa de acertos da prefix cache no seu próprio tráfego
TensorRT-LLMA frota é só NVIDIA e a latência mais baixa importa mais do que a portabilidadeSuporte para o seu modelo e precisão (FP8, FP4)
llama.cppCPU, Apple silicon ou uma única GPU de consumo, com modelos quantizados em GGUFTokens por segundo da sua ferramenta de benchmark na máquina alvo
OllamaDesenvolvimento local e demosPasse para um runtime de serving antes de trabalhar com alta concorrência ou SLOs

Os mesmos pontos de partida para benchmarks, numa lista ordenada:

  1. Uma máquina local ou uma GPU de consumo: comece com o llama.cpp, ou o Ollama para desenvolvimento.
  2. Só NVIDIA e a latência é a prioridade: inclua o TensorRT-LLM no benchmark.
  3. Prefixos partilhados longos ou structured output: SGLang.
  4. Caso contrário: vLLM.

Faça o benchmark da primeira escolha com os seus prompts, comprimentos de saída, hardware e SLO.

O vLLM oferece alocação paginada da KV, batching contínuo, uma API compatível com a OpenAI e vários modos de paralelismo. O suporte a modelos e hardware muda com frequência.

O SGLang combina o RadixAttention para reutilização de prefixos, um scheduler personalizado e structured output. Os ganhos de throughput que publica dependem da carga de trabalho e da configuração.

TensorRT-LLM visa baixa latência por pedido através da captura e reprodução de grafos CUDA, que reduzem o overhead de lançamento, e da otimização de kernels, incluindo fusão, com suporte nativo para FP8/FP4. Versões anteriores, como a v1.2.0, permitiam construir antecipadamente um motor TensorRT. O guia de migração atual da NVIDIA define PyTorch como único backend: a API LLM e trtllm-serve carregam diretamente um checkpoint Hugging Face, sem construir um motor. Os números publicados são específicos do hardware e do modelo, e o TensorRT-LLM funciona apenas em GPUs NVIDIA.

O TGI foi arquivado em 21 de março de 2026 e o seu repositório é apenas de leitura. O seu README recomenda o vLLM, o SGLang, o llama.cpp ou o MLX para novas implementações.

O Ollama dá ênfase a um fluxo de trabalho local simples com modelos.

O llama.cpp é um runtime portátil em C/C++ com caminhos para ARM, x86, Metal, CUDA, ROCm e Vulkan. O GGUF suporta vários níveis de quantização. O desempenho varia muito com o modelo, a quantização, o contexto e o backend.

35. Seleção de GPUs para inferência

A tabela compara características de hardware publicadas. O suporte de precisão do fornecedor não torna os valores de pico de computação diretamente comparáveis entre formatos, por isso escolha primeiro pelo ajuste da memória e faça benchmark da carga alvo. Verifique separadamente os preços atuais da cloud, porque variam com o fornecedor, a região, o compromisso e a disponibilidade.

GPUMemóriaLargura de bandaBaixa precisão nativaNVLink por GPU
B200 SXM180 GB HBM3eAté 8 TB/sFP4, FP81,8 TB/s
H200 SXM141 GB HBM3e4,8 TB/sFP8900 GB/s
H100 SXM80 GB HBM33,35 TB/sFP8900 GB/s
A100 80 GB SXM80 GB HBM2e2,039 TB/sINT8 (sem FP8)600 GB/s
L40S48 GB GDDR6864 GB/sFP8Nenhum
A10G (AWS G5)24 GB GDDR6600 GB/sINT8 (sem FP8)Nenhum
RTX 409024 GB GDDR6X~1 TB/sFP8Nenhum

A linha da A10G usa a especificação da A10 da NVIDIA como aproximação da largura de banda; a especificação AWS G5 ligada não estabelece a largura de banda exata da A10G. O valor da RTX 4090 (1008 GB/s) vem do whitepaper da arquitetura Ada.

A capacidade de 141 GB da H200 pode simplificar algumas implementações de modelos grandes. As placas GDDR das linhas inferiores têm um terço ou menos da largura de banda da H100 e não têm NVLink, pelo que se adequam melhor a modelos quantizados que cabem numa única GPU do que a modelos que precisam de paralelismo de tensores.

O AWQ e o GPTQ servem modelos de 4 bits desquantizando operações matriciais suportadas para um formato de computação como o FP16 ou o BF16. A compatibilidade e a velocidade continuam a depender da arquitetura do modelo, do formato de quantização, do backend de serving, do kernel e da GPU, por isso consulte a matriz de suporte do backend e faça benchmark do artefacto exato. Todas as GPUs listadas suportam operações matriciais INT8.

O decode de LLMs é muitas vezes limitado pela largura de banda de memória, pelo que a capacidade e a largura de banda da HBM podem importar mais do que o pico de TFLOPS nas cargas de serving. Compare as GPUs mantendo constantes o modelo, a precisão, a distribuição dos batches, o comprimento do contexto e o objetivo de latência.

36. Cascading e routing de modelos

O routing de modelos escolhe que LLM trata cada consulta com base na complexidade ou capacidade prevista. O RouteLLM (LMSYS/UC Berkeley, ICLR 2025) reporta uma razão de custos de 3,66x a 95% da pontuação de referência do GPT-4-Turbo no MT-Bench, na Tabela 6. Isto corresponde a um custo modelado cerca de 73% inferior com os preços, o modelo fraco e a configuração de pontuação dessa experiência. Saber se o routing compensa depende dos preços atuais, da mistura de tráfego, dos erros do router e do nível mínimo de qualidade.

Os routers vão de classificadores leves a juízes baseados em LLMs. O cascading é a variante sequencial: uma consulta começa com um modelo mais barato e escala quando uma função de pontuação rejeita a resposta. O FrugalGPT reporta até 98% menos custo ou até 4% mais exatidão no seu conjunto de modelos avaliado. Uma cascata em produção precisa de critérios de escalada calibrados e de monitorização das consultas que o modelo barato aceita incorretamente. A figura mostra onde cada abordagem faz a sua escolha e onde cada uma falha.

Dois painéis. Routing: um router envia as consultas difíceis para o modelo forte e as fáceis para o modelo barato, pelo que corre um só modelo; um erro do router envia uma consulta difícil para o modelo barato. Cascading: o modelo barato responde sempre primeiro; se a pontuação atingir o limiar, a sua resposta é devolvida, caso contrário a consulta escala para o modelo forte. As consultas escaladas pagam duas chamadas, e uma falsa aceitação deixa passar uma resposta errada do modelo barato.Dois painéis. Routing: um router envia as consultas difíceis para o modelo forte e as fáceis para o modelo barato, pelo que corre um só modelo; um erro do router envia uma consulta difícil para o modelo barato. Cascading: o modelo barato responde sempre primeiro; se a pontuação atingir o limiar, a sua resposta é devolvida, caso contrário a consulta escala para o modelo forte. As consultas escaladas pagam duas chamadas, e uma falsa aceitação deixa passar uma resposta errada do modelo barato.


Parte VII — Aplicações

As aplicações falham em locais que a stack de serving não vê. A recuperação pode falhar antes de a geração começar, os agentes podem escolher uma ação inválida e uma alteração a um prompt pode melhorar uma tarefa e estragar outra.

37. Modelos de embeddings vs modelos generativos

Os modelos de embeddings codificam texto em vetores de dimensão fixa que captam o significado semântico. Os modelos de vetor único comparados abaixo produzem um vetor denso por entrada, normalmente com algumas centenas a vários milhares de dimensões. Outras arquiteturas de recuperação podem guardar vários vetores ao nível do token; a comparação de armazenamento seguinte refere-se a embeddings de vetor único. As suas bases incluem transformers encoder-only bidirecionais e modelos derivados de decoders adaptados à aprendizagem de representações. Uma camada de pooling reduz muitas vezes as representações por token a um único vetor, através de mean pooling, de um token especial de classificação ou de um método específico do modelo baseado no último token. O fine-tuning contrastivo aproxima depois os textos semanticamente semelhantes e afasta os dissimilares.

Os modelos de embeddings atuais diferem nas propriedades que determinam o custo de armazenamento e a escolha do índice: dimensões de saída, tipos de entrada aceites, forma de disponibilização e precisão de saída.

ModeloDimensões máximasEntradaPesosPrecisão de saída nativa
Qwen3-Embedding-8B4,096 (32–4,096 via MRL)TextoAbertos (Apache 2.0)Vírgula flutuante
Gemini Embedding 23,072 (128–3,072)Texto, imagem, vídeo, áudio, PDFAPINão indicada
pplx-embed-v1-4B2,560 (MRL)TextoAbertos (MIT)INT8 e binária
OpenAI text-embedding-3-large3,072 (reduzidas via dimensions)TextoAPIVírgula flutuante

Estes são exemplos, não uma classificação. Avalie língua, modalidade, tarefa, dimensão e custo de serving num único conjunto de recuperação.

O Matryoshka Representation Learning (MRL, Kusupati et al., NeurIPS 2022) torna flexíveis as dimensões dos embeddings. Com o nome das bonecas russas encaixáveis, o MRL treina prefixos selecionados dos embeddings para reter informação útil. As experiências comparam esses prefixos com representações mais pequenas treinadas separadamente; igualar a sua qualidade é um resultado empírico, não uma garantia para todas as tarefas. Durante o treino, o MRL agrega as losses sobre um conjunto escolhido de O(log⁡d)O(\log d) dimensões de prefixo, normalmente reduções sucessivas a metade; o exemplo de 2048 dimensões do artigo usa {8,16,…,1024,2048}\{8, 16, \ldots, 1024, 2048\}. A loss agregada leva as primeiras dimensões a transportar informação semântica grosseira, com as dimensões seguintes a acrescentarem detalhe mais fino.

Depois do treino, um embedding MRL pode ser truncado para uma dimensão de prefixo suportada. A OpenAI reporta que o text-embedding-3-large com 256 dimensões supera o text-embedding-ada-002 com 1536 dimensões na comparação MTEB que cita. Isso dá uma redução de 6x no armazenamento bruto dos vetores; a latência da pesquisa e o custo da base de dados dependem também do índice, dos metadados, da filtragem e do hardware.

O modelo de embeddings é um componente importante de um pipeline de RAG, a par do parsing, do chunking, da pesquisa, do reranking e da geração. Se a evidência relevante não for recuperada, um gerador mais forte não consegue recuperá-la de forma fiável.

38. Arquitetura de RAG em produção

A Retrieval-Augmented Generation fornece a um LLM documentos recuperados no momento da consulta. Pode fornecer evidência atual ou privada que está ausente dos pesos do modelo, mas a recuperação não garante que a resposta use essa evidência corretamente. Um sistema RAG em produção é um pipeline com várias fases, que precisam de avaliação separada. A figura mostra o caminho de ingestão offline, o caminho por consulta e onde ocorrem os três modos de falha listados abaixo.

Arquitetura de RAG. Offline: fazer parsing e chunking dos documentos, gerar o embedding de cada chunk e guardar os vetores num índice. Por consulta: reescrever opcionalmente a pergunta com HyDE, gerar o seu embedding com o mesmo modelo, executar uma pesquisa híbrida densa e BM25 sobre o índice, fazer reranking dos candidatos e fazer o LLM responder a partir dos principais chunks. Marcas numeradas situam a falha de recuperação na pesquisa, e o context poisoning e o lost-in-the-middle na entrada do LLM.Arquitetura de RAG. Offline: fazer parsing e chunking dos documentos, gerar o embedding de cada chunk e guardar os vetores num índice. Por consulta: reescrever opcionalmente a pergunta com HyDE, gerar o seu embedding com o mesmo modelo, executar uma pesquisa híbrida densa e BM25 sobre o índice, fazer reranking dos candidatos e fazer o LLM responder a partir dos principais chunks. Marcas numeradas situam a falha de recuperação na pesquisa, e o context poisoning e o lost-in-the-middle na entrada do LLM.

O pipeline de ingestão corre offline. Os documentos em bruto (PDFs, HTML, Markdown, bases de dados) são primeiro convertidos em texto limpo. O parsing de PDFs pode perder tabelas, cabeçalhos e formatação. O texto é depois dividido em chunks, cujos embeddings são gerados e indexados de forma independente.

O chunking afeta tanto o recall da recuperação como o contexto disponível para o gerador. Os tamanhos úteis dependem da estrutura do documento, da granularidade das consultas, dos limites do modelo de embeddings e dos limites do reranker. As abordagens comuns são tamanho fixo com sobreposição, divisão recursiva ao longo das fronteiras do documento e chunking semântico por similaridade de embeddings. Compare-as com etiquetas de relevância ao nível da página ou da secção em vez de adotar universalmente um único intervalo de tokens.

Cada chunk tem depois o seu embedding gerado com um modelo como os da Secção 37 e é guardado num índice vetorial. O pgvector mantém os vetores numa base de dados Postgres que já tenha em execução. Um armazenamento dedicado, como o Qdrant, o Weaviate ou o Pinecone, acrescenta pesquisa híbrida integrada e escala o índice separadamente da base de dados da aplicação.

O pipeline de recuperação corre no momento da consulta. Comece com uma baseline mensurável e acrescente fases quando a análise de erros mostrar que respondem a uma falha real:

  • A pesquisa híbrida combina a recuperação vetorial densa com uma recuperação esparsa como o BM25, muitas vezes fundidas por Reciprocal Rank Fusion (RRF). A pesquisa densa trata das paráfrases semânticas; a esparsa apanha identificadores exatos, códigos de erro e acrónimos. Os benchmarks de fornecedores reportam ganhos face às baselines só vetoriais, mas a dimensão depende do corpus e das etiquetas de relevância.
  • O reranking passa os candidatos recuperados por um modelo que pontua em conjunto a consulta e o documento. Isto pode melhorar a relevância fina ao custo de mais uma chamada a um modelo. O número de candidatos, o número retido e a latência devem ser ajustados em conjunto. Tratei o pipeline completo de várias fases em Construir uma stack moderna de ranking de pesquisa.
  • A transformação da consulta reescreve a consulta do utilizador antes da recuperação para melhorar o recall. O HyDE (Hypothetical Document Embeddings) faz o LLM gerar um documento hipotético, cujo embedding é usado para recuperar documentos reais. O texto gerado é uma representação para pesquisa, não evidência verificada para a resposta. A multi-query expansion gera várias formulações da mesma pergunta. O step-back prompting faz primeiro uma pergunta mais geral para recuperar informação ou raciocinar a partir de princípios mais gerais.

Os modos de falha comuns:

  • Falha de recuperação — o documento correto existe, mas não é recuperado. Teste chunking, transformação da consulta, pesquisa híbrida e filtragem por metadados face à falha.
  • Context poisoning — chunks recuperados irrelevantes induzem o LLM em erro. Teste reranking, filtros de contexto e conjuntos retidos mais pequenos.
  • Lost-in-the-middle — nos cenários testados de perguntas e respostas sobre vários documentos e de recuperação chave-valor, Liu et al. verificaram que o desempenho das respostas era geralmente mais alto quando a informação relevante aparecia perto do início ou do fim da entrada e mais baixo quando aparecia no meio.

O GraphRAG (Microsoft, 2024) extrai entidades e relações, deteta comunidades no grafo e cria resumos dessas comunidades. O seu método de pesquisa global responde a perguntas ao nível do corpus a partir desses resumos; não é simplesmente recuperação vetorial com um grafo acrescentado. O compromisso é o trabalho adicional de extração, indexação, armazenamento e avaliação.

Os guias de profissionais publicam intervalos de latência para embeddings, pesquisa, reranking e geração, mas esses valores variam com a região, o corpus, o hardware e o modelo. Meça cada fase em traces e avalie a alteração de qualidade antes de aceitar a latência adicional.

39. Arquiteturas de agentes e tool calling

Os agentes LLM usam modelos para selecionar e sequenciar tool calls em torno de um estado em evolução. Três padrões úteis de orquestração são:

  • ReAct — intercala a seleção de ações com observações. Consegue adaptar-se após cada tool result, mas um histórico crescente aumenta o custo em tokens e em latência.
  • ReWOO — planeia as tool calls com marcadores de posição e dependências explícitas entre resultados, executa o plano e depois sintetiza. Só as chamadas independentes podem correr em paralelo; uma chamada que refere um resultado anterior tem de esperar por ele. O seu artigo reporta poupanças de tokens face ao ReAct, mas o plano fixo precisa de um caminho de recuperação explícito quando uma ferramenta falha.
  • Planner-executor — separa o planeamento da execução e pode acrescentar uma política de replaneamento após uma falha. Permite um modelo diferente para cada papel, mas acrescenta estado do plano e uma segunda chamada a um modelo que pode falhar.
PadrãoTrabalho repetido do modeloAdaptabilidadePonto de partida útil
ReActLê as observações antes da ação seguinteAtualiza após as observaçõesTool use incerto ou exploratório
ReWOOReutiliza um plano com dependências explícitasPlano fixo, salvo se for estendidoTrabalho que pode ser planeado antes dos tool results
Planner-executorPlaneia, executa e pode voltar a planearPode rever o plano explícitoTarefas mais longas que beneficiam de controlo

A figura desenha os três padrões, para que se veja onde cada um planeia e se tem um caminho de volta após um tool result.

Três linhas de chamadas ao modelo e tool calls. O ReAct alterna chamadas ao modelo e tool calls; o modelo lê cada resultado antes de voltar a decidir, e cada chamada inclui um histórico mais longo. O ReWOO faz uma chamada para planear, executa três tool calls independentes em paralelo neste exemplo e faz uma chamada para escrever a resposta. As chamadas dependentes têm de correr por ordem; este exemplo não tem um caminho de replaneamento. O planner-executor escreve um plano, executa os passos e regressa ao planeador para rever o plano quando o passo 2 falha.Três linhas de chamadas ao modelo e tool calls. O ReAct alterna chamadas ao modelo e tool calls; o modelo lê cada resultado antes de voltar a decidir, e cada chamada inclui um histórico mais longo. O ReWOO faz uma chamada para planear, executa três tool calls independentes em paralelo neste exemplo e faz uma chamada para escrever a resposta. As chamadas dependentes têm de correr por ordem; este exemplo não tem um caminho de replaneamento. O planner-executor escreve um plano, executa os passos e regressa ao planeador para rever o plano quando o passo 2 falha.

O function calling é um mecanismo comum de invocação de ferramentas. As APIs expõem definições de ferramentas e devolvem argumentos estruturados, o que reduz a necessidade de fazer parsing de texto livre. Argumentos válidos segundo o schema podem ainda selecionar a ferramenta errada ou conter valores inválidos. O function calling paralelo pode reduzir as idas e voltas quando as operações são independentes.

O structured output e o constrained decoding impõem um schema restringindo os tokens disponíveis em cada passo de geração. Motores como o xgrammar, usado no vLLM e no SGLang, conseguem eliminar muitas falhas de sintaxe e de parsing com baixo overhead em configurações suportadas. Não garantem que os valores extraídos ou as decisões estejam corretos. O Schema-Guided Reasoning (SGR) usa a ordem dos campos e a estrutura do schema para tornar inspecionável o estado intermédio antes da decisão final. Os seus três padrões são Cascade (passos sequenciais), Routing (union types como comutadores semânticos) e Cycle (listas limitadas).

Um conjunto de ferramentas maior e uma sequência de ações mais profunda podem reduzir a qualidade da seleção e aumentar a latência ou o custo em tokens. Meça essas curvas com as descrições reais das ferramentas e a distribuição real de falhas. Frameworks como o LangGraph podem tornar explícitos o estado e os caminhos de recuperação, mas não eliminam o encargo da avaliação.

40. Prompt engineering para produção

O prompting em produção é um problema de avaliação: altere uma parte do prompt ou do contexto e meça depois a qualidade da tarefa e os modos de falha. As técnicas abaixo são pontos de partida comuns, não uma ordem universal.

Os exemplos few-shot são muitas vezes eficazes para controlar o formato da saída. Comece com 3–5 exemplos que cubram entradas vazias, consultas ambíguas e respostas com várias partes e meça depois o resultado num conjunto de validação separado. Os exemplos devem abranger a distribuição real das entradas e não apenas o caminho feliz. Mais exemplos consomem contexto e não garantem mais ganhos.

O prompting com chain-of-thought (CoT) pede a um modelo que exponha o raciocínio intermédio antes de responder. Kojima et al. (2022) reportaram ganhos com o sufixo “Let’s think step by step” nas suas tarefas de raciocínio testadas, mas o efeito varia com o modelo e as APIs de raciocínio mais recentes podem não expor os traces ocultos. Em produção, prefira uma decomposição inspecionável da tarefa ou uma justificação concisa quando for útil ao avaliador. A self-consistency (Wang et al., 2023) amostra vários caminhos de raciocínio e agrega as respostas, trocando custo adicional de inferência por robustez em tarefas adequadas.

O structured output com JSON Schema explícitos (Secção 39) elimina muitas falhas de parsing. Motores de constrained decoding como o xgrammar conseguem impor a gramática suportada durante a geração; a exatidão factual e a validade semântica continuam a exigir avaliação, e funcionalidades de schema não suportadas podem ainda precisar de tratamento.

O prompt chaining divide uma tarefa em fases focadas, por exemplo classificar a intenção → recuperar contexto → gerar a resposta → validar a saída. Pode localizar falhas, permitir modelos diferentes por fase e expor estado intermédio que pode ser colocado em cache. Também acrescenta interfaces e latência, por isso compare-o com uma baseline de chamada única.

A temperature altera a distribuição de amostragem. Valores baixos são um ponto de partida razoável para classificação ou extração; valores mais altos podem aumentar a diversidade na geração de ideias. O comportamento exato difere entre as APIs de modelos e interage com top_p, top_k e as predefinições do fornecedor, por isso varra as definições suportadas na tarefa em vez de copiar um único intervalo.

A separação entre mensagens de system e de user mantém a política persistente à parte do conteúdo de cada pedido. Os chat templates e o instruction tuning dão a estes papéis prioridades diferentes, mas não fazem da mensagem de system uma fronteira de imposição. Coloque o comportamento estável na mensagem de system, mantenha os dados não confiáveis em conteúdo de user ou de ferramentas e imponha também fora do modelo as restrições rígidas, como a remoção de PII.

O context engineering alarga o trabalho de prompting à montagem de documentos recuperados, tool results, histórico de conversa e exemplos. Liu et al. encontraram um efeito lost-in-the-middle nos modelos de contexto longo que testaram, pelo que a posição deve fazer parte da avaliação e não ser assumida como irrelevante. Tratei o fluxo de trabalho mais amplo em Context engineering para agentes AI.


Parte VIII — Operações em produção

As operações em produção transformam os conceitos anteriores em limites de admissão, testes de carga, alertas e decisões de capacidade sob tráfego real.

41. Rate limiting para pedidos com custo variável

O rate limiting tradicional por pedidos por segundo assume um custo aproximadamente igual por pedido. Os LLMs quebram esse pressuposto. Um prompt de classificação de 10 tokens e uma análise de documentos de 100K tokens atingem o mesmo endpoint da API mas diferem no comprimento da entrada por um fator de 10 000. O custo total também depende do comprimento da saída, do modelo, da cache e dos preços. Limitar por RPS ou deixa passar pedidos caros sem controlo ou deixa à míngua, sem necessidade, os pedidos baratos.

Os sistemas de produção precisam de rate limiting baseado em tokens em várias dimensões. A OpenAI documenta limites de pedidos e de tokens por escalão de utilização. A Anthropic separa os limites de tokens de entrada e de saída. As quotas e os algoritmos exatos podem mudar, por isso trate a documentação do fornecedor como a fonte de verdade; o ponto arquitetural é orçamentar pedidos e tokens de forma independente.

Uma implementação ao nível da aplicação é uma hierarquia multidimensional de limites (utilizador → aplicação → organização → global), com níveis de prioridade para acesso premium. Em cada pedido, reserve tokens na admissão: estime o total (entrada + max_tokens), desconte a estimativa da reserva e devolva a parte não utilizada quando o pedido terminar, de acordo com o consumo real. Isto impede que uma rajada de pedidos com gerações longas esgote a capacidade antes de produzir saída. Um pedido com 2000 tokens de entrada e max_tokens = 1000 reserva 3000. Se a resposta utilizar 200 tokens de saída, o consumo real é de 2200 e são devolvidos 800. Esta é uma política local de admissão; os algoritmos de quotas dos fornecedores podem usar estimativas diferentes e orçamentos separados para entrada e saída. Se a reserva tiver menos de 3000, devolva um 429 ou coloque o pedido em fila.

Em implementações self-hosted, o equivalente é o throughput provisionado: reservar capacidade de GPU dedicada para taxas de tokens alvo. Em implementações de vLLM, isso significa configurar o controlo de admissão em torno dos slots de decode ativos e da pressão sobre a KV cache e não apenas do número de pedidos. Como explica a Secção 5, o throughput e a admissão precisam ambos de limites sensíveis a tokens.

42. Modos de falha a considerar

O serving de LLMs acrescenta modos de falha ligados ao comprimento variável das sequências, à memória de KV e ao trabalho de decode de longa duração. Desenhe e teste sob carga as defesas antes de o tráfego de produção depender delas.

O Out-of-Memory (OOM) é uma falha comum. Um modelo de 70B em FP16 precisa de cerca de 140 GB só para os pesos, e a KV cache de uma única sequência do Llama 3.1 70B com contexto de 128K pode acrescentar cerca de 40 GiB, sob os pressupostos da Secção 6. A diferença entre “cabe na memória” e “OOM sob carga” é menor do que parece, porque um batch de pedidos de contexto longo pode consumir mais memória de KV do que o esperado. A prevenção combina uma reserva de memória medida com quantização e alocação paginada da KV. No vLLM, --gpu-memory-utilization define a fração da memória da GPU que o motor pode usar para pesos, ativações e KV cache; o resto é a reserva. Para cargas com alta pressão sobre a KV, o LMCache pode fazer offload de dados de KV para a memória do CPU ou para disco; use os resultados que publica como ponto de partida e faça benchmark da hierarquia de memória local.

A preempção acontece quando a pressão sobre a KV cache obriga o scheduler a despejar ou recalcular trabalho. A estratégia exata depende da versão e da configuração do serving. Do lado do utilizador, o sintoma é uma latência de ponta a ponta mais alta sem um erro evidente da aplicação. Observe o número de preempções e correlacione-o com o uso de KV, a profundidade da fila e os comprimentos dos pedidos.

A latência de cauda pode disparar quando prefills grandes atrasam o trabalho de decode. O chunked prefill (Secção 7) e o escalonamento atento ao comprimento visam esta interferência. Os artigos do scheduler Learning-to-Rank (Fu et al., 2024) e do CascadeInfer reportam melhorias face às suas baselines nas cargas avaliadas, mas o resultado exato depende da distribuição de comprimentos dos pedidos e da configuração do scheduler.

As falhas em cascata podem começar quando pedidos lentos fazem crescer a fila, os clientes a montante expiram e os retries acrescentam mais carga. As defesas incluem controlo de admissão, limites de concorrência por tenant, limites máximos de saída, orçamentos de retries e circuit breakers no gateway. Pools de prefill e decode desagregados podem ajudar quando os testes de carga mostram interferência persistente entre fases. A figura mostra o ciclo e a defesa em cada aresta.

Um ciclo de quatro passos: pedidos longos ocupam slots de decode, a fila cresce, os clientes expiram enquanto o servidor continua a gerar, e os clientes repetem o pedido, pelo que o mesmo trabalho chega outra vez. Uma barra em cada aresta indica a defesa que a corta: limites de saída, controlo de admissão e limites por tenant, orçamentos de retries e circuit breakers no gateway.Um ciclo de quatro passos: pedidos longos ocupam slots de decode, a fila cresce, os clientes expiram enquanto o servidor continua a gerar, e os clientes repetem o pedido, pelo que o mesmo trabalho chega outra vez. Uma barra em cada aresta indica a defesa que a corta: limites de saída, controlo de admissão e limites por tenant, orçamentos de retries e circuit breakers no gateway.

43. Monitorização de sistemas LLM

A monitorização de LLMs difere da monitorização de APIs em três aspetos: cada pedido tem um custo diferente, cada pedido tem duas fases com bottlenecks diferentes e a sua pegada de memória depende do comprimento tanto da entrada como da saída. A latência dos pedidos e a taxa de erros, por si sós, não mostram nenhum destes aspetos.

O goodput é o número de pedidos por segundo que cumprem todos os limiares de SLO definidos, como o TTFT, o TPOT e a latência total. É uma medida combinada útil porque o throughput bruto pode parecer saudável enquanto os SLOs de latência falham: um sistema que processa 100 pedidos por segundo mas falha os seus limiares em 40% deles tem um goodput de 60 pedidos por segundo. Otimizar o goodput mantém visível a distribuição do desempenho, em vez de reportar apenas a média.

O vLLM expõe um endpoint Prometheus em /metrics com pedidos em execução e em espera, uso da KV cache, distribuições do comprimento de geração e estatísticas da prefix cache. Os nomes das métricas podem mudar entre versões, por isso associe os dashboards à versão em produção. Uma stack típica usa o Prometheus para métricas, o Grafana para visualização e traces compatíveis com OpenTelemetry entre os componentes da aplicação e do serving.

Padrões de alerta úteis incluem os seguintes. Derive os seus limiares a partir de testes de carga em vez de copiar estes exemplos sem alterações:

  • Picos na contagem de preempções — o runtime está a trocar ou a recalcular trabalho, o que acrescenta latência sem um erro da aplicação.
  • Utilização da KV cache a aproximar-se da região de preempção testada — acrescente capacidade ou descarte carga antes de os despejos se propagarem em cascata.
  • Profundidade da fila persistentemente acima do envelope de batch testado — o controlo de admissão deve começar a rejeitar ou a despriorizar.
  • TTFT a subir enquanto o TPOT se mantém estável — esta divergência aponta primeiro para pressão de fila, de admissão, de rede ou de prefill e não para o throughput do decode. Use traces e métricas de fila para as distinguir.

No vLLM, os três primeiros alertas correspondem a vllm:num_preemptions_total, vllm:kv_cache_usage_perc e vllm:num_requests_waiting (métricas do vLLM).

44. Otimização de custos: uma estratégia cumulativa

Os preços dos fornecedores e os rácios de preço entre entrada e saída mudam. Consulte as tarifas atuais antes de uma decisão de compra; o escalão do modelo e o comprimento da saída podem dominar a fatura mesmo antes das otimizações de infraestrutura.

A OpenAI, a Anthropic e a Google cobram todas os tokens de saída a vários múltiplos do preço dos tokens de entrada nos seus modelos de topo. Separadamente, a divisão entre prefill e decode explica por que razão a entrada e a saída têm custos de execução diferentes: o prefill processa os tokens do prompt em conjunto, enquanto o decode normal gera os tokens sequencialmente. Este mecanismo, por si só, não estabelece como um fornecedor define os preços comerciais. Cortar saída desnecessária (um max_tokens apertado, structured output, sem repetir a entrada) costuma poupar mais do que cortar o mesmo número de tokens de entrada.

Várias abordagens podem ser combinadas, mas só depois de medir quais se aplicam à carga de trabalho:

  1. A quantização de FP16 para INT4 corta o armazenamento dos valores brutos dos pesos em 75%, antes das escalas e de outros metadados. Saber se isso reduz a fatura depende da velocidade do kernel, do tamanho do batch e da utilização do hardware (Secção 9).
  2. O routing de modelos envia o tráfego elegível para modelos mais baratos. Meça a taxa de falsas aceitações do router e a qualidade de ponta a ponta antes de aumentar a quota tratada pelo modelo mais barato (Secção 36).
  3. O prompt caching reduz o trabalho de prefixos repetidos. Os descontos dos fornecedores e o tratamento dos rate limits mudam ao longo do tempo, por isso combine a taxa de acertos medida com os termos atuais (Secção 16).
  4. As batch APIs podem dar desconto em trabalho não em tempo real, como avaliações, geração de dados sintéticos e classificação em massa. Verifique os preços atuais e as janelas de conclusão.
  5. O self-hosting pode compensar com utilização sustentada, mas não há um ponto de equilíbrio universal em volume de tokens. Inclua os custos de engenharia, orquestração, observabilidade, folga de capacidade e on-call, além do aluguer de GPUs.

Não multiplique ganhos separados de otimização como se todos os pedidos beneficiassem deles de forma independente: a quantização altera o débito, o encaminhamento altera a distribuição de qualidade, e a cache e o processamento por lotes só se aplicam ao tráfego elegível. Construa a estimativa a partir das proporções de tráfego medidas e valide-a face à fatura.

45. Planeamento de capacidade e autoscaling

O planeamento de capacidade do serving de LLMs tem de ter em conta o custo variável dos pedidos, o trabalho de decode de longa duração e a memória que depende da sequência. Consoante a carga de trabalho, o recurso limitante pode ser a memória de KV, a largura de banda de memória, a computação ou a interligação.

O limite teórico de memória para pedidos concorrentes vem do orçamento da KV cache:

max concurrent sequences=GPU memory−model weights−overheadper-sequence KV cache size\text{max concurrent sequences} = \frac{\text{GPU memory} - \text{model weights} - \text{overhead}}{\text{per-sequence KV cache size}}

Para a aritmética de capacidade, suponha que o runtime expõe um orçamento de KV de 40 GiB para o Llama 3.1 70B com uma KV cache em FP16. Cada sequência de 4K usa cerca de 1,25 GiB e cada sequência de 128K cerca de 40 GiB. Esse orçamento comporta, portanto, no máximo cerca de 32 sequências de 4K ou uma sequência de 128K, antes do overhead do alocador, do runtime, da variância da carga e do SLO. A função kv_cache_bytes da Secção 6 repete este cálculo para outro modelo ou orçamento. É por isso que a seleção de GPUs e a otimização da KV cache determinam diretamente o plano de capacidade.

A fórmula de capacidade para dimensionar a frota:

required GPUs=peak tokens/s×safety-factor multiplierper-GPU tokens/s at target SLO\text{required GPUs} = \frac{\text{peak tokens/s} \times \text{safety-factor multiplier}}{\text{per-GPU tokens/s at target SLO}}

Converta ambas as taxas para a mesma unidade de tempo antes de dividir. Use o throughput por GPU medido no SLO alvo, não o throughput de pico. Um multiplicador de 1,3 disponibiliza 30% mais throughput do que a procura prevista, o que deixa cerca de 23% da capacidade disponibilizada por usar nessa procura. Arredonde por excesso para réplicas completas de serving; uma réplica TP/PP precisa do seu grupo completo de GPUs. As taxas por GPU só são úteis para uma topologia de réplicas fixa e medida. Escolha o multiplicador a partir da variabilidade das rajadas, das falhas e do tempo de recuperação medidos. O throughput de pico de tokens e o throughput que cumpre o SLO podem diferir muito à medida que a concorrência sobe. Faça benchmark com a distribuição real de prompts e de comprimentos de saída nos limiares exigidos de TTFT e TPOT, em vez de usar um máximo teórico.

A utilização da GPU é insuficiente como único sinal de autoscaling porque pode manter-se alta tanto durante um processamento saudável como numa sobrecarga. Combine-a com a profundidade da fila, a utilização da KV cache e a degradação do goodput. Ajuste os limiares a partir de testes de carga; valores como 80% de utilização de KV são pontos de partida, não limites universais. Estas métricas são apresentadas na Secção 43.

O scale-to-zero pode adequar-se a ambientes de desenvolvimento e de staging com longos períodos de inatividade. As plataformas de inferência serverless e os autoscalers baseados em Kubernetes, como o KEDA, conseguem remover capacidade inativa, mas a poupança e o tempo de cold start dependem do tamanho do modelo, da cache de imagens e de pesos e da infraestrutura. Meça o tempo de arranque antes de usar a mesma política para tráfego de produção sensível à latência.


Como os conceitos de inferência se ligam

As Partes I a III ligam a capacidade de memória ao desempenho de serving. O tamanho da KV por sequência (Secção 6) dá um limite bruto ao número de sequências residentes. Batches de decode maiores podem melhorar a reutilização dos pesos e a intensidade aritmética (Secção 1), mas o tráfego de attention, o escalonamento e a comunicação também afetam o TPOT e o throughput. O GQA e o armazenamento KV de menor precisão reduzem o volume da cache; o PagedAttention reduz o desperdício de alocação. Com a configuração indicada do Llama 3.1 70B, uma KV cache em FP16 e um orçamento de 40 GiB, o limite bruto é de 32 sequências de 4K tokens ou uma de 128K, antes do overhead do runtime e das restrições de latência. O controlo de admissão pode colocar trabalho em fila ou rejeitá-lo antes de esse limite ser atingido. Sob pressão da KV, o runtime pode fazer preempção de pedidos; alocações sem controlo podem causar OOM (Secção 42).

O routing, o caching, a quantização e a escolha de hardware só se acumulam quando cada um é avaliado face ao mesmo objetivo de qualidade e de latência. Escolha um sintoma da tabela no início, estabeleça essa baseline e faça com que a alteração seguinte seja fácil de reverter.


Leitura adicional

Artigos relacionados deste blogue, organizados por tema:


Referências

Organizadas por área temática.

Inferência e attention

Descodificação especulativa

Quantização

Treino e fine-tuning

Alinhamento

Escala e arquitetura

Embeddings

Arquiteturas de agentes

Prompting

Routing

Benchmarks

Arquiteturas de serving

Frameworks de serving

  • vLLM - motor de serving baseado em PagedAttention
  • SGLang - RadixAttention e structured output
  • TensorRT-LLM - inferência otimizada da NVIDIA
  • llama.cpp - inferência portátil em C/C++
  • DeepSpeed - biblioteca de treino distribuído da Microsoft
  • Ollama - executor local de LLMs

Operações