Ajuste Fino (Fine-Tuning)

O que “ajuste fino (fine-tuning)” significa para modelos de linguagem grandes (large language models, LLMs)

Ajuste fino é o processo de adaptar um modelo de linguagem grande pré-treinado (pretrained) continuando o treinamento em um conjunto de dados (dataset) menor, específico de tarefa ou de domínio. Diferentemente do direcionamento por prompt (prompt-based steering) (veja Aprendizado em Contexto), o ajuste fino altera os pesos (weights) do modelo, tornando o comportamento mais consistente e “incorporado” no momento da inferência (inference).

O ajuste fino é comumente usado para:

  • Melhorar o seguimento de instruções (instruction following) e o comportamento conversacional (veja Seguimento de Instruções)
  • Adaptar um modelo geral a um domínio (domain) (jurídico, médico, documentação interna, suporte ao cliente)
  • Impor um estilo, estrutura ou esquema de saída (output schema) específico (por exemplo, JSON)
  • Ensinar padrões de uso de ferramentas (tool-use patterns) e formatos de chamada de função (function-calling formats) (veja LLMs que Usam Ferramentas)
  • Reduzir taxas de alucinação (hallucination) para fatos conhecidos dentro do domínio (embora não seja uma solução completa; veja Alucinações)

Duas famílias dominam o ajuste fino prático de LLMs hoje:

  • Ajuste fino supervisionado (supervised fine-tuning, SFT): aprendizado supervisionado (supervised learning) padrão em exemplos de prompt→resposta.
  • Adaptação de baixo posto (low-rank adaptation, LoRA) e métodos relacionados de ajuste fino eficiente em parâmetros (parameter-efficient fine-tuning, PEFT): atualizam um pequeno conjunto de parâmetros adicionados em vez de todos os pesos do modelo, reduzindo drasticamente os requisitos de memória de GPU e armazenamento.

Este artigo foca em ajuste fino supervisionado, adaptação de baixo posto e técnicas pragmáticas de adaptação usadas em fluxos de trabalho modernos de LLMs.

Onde o ajuste fino se encaixa no pipeline de treinamento de LLMs

Um ciclo de vida típico de LLM se parece com:

  1. Pré-treinamento (pretraining): previsão do próximo token (next-token prediction) em corpora (corpora) massivos de texto/código (veja Pré-treinamento e Leis de Escala).
  2. Pré-treinamento contínuo (continued pretraining) opcional (pré-treinamento adaptado ao domínio (domain-adaptive pretraining)): mais treinamento de próximo token em corpora do domínio.
  3. Ajuste fino supervisionado: treinamento supervisionado em dados de instrução/diálogo (“faça X” → “aqui está X”).
  4. Otimização por preferência (preference optimization) opcional: otimizar em direção a respostas preferidas usando feedback pareado (pairwise) ou feedback escalar (scalar feedback) (veja Métodos de Otimização por Preferência)—frequentemente discutida sob o guarda-chuva de Alinhamento.
  5. Mitigações de segurança (safety mitigations): ajuste de políticas (policy tuning), comportamento de recusa (refusal behavior), filtros e monitoramento (monitoring) (veja Mitigações de Segurança).

Na prática, “ajuste fino” normalmente se refere às etapas 2–4, mas muitas equipes usam o termo especificamente para ajuste fino supervisionado e ajuste fino eficiente em parâmetros.

Ajuste fino supervisionado (SFT)

A ideia central

O ajuste fino supervisionado treina o modelo para produzir uma resposta desejada dado um prompt. Para um LLM autoregressivo (autoregressive), o treinamento normalmente minimiza a perda de entropia cruzada (cross-entropy loss) nos tokens-alvo:

  • Entrada: uma sequência representando o prompt e possivelmente o histórico da conversa
  • Alvo: os tokens de resposta do assistente
  • Objetivo: aumentar a probabilidade dos tokens de resposta condicionada ao prompt

Isso ainda é “previsão do próximo token”, mas agora a distribuição de dados é instrucional e curada, não texto amplo da web.

Por baixo dos panos, isso é otimizado com variantes de Descida do Gradiente usando retropropagação (backpropagation) através de uma Arquitetura Transformer.

Formatos de dados: instrução, chat e mascaramento

Um detalhe crítico no ajuste fino supervisionado é em quais tokens você calcula a perda (loss).

