◈ TRIADIC.LIFE / Engenharia de IA / Módulo 11 0/11 aulas
Módulo 11 · 32h estimadas

Modelos Abertos, Fine-Tuning e Destilação

Ajustar um modelo parece o caminho natural para "ensinar a empresa à IA", e quase nunca é o primeiro. Este módulo ensina quando vale a pena, como preparar os dados (a parte que decide o resultado), como o LoRA funciona por dentro, como um modelo grande ensina um pequeno e como medir se tudo isso compensou.

7 aulas3 labs offline + 1 para ColabPythonPré-requisitos: Módulos 1 e 3

Ao final do módulo você vai

  • Decidir entre prompt, RAG, ajuste fino e modelos próprios com critérios claros.
  • Rodar modelos abertos localmente e entender quantização e licenças.
  • Preparar datasets de ajuste limpos, sem dados pessoais e sem vazamento.
  • Explicar e implementar LoRA, e aplicá-lo com bibliotecas reais.
  • Destilar um modelo grande num pequeno e combiná-los numa cascata.
  • Avaliar modelos ajustados contra os genéricos, inclusive fora da distribuição do treino.
cd o-arco/curso-ia/codigo/modulo-11/python
pip install -r requirements.txt
python 01_preparar_dataset.py && python 02_lora_do_zero.py && python 03_destilacao.py

11.1 Quando ajustar um modelo (e quando não)

Leitura · 1h

Suba os degraus na ordem, e só quando o anterior comprovadamente não bastar (medido no seu eval):

DegrauResolveNão resolve
1. Prompt + exemplosFormato, tom, regras, a maioria das tarefasVolume altíssimo com custo apertado
2. RAG (Módulo 4)Conhecimento da empresa, fatos que mudam, citaçõesEstilo ou comportamento muito específico
3. Modelo menor + bom promptCusto e latênciaTarefas que o modelo menor não domina
4. Ajuste fino / destilaçãoTarefa estreita, repetitiva, em alto volume; formato muito rígido; modelo pequeno ou local que precisa chegar perto do grandeInjetar conhecimento que muda (desatualiza e não cita fonte)
5. Treinar do zeroQuase nunca faz sentido fora de laboratórios de pesquisa—
O erro mais comumAjustar um modelo para ele "saber" os produtos ou políticas da empresa. Fatos mudam, o modelo ajustado fica desatualizado, alucina com mais confiança e não cita a fonte. Fatos pertencem ao RAG; o ajuste ensina comportamento.

Quiz rápido

Qual problema é o melhor candidato a fine-tuning ou destilação?

Conhecimento que muda pede RAG. Tarefa estreita, estável e de alto volume, com um professor que já acerta, é onde ajuste e destilação pagam.

11.2 Modelos abertos e inferência local

Leitura · 3h
  • Famílias de pesos abertos: Llama, Qwen, Mistral, Gemma, DeepSeek e outras, de menos de 1 bilhão a centenas de bilhões de parâmetros. Compare pelo seu eval, não pelo ranking.
  • Licenças: "aberto" nem sempre é livre para uso comercial; algumas licenças limitam escala de usuários ou usos. Leia antes de construir em cima.
  • Quantização: guardar pesos em 8 ou 4 bits em vez de 16 reduz memória (um modelo de 7B cai de ~14 GB para ~4 GB) com perda pequena de qualidade, a ser medida.
  • Para rodar: Ollama e llama.cpp (notebook e desenvolvimento), vLLM e servidores de inferência (produção, alto volume, GPUs; veja a aula 11.9).
# Ollama: baixar e rodar um modelo pequeno localmente
ollama pull qwen2.5:3b
ollama run qwen2.5:3b "Classifique: 'cobraram duas vezes no cartão' → cobranca, entrega, defeito ou duvida?"
# API local em http://localhost:11434, para usar do seu código
API de modelo proprietárioModelo aberto próprio
CapacidadeEm geral, a maiorMenor, mas suficiente para muitas tarefas estreitas
CustoPor token, sem custo fixoGPU fixa; barato só com volume alto e constante
DadosSaem para o provedor (com contrato)Ficam na sua infraestrutura
OperaçãoNenhumaSua: escala, atualização, monitoramento, segurança

