IA
Fine-tuning de LLMs: Quando Vale a Pena (e Quando Não Vale)
30 de outubro de 2024·Paulo de Paula
Fine-tuning virou a resposta padrão para “o modelo não está fazendo o que quero”. Problema: na maioria dos casos, não é a solução certa, e o custo (tempo, dados, dinheiro) não se justifica.
O fluxo de decisão correto
Problema com o modelo
↓
Melhorei o prompt? ────── Não → Melhore o prompt primeiro
↓ Sim
RAG resolve? ──────────── Sim → Use RAG (muito mais simples)
↓ Não
O problema é de FORMATO ─ Sim → Fine-tuning pode ajudar
ou ESTILO de resposta?
↓ Não
O problema é de ─ Sim → Fine-tuning provavelmente não
CONHECIMENTO? resolve — use RAG ou modelo maiorQuando fine-tuning resolve
1. Formato de saída consistente
# Você quer SEMPRE isso:
# {
# "action": "...",
# "confidence": 0.95,
# "reasoning": "..."
# }
#
# Mas o modelo às vezes retorna JSON com campos extras,
# às vezes markdown, às vezes texto livre.
# Fine-tuning treina o modelo a seguir o formato
# de forma mais confiável que instruções no prompt.2. Tom e estilo de marca
Se você precisa que o modelo responda sempre com a voz da sua empresa — tom específico, vocabulário proprietário, sem desvios — fine-tuning é mais eficiente que prompt gigante.
3. Redução de custo com modelos menores
# Cenário: você usa GPT-4 com um prompt de 2000 tokens
# para classificar emails em 5 categorias.
#
# Com fine-tuning em GPT-3.5-turbo (ou Llama 3.1 8B):
# - Modelo menor = custo ~10x menor
# - Prompt menor = sem instrução detalhada de classificação
# - Latência menor
#
# Esse é o caso de uso mais forte para fine-tuning.Quando fine-tuning NÃO resolve
Injetar conhecimento novo
# ❌ Expectativa comum:
# "Vou fazer fine-tuning com minha documentação interna
# para o modelo 'saber' sobre o meu produto."
#
# ❌ O que realmente acontece:
# - O modelo pode memorizar alguns fatos
# - Mas hallucina com confiança em perguntas não vistas no treino
# - RAG é muito mais confiável para esse casoPor que RAG é melhor para conhecimento:
from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings
# Indexa sua documentação
vectorstore = Chroma.from_documents(
documents=docs,
embedding=OpenAIEmbeddings()
)
# Na query: busca os trechos relevantes e inclui no contexto
def answer_question(question: str) -> str:
relevant_docs = vectorstore.similarity_search(question, k=4)
context = "\n\n".join([d.page_content for d in relevant_docs])
# O modelo tem o CONTEXTO EXATO — não precisa "memorizar"
return llm.invoke(f"Contexto:\n{context}\n\nPergunta: {question}")Comportamento errado com pouco dado
# Fine-tuning com < 100 exemplos raramente funciona bem.
# O modelo "decora" os exemplos ao invés de generalizar.
# Recomendado: 500-1000+ exemplos para casos simples,
# mais para tarefas complexas.Preparando dados de fine-tuning (OpenAI)
# Formato JSONL para GPT-3.5-turbo / GPT-4o-mini
import json
training_data = [
{
"messages": [
{
"role": "system",
"content": "Você classifica emails de suporte em: billing, technical, general, urgent."
},
{
"role": "user",
"content": "Meu cartão foi cobrado duas vezes esse mês."
},
{
"role": "assistant",
"content": '{"category": "billing", "priority": "high"}'
}
]
},
# ... mais exemplos
]
# Salva como JSONL
with open('training.jsonl', 'w') as f:
for example in training_data:
f.write(json.dumps(example) + '\n')
# Valida o arquivo
# openai tools fine_tunes.prepare_data -f training.jsonlIniciando o fine-tuning
from openai import OpenAI
client = OpenAI()
# Upload do dataset
file = client.files.create(
file=open("training.jsonl", "rb"),
purpose="fine-tune"
)
# Cria o job
job = client.fine_tuning.jobs.create(
training_file=file.id,
model="gpt-4o-mini-2024-07-18", # mais barato para fine-tuning
hyperparameters={
"n_epochs": 3, # padrão; aumente se underfitting
}
)
print(f"Job criado: {job.id}")
# Monitora
events = client.fine_tuning.jobs.list_events(fine_tuning_job_id=job.id)
for event in events:
print(event.message)Avaliando o resultado
# Sempre compare com o modelo base no seu conjunto de teste
import json
test_cases = [...] # dados que o modelo NÃO viu no treino
def evaluate(model_id: str, test_cases: list) -> float:
correct = 0
for case in test_cases:
response = client.chat.completions.create(
model=model_id,
messages=case['messages'][:-1] # sem a resposta esperada
)
predicted = json.loads(response.choices[0].message.content)
expected = json.loads(case['messages'][-1]['content'])
if predicted['category'] == expected['category']:
correct += 1
return correct / len(test_cases)
base_accuracy = evaluate("gpt-4o-mini", test_cases)
finetuned_accuracy = evaluate(job.fine_tuned_model, test_cases)
print(f"Base: {base_accuracy:.1%} → Fine-tuned: {finetuned_accuracy:.1%}")A regra prática
Tente na ordem:
- Melhor prompt (zero custo)
- Few-shot no prompt (adicionar exemplos)
- RAG (para conhecimento específico)
- Fine-tuning (para formato/estilo consistente ou custo/latência)
- Modelo maior (se nada mais funcionar)
Fine-tuning é caro para criar, caro para manter e frágil com atualizações de modelo. Use quando os benefícios concretos — latência, custo, consistência — justificam o investimento.