Para ajuste no estilo chat (chat-style), em geral você quer:

  • Nenhuma perda em tokens de sistema/usuário (eles são “contexto”).
  • Perda apenas em tokens do assistente (o modelo é treinado para gerar o turno do assistente).

Conceitualmente:

  • Tokens de contexto: fornecidos como entrada condicionante (conditioning input)
  • Tokens de rótulo (label): resposta do assistente

Muitos pipelines de treinamento implementam isso por meio de uma máscara de perda (loss mask) (por exemplo, rótulo = -100 para tokens nos quais não se deve treinar).

Exemplo prático: um único exemplo de treinamento de chat

Uma representação comum em JSONL:

{
  "messages": [
    {"role": "system", "content": "You are a helpful assistant that answers concisely."},
    {"role": "user", "content": "Write a Python function to check if a number is prime."},
    {"role": "assistant", "content": "Here is a simple implementation...\n\n```python\n...\n```"}
  ]
}

O “modelo de chat (chat template)” exato (tokens especiais, marcadores de papel/role, separadores) deve corresponder à formatação esperada pelo modelo base. Modelos incompatíveis são uma causa frequente de degradação de desempenho.

Qualidade de dados supera quantidade de dados

O ajuste fino supervisionado é altamente sensível à qualidade do conjunto de dados porque ele costuma ser muito menor que os dados de pré-treinamento. Boas práticas comuns:

  • Remover duplicatas de exemplos quase idênticos (evita memorização (memorization) e sobreajuste (overfitting))
  • Remover saídas de assistente de baixa qualidade (prolixas, incorretas, tóxicas, inconsistentes)
  • Preferir exemplos de alto sinal com:
    • Instruções claras
    • Respostas corretas e verificadas
    • O estilo de saída que você quer em produção (tamanho, tom, estrutura)
  • Incluir negativos difíceis (hard negatives) com cuidado (por exemplo, exemplos em que o assistente recusa ou faz perguntas de esclarecimento)

Se você ajustar para um esquema estrito (por exemplo, JSON), forneça muitos exemplos que demonstrem:

  • aspas/escape corretos
  • chaves consistentes
  • o que fazer em caso de entrada inválida

Hiperparâmetros típicos de ajuste fino supervisionado (regras práticas)

Eles variam por tamanho do modelo e conjunto de dados, mas controles comuns incluem:

  • Taxa de aprendizado (learning rate): frequentemente menor do que no pré-treinamento; o ajuste fino eficiente em parâmetros pode tolerar taxas de aprendizado ligeiramente maiores do que o ajuste fino completo (full fine-tuning).
  • Épocas (epochs): 1–3 passagens são comuns; mais do que isso aumenta o risco de sobreajuste.
  • Comprimento de sequência (sequence length): contexto mais longo custa mais; considere suas Janelas de Contexto em produção.
  • Empacotamento (packing): combinar vários exemplos curtos em uma sequência melhora a eficiência, mas exige mascaramento cuidadoso.
  • Loteamento (batching): use acumulação de gradientes (gradient accumulation) para atingir um tamanho de lote efetivo (effective batch size).

Modos de falha no ajuste fino supervisionado

Problemas comuns e como eles aparecem:

  • Sobreajuste: o modelo repete frases do treinamento, piora em prompts retidos (held-out).
  • Esquecimento catastrófico (catastrophic forgetting): ganhos no seu domínio, mas perde capacidades gerais.
  • Colapso de estilo (style collapse): saídas ficam uniformemente sucintas/prolixas independentemente do prompt.
  • Generalização incorreta de instruções (instruction misgeneralization): aprende padrões superficiais (“sempre retornar JSON”) mesmo quando isso é inadequado.
  • Regressão de segurança (safety regression): o ajuste fino pode enfraquecer comportamentos de recusa embutidos, a menos que você inclua dados relevantes de segurança e avalie com cuidado (veja Mitigações de Segurança).

Ajuste fino eficiente em parâmetros (PEFT)

Por que o ajuste fino eficiente em parâmetros existe

O ajuste fino completo atualiza todos os parâmetros, o que é caro:

  • Estados do otimizador (optimizer states) (por exemplo, Adam) podem exigir ~2–8× a memória de parâmetros dependendo da precisão e da implementação.
  • Armazenar uma cópia completamente ajustada por tarefa é grande e operacionalmente inconveniente.