11.3 Preparando datasets de ajuste

Leitura + lab · 4h

A qualidade do ajuste é a qualidade dos dados. O lab transforma uma exportação bruta de chamados num dataset pronto:

LAB 11.3 — Da exportação bruta ao JSONL

python 01_preparar_dataset.py
LIMPEZA
  lidos                        428
  mascarado [CPF]              103
  mascarado [EMAIL]            93
  mascarado [TELEFONE]         93
  descartado: sem rótulo       1
  descartado: duplicata        172
  descartado: vazio ou curto   2
  exemplos finais              253

DIVISÃO (estratificada)
  treino      175  cobranca=58  defeito=37  duvida=12  entrega=68
  validacao    39  cobranca=13  defeito=8  duvida=3  entrega=15
  teste        39  cobranca=13  defeito=8  duvida=3  entrega=15

Repare nas 172 duplicatas: muitas só apareceram depois do mascaramento, porque eram o mesmo chamado com CPFs diferentes. Deduplicar antes de dividir evita que o mesmo texto esteja no treino e no teste, o que inflaria a nota. O script confere isso com um assert.

  • Formato de conversa ({"messages": [system, user, assistant]}), o mais comum nas APIs e bibliotecas.
  • Dados pessoais fora: um modelo ajustado pode repetir trechos do treino.
  • Equilíbrio: 12 exemplos de "duvida" contra 68 de "entrega" fazem o modelo errar justamente a classe rara. Colete mais ou gere exemplos sintéticos (revisados).
  • Diversidade importa mais que volume: centenas de exemplos variados e corretos valem mais que milhares repetitivos.

11.4 Fine-tuning gerenciado por API

Leitura · 2h

Vários provedores e nuvens oferecem ajuste fino gerenciado: você envia o JSONL, escolhe alguns hiperparâmetros e recebe um modelo ajustado, hospedado por eles. A disponibilidade muda por modelo e por provedor; confira a documentação de quem você usa.

Enviar treino e validação→Criar o job (épocas, taxa de aprendizado)→Acompanhar a perda de treino e de validação→Avaliar o modelo ajustado no seu eval→Versionar e implantar
  • Épocas: poucas (1 a 3) para datasets médios. Validação subindo enquanto o treino cai é overfitting (você viu isso no Módulo 1).
  • Custo: treino cobrado por token processado (tokens × épocas), mais o uso do modelo ajustado, às vezes com preço diferente do modelo base.
  • Dependência: o modelo ajustado vive no provedor e pode ser descontinuado junto com o modelo base. Guarde os dados e o processo para refazer.

11.5 LoRA e QLoRA, por dentro

Leitura + lab · 5h

Em vez de ajustar todos os pesos W, o LoRA congela W e treina duas matrizes pequenas A (d×r) e B (r×k): W' = W + (α/r)·A·B. Com posto r pequeno, o número de parâmetros treinados cai para uma fração. A hipótese por trás: a mudança necessária para adaptar um modelo a uma tarefa nova tem dimensão baixa.

LAB 11.5 — LoRA do zero, em NumPy

python 02_lora_do_zero.py      # ~2 segundos, sem GPU
Modelo base: 17.668 parâmetros · acerto na tarefa A: 88.8%

