12  Estratégias de Inferência e Geração de Texto

Desenvolvimento de métodos de geração de texto, explorando Greedy Search, Beam Search, amostragem Top-k, Top-p (Nucleus) e Temperatura.

Este capítulo detalha os mecanismos fundamentais pelos quais os Grandes Modelos de Linguagem (LLMs) transformam probabilidades estatísticas em texto coerente. A inferência em modelos autoregressivos envolve prever o próximo token (\(w_t\)) dado uma sequência de tokens anteriores (\(w_{1:t-1}\)).

Embora o modelo forneça uma distribuição de probabilidade sobre todo o vocabulário (os logits), a escolha de qual token selecionar — a Estratégia de Decodificação — determina a criatividade, coerência e qualidade do texto gerado.

12.1 1. O Processo Autoregressivo

Antes de explorar as estratégias, é crucial entender o fluxo de dados. O modelo recebe um contexto, processa através de camadas de attention, e a camada final projeta o estado oculto para o tamanho do vocabulário. Uma função Softmax converte esses valores brutos (logits) em probabilidades.

\[ P(w_t | w_{1:t-1}) = \text{softmax}(h_{t}) \]

A estratégia de decodificação intervém exatamente após a geração dos logits e antes (ou durante) a seleção final do token.

O processo continua até um token de fim, um limite de comprimento ou outro critério de parada. A janela de contexto limita quantos tokens podem participar do cálculo; quando prompt e geração ultrapassam esse limite, o sistema precisa truncar, resumir ou recuperar somente informações relevantes.

Em desempenho, separe prefill e decode. O prefill processa o prompt em paralelo e preenche o cache de chaves e valores. O decode gera tokens sequencialmente e reutiliza esse cache. Por isso, tempo para o primeiro token e tokens por segundo medem aspectos distintos da experiência.

12.2 2. Métodos Determinísticos

Métodos determinísticos sempre produzem a mesma saída para a mesma entrada, focando na maximização da probabilidade.

12.2.1 2.1 Greedy Search (Busca Gulosa)

A estratégia mais simples. Em cada passo, o modelo seleciona o token com a maior probabilidade absoluta.

  • Algoritmo: \(w_t = \text{argmax } P(w | w_{1:t-1})\)
  • Vantagens: Computacionalmente eficiente; bom para tarefas que exigem respostas exatas (aritmética, tradução curta).
  • Desvantagens: Tende a gerar textos repetitivos e genéricos. Sofre de “miopia”: escolhe o melhor token agora, mas pode levar a um “beco sem saída” probabilístico mais tarde.
# Exemplo simplificado de Greedy Search
import torch

def greedy_decode(model, input_ids, max_length):
    for _ in range(max_length):
        outputs = model(input_ids)
        next_token_logits = outputs.logits[:, -1, :]
        # Seleciona o índice com o maior valor
        next_token = torch.argmax(next_token_logits, dim=-1).unsqueeze(0)
        input_ids = torch.cat([input_ids, next_token], dim=1)
    return input_ids

12.2.2 2.2 Beam Search (Busca em Feixe)

O Beam Search mitiga a miopia do Greedy Search mantendo as \(k\) sequências mais prováveis (onde \(k\) é o num_beams) em cada passo, em vez de apenas uma.

  • Funcionamento:
    1. Gera a distribuição para o próximo passo.
    2. Considera todas as extensões possíveis das \(k\) sequências atuais.
    3. Calcula a probabilidade acumulada (score) de cada caminho.
    4. Mantém apenas as top-\(k\) sequências para o próximo passo.
  • Vantagens: Gera textos mais coerentes e gramaticalmente corretos que o Greedy.
  • Desvantagens: Computacionalmente mais caro. Ainda pode sofrer de repetição em textos longos.

12.3 3. Métodos Estocásticos (Amostragem)

Para geração de texto criativo (chatbots, histórias), a maximização da probabilidade é indesejável, pois a linguagem humana não é estritamente previsível. Métodos estocásticos introduzem aleatoriedade controlada.

12.3.1 3.1 Temperatura (Temperature)

A temperatura é um hiperparâmetro que escala os logits antes da aplicação da Softmax.

\[ P_i = \frac{\exp(z_i / T)}{\sum_j \exp(z_j / T)} \]

  • \(T < 1\) (Baixa Temperatura): Aumenta a diferença entre logits altos e baixos. A distribuição fica “pontiaguda”. O modelo torna-se mais confiante e conservador.
  • \(T = 1\): Distribuição original do modelo.
  • \(T > 1\) (Alta Temperatura): Achata a distribuição. Tokens menos prováveis ganham chance de serem escolhidos. Aumenta a criatividade, mas também o risco de alucinação ou incoerência.

12.3.2 3.2 Top-k Sampling

Em vez de amostrar de todo o vocabulário (que pode ter 50.000+ tokens), limitamos a amostragem aos \(k\) tokens com maior probabilidade.

  1. Ordena-se o vocabulário por probabilidade.
  2. Mantém-se apenas os top-\(k\) tokens.
  3. A massa de probabilidade restante é zerada.
  4. A distribuição é re-normalizada.
  5. Amostra-se um token dessa nova distribuição.
  • Problema: \(k\) é fixo. Se a distribuição for plana (muitas palavras válidas), \(k\) pode cortar opções boas. Se for pontiaguda (poucas palavras válidas), \(k\) pode incluir palavras sem sentido.

12.3.3 3.3 Top-p (Nucleus) Sampling