Métodos de ajuste fino eficiente em parâmetros adicionam um pequeno número de parâmetros treináveis enquanto mantêm o modelo base congelado (frozen). Benefícios:

  • Uso de VRAM muito menor
  • Treinamento mais rápido
  • Facilidade para manter múltiplos adaptadores (adapters) por tarefa
  • Menor risco de esquecimento catastrófico (não é eliminado, mas muitas vezes é reduzido)

A adaptação de baixo posto é o método de ajuste fino eficiente em parâmetros mais amplamente adotado para LLMs.

Adaptação de baixo posto (LoRA)

A intuição e a matemática

Em transformers, muitos pesos são matrizes grandes (por exemplo, projeções lineares em atenção (attention) e em blocos de perceptron multicamadas (multi-layer perceptron, MLP)). A adaptação de baixo posto representa uma atualização de uma matriz de pesos W como uma decomposição de baixo posto:

  • Original: W
  • Atualizada: W' = W + ΔW
  • A adaptação de baixo posto restringe: ΔW = B A onde:
    • A tem forma (r, in_features)
    • B tem forma (out_features, r)
    • r é um pequeno posto (rank) (por exemplo, 4, 8, 16, 32)

Assim, em vez de treinar toda W, você treina A e B, o que é muito menor quando r << min(in, out).

Um fator de escala (scaling factor) (frequentemente escrito α/r) é aplicado para estabilizar o treinamento. Muitas implementações expõem:

  • r (posto)
  • lora_alpha (escala)
  • lora_dropout (dropout)

Onde a adaptação de baixo posto é aplicada

Alvos comuns em blocos transformer:

  • Projeções de atenção: q_proj, k_proj, v_proj, o_proj
  • Projeções de MLP: up_proj, down_proj, gate_proj (dependente da arquitetura)

Aplicar adaptação de baixo posto a mais módulos aumenta a capacidade, mas custa mais memória.

Exemplo prático: ajuste fino supervisionado + adaptação de baixo posto com Hugging Face (PEFT)

Abaixo está uma configuração mínima (ilustrativa) usando transformers + peft. Um treinamento real adicionaria avaliação, checkpointing, melhor processamento de dados e um modelo de chat correto para o seu modelo.

from datasets import load_dataset
from transformers import AutoTokenizer, AutoModelForCausalLM, TrainingArguments, Trainer
from peft import LoraConfig, get_peft_model

model_name = "meta-llama/Meta-Llama-3-8B"  # example; use a model you have access to
tokenizer = AutoTokenizer.from_pretrained(model_name, use_fast=True)
tokenizer.pad_token = tokenizer.eos_token

base_model = AutoModelForCausalLM.from_pretrained(
    model_name,
    torch_dtype="auto",
    device_map="auto",
)

lora_cfg = LoraConfig(
    r=16,
    lora_alpha=32,
    lora_dropout=0.05,
    target_modules=["q_proj", "v_proj", "o_proj", "up_proj", "down_proj"],
    bias="none",
    task_type="CAUSAL_LM",
)

model = get_peft_model(base_model, lora_cfg)

ds = load_dataset("json", data_files="train.jsonl")["train"]

def format_example(ex):
    # Replace this with the *correct* chat template for your model.
    text = ""
    for m in ex["messages"]:
        text += f"{m['role'].upper()}: {m['content']}\n"
    text += "ASSISTANT:"
    return {"text": text}

ds = ds.map(format_example)

def tokenize(batch):
    out = tokenizer(
        batch["text"],
        truncation=True,
        max_length=2048,
        padding="max_length",
    )
    out["labels"] = out["input_ids"].copy()  # for true chat SFT, mask non-assistant tokens
    return out

ds = ds.map(tokenize, batched=True, remove_columns=ds.column_names)

args = TrainingArguments(
    output_dir="lora-sft-out",
    per_device_train_batch_size=1,
    gradient_accumulation_steps=16,
    learning_rate=2e-4,
    num_train_epochs=1,
    logging_steps=10,
    save_steps=200,
    fp16=True,
)

trainer = Trainer(model=model, args=args, train_dataset=ds)
trainer.train()

# Save only adapter weights (small files)
model.save_pretrained("lora-adapter")
tokenizer.save_pretrained("lora-adapter")

Importante: O exemplo acima define labels = input_ids por simplicidade. Para ajuste fino supervisionado em chat, em geral você precisa mascarar os rótulos para tokens que não são do assistente; caso contrário, o modelo é treinado para “prever” também o texto do usuário/sistema, o que pode prejudicar o comportamento de seguimento de instruções.