abordagem                   treinados % do total  acerto B  acerto A depois
sem ajuste (base)                   0       0.0%     63.3%            88.8%
ajuste completo                17.668     100.0%     80.3%            65.1%
LoRA r=1 (só em W1)               320       1.8%     82.3%            88.8%
LoRA r=2 (só em W1)               640       3.6%     86.3%            88.8%
LoRA r=4 (só em W1)             1.280       7.2%     85.6%            88.8%
LoRA r=8 (só em W1)             2.560      14.5%     84.0%            88.8%
  • Esquecimento catastrófico: o ajuste completo aprendeu a tarefa B e caiu de 88,8% para 65,1% na A. Com LoRA, a base fica intacta: tirando o adaptador, a tarefa A volta inteira.
  • O melhor posto foi r=2, exatamente a dimensão da mudança entre as tarefas (o experimento foi construído assim). Na vida real você não conhece essa dimensão: r é um hiperparâmetro a medir.
  • Vários adaptadores, uma base: um adaptador por cliente ou por tarefa, trocados na hora, sobre o mesmo modelo carregado.

QLoRA é o mesmo com a base quantizada em 4 bits, o que permite ajustar modelos de bilhões de parâmetros numa única GPU modesta. O 04_lora_peft_colab.py aplica LoRA com a biblioteca PEFT num modelo aberto de 0,5 B de parâmetros, com a perda calculada só na resposta. Ele precisa de GPU (o Colab gratuito serve) e não roda no ambiente de testes do curso; lá passa só por checagem de sintaxe.

11.6 Destilação e cascata

Leitura + lab · 4h

Destilação: um modelo grande (professor) rotula muitos exemplos; um modelo pequeno (aluno) aprende com esses rótulos. Na cascata, o aluno responde quando está confiante e passa para o professor quando não está.

LAB 11.6 — Aluno, professor e o mundo real

python 03_destilacao.py                   # offline
python 03_destilacao.py --professor api   # o claude-opus-5-5 rotula o treino
Acerto do aluno:
  teste (mesma origem do treino)                39/39  = 100%   confiança média 0.96
  chamados novos (escritos de outro jeito)       8/20  =  40%   confiança média 0.56

CASCATA nos chamados novos: o aluno responde se estiver confiante; senão, o professor.
   limiar  vai p/ professor  acerto*
      0.0                0%      40%
      0.5               40%      70%
      0.7               85%     100%
      0.9              100%     100%

A lição mais importante do módulo está aqui. O aluno tirou 100% no teste que veio da mesma origem do treino e 40% em 20 chamados escritos à mão com outro vocabulário. Ele aprendeu as palavras do treino, não o problema. Um eval só com dados parecidos com o treino teria aprovado um modelo que falha no mundo real. Por sorte, a confiança dele cai junto, e a cascata usa isso. (No modo offline, os rótulos do dataset fazem o papel do professor; o acerto da cascata supõe que o professor acerta os casos que recebe, o que você deve medir.)

Quiz rápido

O modelo destilado acertou 100% no conjunto de teste do desenvolvimento. O que falta?

No lab, o mesmo modelo caiu para 40% em chamados novos. A cascata (pequeno primeiro, grande quando incerto) protege contra isso.

11.7 Avaliar, comparar e versionar modelos

Leitura · 2h
  • Compare com a linha de base certa: modelo ajustado contra o modelo grande com um bom prompt, no mesmo eval (Módulo 3), em qualidade, custo por tarefa e latência.
  • Teste fora da distribuição: dados reais recentes, escritos por outras pessoas, com outros formatos. É onde modelos ajustados quebram.
  • Regressões em outras capacidades: o ajuste pode piorar o que o modelo fazia bem (esquecimento). Mantenha alguns casos gerais no eval.
  • Versione tudo junto: dataset (com hash), código de preparação, hiperparâmetros, modelo base, adaptador e resultado do eval. Sem isso, não dá para reproduzir nem fazer rollback.
  • Lance como qualquer mudança de modelo: canário pelo gateway (Módulo 9), monitoramento e caminho de volta.

11.8 Dados sintéticos: gerar, filtrar e provar

Leitura + lab · 4h

Faltam exemplos? Um modelo grande pode escrever milhares. O risco não é a falta de volume, é o lixo: duplicatas, cópias do treino, rótulos errados e, o pior, exemplos parecidos com a avaliação, que fazem a nota subir sem o modelo melhorar. O lab gera, filtra e mede o efeito no aluno do Lab 11.6, que cai de 100% para 40% nos chamados escritos de outro jeito.