Introduzido para resolver a rigidez do Top-k. Em vez de um número fixo de tokens, seleciona-se o menor conjunto de tokens cuja probabilidade acumulada excede um limiar \(p\) (ex: \(0.9\) ou \(90\%\)).

  • Dinâmica: O tamanho do conjunto de candidatos (\(k\)) varia dinamicamente a cada passo.
    • Se o modelo está incerto (distribuição plana), muitos tokens são necessários para somar 90%, aumentando a variedade.
    • Se o modelo está certo (distribuição pontiaguda), poucos tokens somam 90%, reduzindo o risco de erros.

12.3.3.1 Comparação Visual: Top-k vs Top-p

graph TD
    subgraph Vocabulario_Ordenado
    T1[Token 1: 0.4]
    T2[Token 2: 0.3]
    T3[Token 3: 0.15]
    T4[Token 4: 0.05]
    T5[Token 5: 0.05]
    T6[Token 6: 0.01]
    end

    subgraph Top_K_k_is_2
    K_Select[Seleciona Top 2] --> T1
    K_Select --> T2
    style T3 fill:#f9f9f9,stroke:#333,stroke-dasharray: 5 5
    end

    subgraph Top_P_p_is_0_85
    P_Calc[Soma Cumulativa >= 0.85]
    T1 --0.4--> Sum1(0.4)
    T2 --0.3--> Sum2(0.7)
    T3 --0.15--> Sum3(0.85 - STOP)
    
    P_Select[Seleciona Conjunto] --> T1
    P_Select --> T2
    P_Select --> T3
    end

12.4 4. Resumo Comparativo e Implementação

A maioria dos sistemas modernos utiliza uma combinação dessas estratégias (ex: Top-k seguido de Top-p, com Temperatura ajustada).

Estratégia Determinístico? Caso de Uso Ideal Risco Principal
Greedy Sim Respostas curtas, factuais, matemática. Repetição, falta de criatividade.
Beam Search Sim Tradução, resumo de texto. Custo computacional, repetição.
Temperatura Não (se T \(\neq\) 0) Controle global de “risco”. T alto: Alucinação. T baixo: Repetição.
Top-k Não Chatbots simples. Corte arbitrário de palavras válidas.
Top-p Não Escrita criativa, diálogo humano. Imprevisibilidade em contextos técnicos.

12.4.1 Exemplo de Configuração de Geração (Hugging Face Transformers)

# Configuração típica para um Chatbot criativo mas coerente
generation_config = {
    "max_new_tokens": 200,
    "do_sample": True,        # Ativa métodos estocásticos
    "temperature": 0.7,       # Equilíbrio entre foco e criatividade
    "top_k": 50,              # Limita a cauda longa de tokens improváveis
    "top_p": 0.95,            # Nucleus sampling para adaptação dinâmica
    "repetition_penalty": 1.2 # Penaliza repetição de tokens já gerados
}

# model.generate(**inputs, **generation_config)

12.5 5. Critérios de parada e avaliação

Uma geração precisa de condições explícitas: token de fim, limite de novos tokens e, em aplicações estruturadas, sequências de parada. O limite deve contar apenas os novos tokens quando o tamanho do prompt varia. Penalidades de repetição podem ajudar, mas valores altos também deformam nomes, código e termos que precisam reaparecer.

Não existe configuração universalmente melhor. Compare estratégias no conjunto representativo da aplicação e meça correção, diversidade, latência e taxa de respostas inválidas. Para geração factual, a verificação por fontes pode ser mais importante que qualquer ajuste de temperatura.

TipExperimento controlado

Fixe prompt e semente, varie um único parâmetro por vez e registre a distribuição antes e depois do filtro. Isso torna visível se a mudança veio da temperatura, de top-\(k\) ou de top-\(p\).

Técnicas como speculative decoding usam um modelo menor para propor vários tokens e o modelo principal para verificá-los. Quando as propostas são aceitas com frequência, a latência diminui sem alterar a distribuição final produzida pelo modelo principal.

12.6 Cache de chaves e valores

Sem cache, ao gerar o token \(t\), o modelo recalcularia chaves e valores dos \(t-1\) tokens anteriores em todas as camadas. O KV cache guarda esses tensores. A nova consulta é comparada com as chaves armazenadas, e somente o novo par de chave e valor é anexado.

Para \(L\) camadas, \(H_{kv}\) cabeças KV, dimensão \(d_h\), comprimento \(T\) e \(b\) bytes por elemento, uma estimativa para um item do lote é

\[ \text{bytes}\approx 2LTH_{kv}d_hb, \]

em que o fator 2 representa chaves e valores. A fórmula explica por que contextos longos e lotes concorrentes pressionam a memória, e por que GQA reduz o cache ao diminuir \(H_{kv}\).

O cache acelera o decode, mas não remove sua dependência sequencial. Também não deve ser reutilizado entre prompts incompatíveis: as posições, máscaras e tokens precisam corresponder exatamente ao prefixo armazenado.

12.7 Quantização na inferência

Quantizar é representar pesos ou ativações com menos bits. Um peso em FP32 ocupa quatro bytes; em int8, aproximadamente um byte, antes de metadados de escala. Na quantização simétrica, um intervalo centrado em zero é mapeado para inteiros. Na assimétrica, um ponto zero permite representar intervalos deslocados.

PTQ quantiza um modelo já treinado usando calibração; QAT simula a quantização durante o treino para adaptar os pesos. Reduzir bits economiza memória e pode acelerar kernels compatíveis, mas introduz erro de arredondamento. Camadas sensíveis podem permanecer em maior precisão.

TipAvalie o sistema, não apenas o tamanho

Compare perplexidade ou tarefas, tokens por segundo, tempo para o primeiro token e memória. Um arquivo menor não garante aceleração se o hardware precisar desquantizar valores ou não possuir kernels adequados.