Mesclagem da adaptação de baixo posto para implantação

Uma vantagem prática da adaptação de baixo posto é a flexibilidade de implantação:

  • Manter adaptadores separados: carregar o modelo base uma vez e, então, carregar diferentes adaptadores por locatário (tenant)/tarefa.
  • Mesclar adaptadores nos pesos base para uma inferência mais simples (ao custo de produzir um novo artefato completo do modelo).

A mesclagem pode alterar ligeiramente o comportamento numérico (devido a precisão e quantização (quantization)), então valide após mesclar.

QLoRA: adaptação de baixo posto com pesos base quantizados

QLoRA é uma abordagem amplamente usada que:

  • Carrega o modelo base na forma quantizada em 4 bits (escolhas comuns incluem quantização no estilo NF4 em ferramentas populares)
  • Treina adaptadores de adaptação de baixo posto em maior precisão (por exemplo, fp16/bf16)
  • Reduz drasticamente os requisitos de VRAM, tornando modelos da classe 7B–13B ajustáveis em uma única GPU em muitas configurações

Pontos práticos importantes:

  • A quantização pode afetar a estabilidade; use configurações de otimizador recomendadas e padrões das bibliotecas.
  • Valide cuidadosamente em benchmarks específicos do domínio, especialmente se você depois servir em um modo de precisão diferente.

Outras técnicas práticas de adaptação (além de “ajuste fino supervisionado puro”)

Ajuste fino não é uma técnica única; é uma caixa de ferramentas. Padrões comuns incluem:

Pré-treinamento contínuo (pré-treinamento adaptado ao domínio)

Se seu objetivo é uma competência linguística profunda no domínio (jargão, estilo, sintaxe específica do domínio), pode ajudar fazer pré-treinamento contínuo em texto de domínio não rotulado antes do ajuste fino supervisionado:

  • Objetivo: previsão do próximo token em um corpus do domínio
  • Benefício: melhora fluência e terminologia do domínio
  • Risco: pode causar deriva (drift); você ainda precisa de ajuste fino supervisionado de estilo instrucional depois para o comportamento

Isso costuma ser útil para:

  • documentos internos de engenharia
  • contratos jurídicos
  • artigos biomédicos
  • bases de código proprietárias (sujeitas a restrições de licenciamento e privacidade)

Ajuste para saída estruturada (esquemas, JSON, chamada de função)

Se a produção exige formatação estrita (por exemplo, chamadas de ferramenta em JSON), o ajuste fino supervisionado pode fazer o modelo emitir o esquema de forma confiável.

Dicas práticas:

  • Incluir muitos exemplos com casos de borda e escape
  • Adicionar exemplos em que o assistente recusa ou retorna um objeto de erro estruturado
  • Avaliar com parsers estritos (strict parsers) (não apenas similaridade de texto)

O uso de ferramentas está intimamente ligado a padrões de inferência e pode interagir com a decodificação (decoding) (veja Estratégias de Decodificação)—por exemplo, aleatoriedade demais pode quebrar JSON.

Conjuntos de dados multitarefa e “mistura”

Muitas organizações constroem um conjunto de dados de ajuste fino supervisionado como uma mistura de:

  • seguimento geral de instruções
  • perguntas e respostas do domínio
  • rastros de uso de ferramentas
  • exemplos de estilo
  • exemplos de recusa/segurança

Isso ajuda a evitar esquecimento catastrófico e produz um assistente mais equilibrado. Mas a mistura de conjuntos de dados introduz trade-offs: se uma categoria domina, ela pode “puxar” o comportamento do modelo de forma desproporcional. A ponderação (weighting) e a estratégia de amostragem (sampling strategy) importam.

Composição e roteamento de adaptadores

Com adaptação de baixo posto/ajuste fino eficiente em parâmetros, você pode manter múltiplos adaptadores:

  • adapter_finance
  • adapter_support
  • adapter_legal

Então selecionar, no tempo de inferência, com base em lógica de roteamento (regras ou um classificador (classifier)). Isso é operacionalmente atrativo porque evita retreinar um monólito (monolith) único a cada atualização.

Ajuste de prompt e ajuste de prefixo (brevemente)

Existem outras variantes de ajuste fino eficiente em parâmetros:

  • Ajuste de prompt (prompt tuning): aprender um pequeno conjunto de “tokens virtuais (virtual tokens)” pré-anexados (prepended) às entradas
  • Ajuste de prefixo (prefix tuning): aprender prefixos contínuos (continuous prefixes) injetados na atenção