python 05_dados_sinteticos.py          # usa um conjunto pronto, com problemas plantados
python 05_dados_sinteticos.py --api    # o claude-opus-5-5 gera (36 lotes: categoria × tom × canal) e julga
Filtros:
  ✗ duplicata                       sim=1.00  «gente??? me cobraram o frete…»   [plantado: duplicata exata]
  ✗ cópia do treino                 sim=1.00  «O purificador parou de ligar…»   [plantado: cópia do treino]
  ✗ contaminação                    sim=0.69  «o código de rastreio que vocês mandaram não f…»
  ✗ contaminação                    sim=1.00  «Vi duas transações iguais no extrato…»  [plantado]
  ? rótulo contestado pelo aluno (revisar): 2
Problemas plantados pegos pelos filtros: 8/9
Diversidade (bigramas distintos / total): treino 0.12 · sintéticos 0.92

  treino usado                               teste  chamados novos
  só reais                                    100%             40%
  reais + sintéticos filtrados                100%             50%
  reais + sintéticos SEM filtro               100%             55%
  • Sem filtro parece melhor, e é mentira. O script mostra de onde vem cada ganho: o ponto extra sem filtro é exatamente o chamado da avaliação que vazou para o treino.
  • Contaminação acontece sem querer. O conjunto pronto foi escrito pelo Claude que montou este curso, sem consultar os chamados novos. Mesmo assim, uma das frases dele ("o código de rastreio que vocês mandaram não funciona…") era quase igual a um chamado da avaliação. Geradores escrevem frases naturais, e avaliações também: o filtro é obrigatório.
  • Filtro por palavras tem limite. Um dos ganhos ("O controle remoto não funciona de jeito nenhum") tem um sintético parecido em sentido, mas não em palavras (similaridade 0,25), que o filtro deixou passar; e uma quase duplicata plantada escapou. Em produção, combine palavras com embeddings (Módulo 4) e revise uma amostra à mão.
  • O ganho real é pequeno e precisa ser lido caso a caso: com 20 chamados de avaliação, cada um vale 5 pontos. Os outros três ganhos não têm sintético parecido: o aluno aprendeu vocabulário novo. Um chamado que ele acertava passou a errar.

Receita

  1. Varie de propósito: categorias × tom × canal × tamanho, com sementes reais mostradas como tema, nunca para copiar.
  2. Deduplique dentro do lote e contra o treino; números e nomes viram marcadores antes de comparar.
  3. Separe a avaliação antes de gerar e remova tudo que se pareça com ela.
  4. Revise rótulos: um juiz (outro modelo ou o mesmo com outra instrução) e amostra humana.
  5. Meça o efeito numa avaliação fora da distribuição, caso a caso.

Licenças: confira se os termos do provedor permitem usar as saídas para treinar o modelo que você vai usar.

11.9 Servir modelos abertos em produção

Leitura + lab · 4h

Rodar um modelo no notebook com o Ollama é um passo; servir para muitos usuários é outro. Servidores de inferência como vLLM, TGI e llama.cpp (llama-server) usam lote contínuo: a cada passo, geram um token para todas as conversas ativas juntas. Isso multiplica a vazão e muda as contas de capacidade.

Parte 1 · Quanto de GPU

KV cache por token (16 bits):
  Qwen2.5 7B           56 KiB/token · uma conversa de 8.000 tokens = 0.43 GiB
  Llama 3.1 8B        128 KiB/token · uma conversa de 8.000 tokens = 0.98 GiB
  Llama 3.1 70B       320 KiB/token · uma conversa de 8.000 tokens = 2.44 GiB