Elas podem ser eficazes, mas muitas vezes são menos adotadas universalmente para ajuste fino de chat de LLMs do que a adaptação de baixo posto, porque a adaptação de baixo posto tende a ser direta e robusta em diferentes arquiteturas.

Ajuste fino vs. RAG vs. prompting: escolhendo a ferramenta certa

Ajuste fino nem sempre é o melhor primeiro passo.

Use prompting (uso de prompts) / Aprendizado em Contexto quando:

  • você precisa iterar rápido
  • a mudança de comportamento é superficial (tom, formatação)
  • você pode tolerar inconsistência ocasional

Use geração aumentada por recuperação (retrieval-augmented generation, RAG) quando:

  • você precisa de conhecimento atualizado ou grande que muda frequentemente
  • a correção depende de citar fontes
  • o modelo não deve memorizar fatos proprietários

Use ajuste fino (ajuste fino supervisionado/adaptação de baixo posto) quando:

  • você precisa de estilo/formato/chamadas de ferramenta consistentes
  • você quer um comportamento de domínio que generalize além de alguns exemplos
  • você precisa de prompts com menor latência (menos sobrecarga de instruções)
  • você quer que o modelo “simplesmente faça” sem longas estruturas de prompt

Frequentemente, os melhores sistemas de produção usam RAG + ajuste fino: ajuste fino para comportamento/uso de ferramentas, recuperação para fatos.

Avaliação e depuração prática

Uma execução de ajuste fino é tão boa quanto seu ciclo de avaliação (evaluation loop). Camadas úteis incluem:

  • Conjunto de validação retido (held-out) da mesma distribuição do treinamento
  • Avaliações comportamentais (behavioral evals): prompts curados que refletem fluxos reais de usuário
  • Testes de regressão (regression tests): garantir que capacidades antigas permaneçam intactas
  • Avaliações de segurança: consistência de recusa, conformidade com políticas (veja Mitigações de Segurança)
  • Verificações de esquema/parsing (schema/parse checks) para saídas estruturadas
  • Revisão humana (human review) para correção e tom em uma amostra representativa

Quando os resultados são ruins, culpados comuns são:

  • Modelo de chat errado ou inconsistente
  • Rótulos não mascarados corretamente no ajuste fino supervisionado de chat
  • Taxa de aprendizado alta demais (especialmente para ajuste fino completo)
  • Contaminação do conjunto de dados (dataset contamination) (exemplos de treinamento muito próximos dos prompts de avaliação)
  • Super-representação de uma categoria de comportamento (por exemplo, recusa em todo lugar)

Um checklist prático de ajuste fino (SFT + LoRA)

  • Defina o comportamento-alvo com precisão (tom, tamanho, formato de ferramenta, regras de recusa).
  • Comece com um conjunto de dados pequeno e de alta qualidade; itere antes de escalar.
  • Garanta que a formatação corresponde ao modelo de chat do modelo base.
  • Masqueie a perda para treinar apenas nos tokens do assistente (para ajuste fino supervisionado de chat).
  • Prefira adaptação de baixo posto/QLoRA a menos que você tenha um motivo para ajuste fino completo.
  • Acompanhe métricas de avaliação e exemplos qualitativos a cada checkpoint (checkpoint).
  • Verifique regressões na utilidade geral e na segurança.
  • Decida a estratégia de implantação: mesclar adaptadores ou carregar dinamicamente.
  • Versione seus conjuntos de dados e configs de treinamento para reprodutibilidade (reproducibility).

Como o ajuste fino se relaciona com métodos de alinhamento

O ajuste fino supervisionado ensina ao modelo “o que fazer” em um sentido supervisionado. Métodos de alinhamento (por exemplo, abordagens da família DPO (DPO-family approaches)) ensinam ao modelo “qual de duas respostas é melhor” de acordo com sinais de preferência—frequentemente melhorando a utilidade (helpfulness) e reduzindo comportamentos indesejáveis. Em stacks modernos, o ajuste fino supervisionado frequentemente é seguido de otimização por preferência; veja Métodos de Otimização por Preferência e Alinhamento.

O ajuste fino é poderoso, mas não é mágica: é melhor vê-lo como especialização comportamental e estilística (e às vezes adaptação ao domínio), complementado por recuperação para fundamentação factual e otimização por preferência para qualidade de resposta mais nuanceada.