Conversas simultâneas com 8.000 tokens (— = não cabe):
  modelo          precisão           L4 24 GB       L40S 48 GB  A100/H100 80 GB
  Qwen2.5 7B      16 bits                   8               50              106
  Llama 3.1 8B    16 bits                   3               21               45
  Llama 3.1 8B    4 bits                   14               32               57
  Llama 3.1 70B   4 bits                    —                1               10
  • Os pesos definem se o modelo cabe; o KV cache define quantas conversas cabem. Ele cresce com o contexto e com o número de usuários.
  • O Qwen2.5 7B guarda metade das cabeças de atenção no cache em relação ao Llama 3.1 8B (4 contra 8, atenção agrupada) e, por isso, comporta mais que o dobro de conversas na mesma GPU.
  • É uma estimativa de ordem de grandeza: confira a arquitetura no config.json do modelo e meça no servidor real.

Parte 2 · Teste de carga

python servidor_simulado.py &                           # sem GPU: imita o lote contínuo
python 07_teste_de_carga.py
# servidor real (vLLM, llama-server ou Ollama):
python 07_teste_de_carga.py --url http://localhost:8000/v1 --modelo Qwen/Qwen2.5-7B-Instruct
 simult.  TTFT p50  TTFT p95  ITL p50  lat p50  lat p95  vazão tok/s  erros
       1      19ms      22ms     13ms     1.6s     1.7s           78      0
       4      34ms      37ms     15ms     1.9s     1.9s          270      0
      16      84ms      90ms     22ms     2.9s     2.9s          712      0
      32     154ms     164ms     32ms     4.2s     4.2s          975      0
      64    4329ms    4339ms     32ms     8.4s     8.4s          978      0

Os números vêm de um servidor simulado (lote máximo de 32), não de uma GPU, mas a forma das curvas é a de um servidor real: a vazão total sobe até o lote encher (78 → 975 tokens/s), enquanto cada usuário vê o texto sair mais devagar (ITL de 13 para 32 ms). Passando do limite, a vazão para de crescer e as conversas esperam na fila: o TTFT salta para mais de 4 segundos. O ponto de operação é a maior concorrência em que o p95 ainda cumpre o seu SLO.

O teste de carga achou um bugNa primeira rodada, o servidor simulado recusava conexões a partir de 16 usuários e o TTFT p95 saltava para 1 segundo: a fila de conexões padrão do servidor HTTP do Python tem só 5 lugares, e o cliente esperava a retransmissão do TCP. Problemas assim só aparecem sob carga, e é por isso que se testa antes de lançar.

Escolhas de produção

  • vLLM para vazão em GPU NVIDIA ou AMD, com API compatível com a da OpenAI; TGI e SGLang são alternativas a comparar com o seu modelo e hardware; llama.cpp para CPU, Apple Silicon e GPUs pequenas; Ollama para desenvolvimento.
  • Quantização (AWQ, GPTQ, FP8) libera memória para mais conversas; meça a qualidade com as evals do Módulo 3 antes de adotar.
  • Coloque o servidor atrás do gateway do Módulo 9: limites, reserva para outro provedor e custo por requisição continuam valendo.
  • Compare o custo por milhão de tokens da GPU (preço da hora ÷ vazão medida no seu SLO) com o de uma API antes de decidir hospedar.

Projeto do módulo: modelo pequeno especializado

Projeto · 10h · Python

Escolha uma tarefa estreita do seu contexto (classificação, extração, roteamento) e entregue:

  1. Dataset preparado com o pipeline do lab 11.3: limpeza, mascaramento, deduplicação, divisão estratificada, checagem de vazamento.
  2. Um conjunto "mundo real" separado, escrito ou coletado de forma independente do treino.
  3. Três concorrentes no mesmo eval: modelo grande com prompt, aluno destilado (lab 11.6) e modelo aberto ajustado com LoRA (lab 11.5b, no Colab).
  4. Uma cascata com limiar escolhido na validação.
  5. Recomendação final com números: qual usar, em que volume compensa, e o plano de reajuste quando os dados mudarem.

Critérios de avaliação

CritérioPeso
Pipeline de dados com checagens25%
Avaliação fora da distribuição20%
Comparação justa dos três concorrentes25%
Cascata com limiar justificado15%
Recomendação e plano de manutenção15%

Recursos para aprofundar

Todos gratuitos