Como Fazer Fine-Tuning com Hugging Face Transformers
Guia passo a passo para fazer fine-tuning de modelos de linguagem usando a biblioteca Transformers do Hugging Face.
Lembro da primeira vez que tentei treinar um modelo do zero para classificação de texto em português. Semanas de trabalho, milhares de exemplos coletados manualmente, GPU alugada por dias — e o resultado foi medíocre comparado a um BERT pré-treinado que eu fine-tunei em horas com cem vezes menos dados.
Essa experiência me ensinou a diferença entre trabalhar duro e trabalhar inteligente em ML. Fine-tuning é uma das técnicas de maior alavancagem que existe: você pega um modelo treinado em bilhões de tokens e o especializa no seu domínio com recursos que qualquer desenvolvedor tem acesso. Vou mostrar exatamente como fiz isso em projetos reais. Se depois de dominar o fine-tuning você quiser expor o modelo como serviço, confira o tutorial de API REST com FastAPI.
Por que Fine-Tuning?
Modelos pré-treinados como BERT, RoBERTa e GPT são poderosos mas genéricos. Eles foram treinados em enormes volumes de texto da internet e possuem conhecimento geral sobre linguagem, mas não conhecem o seu domínio específico.
O fine-tuning é o processo de continuar o treinamento de um modelo pré-treinado com seus próprios dados. O resultado é um modelo que combina o conhecimento linguístico geral com expertise no seu domínio específico — seja classificação de sentimento, detecção de spam, chatbot de suporte técnico, geração de código, entre outros.
A principal vantagem é que você precisa de muito menos dados do que treinar um modelo do zero. Um modelo BERT pode ser fine-tunado com apenas alguns milhares de exemplos e ainda assim produzir resultados excelentes.
Requisitos
- Python 3.10 ou superior
- GPU NVIDIA com CUDA (recomendado) ou CPU
- 8 GB de RAM (mínimo); 16 GB recomendado para modelos maiores
Instalação
pip install transformers datasets accelerate peft bitsandbytes scikit-learn
Para verificar se a GPU está disponível:
import torch
print(f"GPU disponível: {torch.cuda.is_available()}")
print(f"Dispositivo: {torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'CPU'}")
1. Carregar Dataset
O Hugging Face Hub tem milhares de datasets prontos. Aqui vamos usar um dataset de análise de sentimento em português:
from datasets import load_dataset, Dataset, DatasetDict
import pandas as pd
# Opção 1: Usar dataset público do Hugging Face Hub
# O dataset "imdb" é em inglês, mas serve para ilustrar o processo
# Para português, use seu próprio dataset (ver Opção 2)
dataset = load_dataset("imdb")
# Colunas: "text" (texto da review) e "label" (0=negativo, 1=positivo)
# Opção 2: Usar um CSV com seus dados (recomendado para dados em português)
# Estrutura esperada do CSV: coluna "texto" e coluna "label" (0 ou 1)
#
# df = pd.read_csv("reviews_portugues.csv")
# # Garantir que os nomes de coluna estão corretos
# df = df.rename(columns={"review": "texto", "sentimento": "label"})
# df["label"] = df["label"].map({"positivo": 1, "negativo": 0})
# dataset = Dataset.from_pandas(df)
# # Dividir em treino/validação/teste
# split = dataset.train_test_split(test_size=0.2, seed=42)
# dataset = split["train"].train_test_split(test_size=0.1, seed=42)
# dataset = DatasetDict({
# "train": dataset["train"],
# "validation": dataset["test"],
# "test": split["test"]
# })
print(f"Total de exemplos (treino): {len(dataset['train'])}")
print(f"Colunas disponíveis: {dataset['train'].column_names}")
print(f"\nExemplo de entrada:")
print(dataset["train"][0])
Para usar datasets locais em formato JSON ou CSV:
from datasets import load_dataset
# A partir de arquivos locais
dataset = load_dataset("json", data_files={
"train": "dados/treino.json",
"validation": "dados/validacao.json",
"test": "dados/teste.json"
})
2. Tokenização
A tokenização converte texto em IDs numéricos que o modelo entende. Cada modelo tem seu próprio tokenizador — é importante usar o tokenizador do mesmo modelo que você vai fazer fine-tuning:
from transformers import AutoTokenizer
# Modelo BERT treinado para português
model_name = "neuralmind/bert-base-portuguese-cased"
tokenizer = AutoTokenizer.from_pretrained(model_name)
def tokenize(examples):
"""Tokeniza um batch de exemplos."""
return tokenizer(
examples["text"], # coluna com o texto (ajuste para o nome da sua coluna)
padding="max_length",
truncation=True,
max_length=128 # BERT tem limite de 512, mas 128 é suficiente para reviews
)
# Aplicar tokenização ao dataset inteiro de forma eficiente
tokenized_dataset = dataset.map(tokenize, batched=True)
# Verificar resultado
print(f"Tokens de um exemplo: {tokenized_dataset['train'][0]['input_ids'][:20]}...")
print(f"Comprimento: {len(tokenized_dataset['train'][0]['input_ids'])}")
3. Configurar o Modelo
Carregamos o modelo pré-treinado e adicionamos uma cabeça de classificação no topo:
from transformers import AutoModelForSequenceClassification
# 2 classes: positivo (1) e negativo (0)
model = AutoModelForSequenceClassification.from_pretrained(
model_name,
num_labels=2,
id2label={0: "NEGATIVO", 1: "POSITIVO"},
label2id={"NEGATIVO": 0, "POSITIVO": 1}
)
# Verificar parâmetros treináveis
total_params = sum(p.numel() for p in model.parameters())
trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
print(f"Parâmetros totais: {total_params:,}")
print(f"Parâmetros treináveis: {trainable_params:,}")
4. Treinar com o Trainer
O Trainer do Hugging Face cuida de todo o loop de treinamento, avaliação, logging e checkpoints automaticamente:
from transformers import TrainingArguments, Trainer
import numpy as np
from sklearn.metrics import accuracy_score, f1_score
def compute_metrics(eval_pred):
"""Calcula métricas de avaliação durante o treinamento."""
logits, labels = eval_pred
predictions = np.argmax(logits, axis=-1)
return {
"accuracy": accuracy_score(labels, predictions),
"f1": f1_score(labels, predictions, average="weighted"),
}
training_args = TrainingArguments(
output_dir="./resultados-fine-tuning",
num_train_epochs=3, # 3 épocas é um bom ponto de partida
per_device_train_batch_size=16,
per_device_eval_batch_size=32,
warmup_steps=500, # Aquecimento do learning rate
weight_decay=0.01, # Regularização L2
learning_rate=2e-5, # Taxa de aprendizado baixa para fine-tuning
logging_dir="./logs",
logging_steps=50,
eval_strategy="epoch",
save_strategy="epoch",
load_best_model_at_end=True,
metric_for_best_model="f1", # Salvar o modelo com melhor F1
fp16=True, # Usar float16 para treinar mais rápido (GPU)
report_to="none", # Desativar W&B/MLflow por padrão
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized_dataset["train"],
eval_dataset=tokenized_dataset["test"], # IMDB usa "test" como validação
compute_metrics=compute_metrics,
)
# Iniciar treinamento
print("Iniciando fine-tuning...")
trainer.train()
# Avaliar no conjunto de teste
print("\nAvaliação no conjunto de teste:")
results = trainer.evaluate(tokenized_dataset["test"])
print(f"Accuracy: {results['eval_accuracy']:.4f}")
print(f"F1 Score: {results['eval_f1']:.4f}")
5. Usar o Modelo Treinado
Após o treinamento, salve e use o modelo em produção:
# Salvar modelo e tokenizador
model.save_pretrained("./meu-modelo-sentimento")
tokenizer.save_pretrained("./meu-modelo-sentimento")
print("Modelo salvo em ./meu-modelo-sentimento")
Para carregar e usar em produção:
from transformers import pipeline
# Carregar modelo treinado
classifier = pipeline(
"text-classification",
model="./meu-modelo-sentimento",
tokenizer="./meu-modelo-sentimento"
)
# Testar
textos = [
"Esse produto é incrível! Chegou antes do prazo e funciona perfeitamente.",
"Péssima experiência. O produto veio com defeito e o suporte não ajudou.",
"Produto ok, dentro do esperado.",
]
for texto in textos:
resultado = classifier(texto)[0]
print(f"\nTexto: {texto}")
print(f" → {resultado['label']} (confiança: {resultado['score']:.1%})")
Resultado Esperado
Texto: Esse produto é incrível! Chegou antes do prazo e funciona perfeitamente.
→ POSITIVO (confiança: 98.3%)
Texto: Péssima experiência. O produto veio com defeito e o suporte não ajudou.
→ NEGATIVO (confiança: 97.1%)
Texto: Produto ok, dentro do esperado.
→ POSITIVO (confiança: 62.4%)
Fine-Tuning Eficiente com LoRA (para modelos grandes)
Para modelos com bilhões de parâmetros, fazer fine-tuning completo exige GPUs caras. O LoRA (Low-Rank Adaptation) resolve isso treinando apenas matrizes de adaptação de baixa dimensão, reduzindo os parâmetros treináveis em até 10.000x:
from peft import LoraConfig, get_peft_model, TaskType
from transformers import AutoModelForCausalLM
# Carregar modelo base (ex: Llama ou similar)
base_model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-3.2-3B",
load_in_8bit=True, # Quantização 8-bit para economizar VRAM
device_map="auto"
)
# Configurar LoRA
lora_config = LoraConfig(
task_type=TaskType.CAUSAL_LM,
r=8, # Rank da decomposição (menor = mais eficiente, menos expressivo)
lora_alpha=16, # Escala para os pesos LoRA
lora_dropout=0.1,
target_modules=["q_proj", "v_proj"], # Quais layers aplicar LoRA
)
# Aplicar LoRA ao modelo
model_lora = get_peft_model(base_model, lora_config)
model_lora.print_trainable_parameters()
# Saída: trainable params: 4,194,304 || all params: 3,215,681,536 || trainable%: 0.13
Com LoRA, é possível fazer fine-tuning de modelos de 3B parâmetros em uma GPU de 8 GB de VRAM — algo impraticável com treinamento completo.
Dicas Importantes
-
Comece pequeno: use um subset dos dados (1.000–5.000 exemplos) para validar o pipeline completo antes de treinar com o dataset inteiro. Isso evita desperdiçar horas de GPU em configurações erradas.
-
Learning rate baixo: para fine-tuning, use taxas entre
2e-5e5e-5. Taxas mais altas podem destruir o conhecimento pré-treinado do modelo (catastrofic forgetting). -
Poucas épocas: 2 a 5 épocas geralmente são suficientes. Mais do que isso tende a causar overfitting.
-
Early stopping: monitore a loss de validação e pare o treinamento quando ela parar de diminuir:
from transformers import EarlyStoppingCallback
trainer = Trainer(
...
callbacks=[EarlyStoppingCallback(early_stopping_patience=2)]
)
-
Dados de qualidade: a qualidade do dataset é mais importante que a quantidade. 500 exemplos bem rotulados superam 5.000 exemplos com ruído.
-
Balanceamento: se o dataset for muito desbalanceado (ex: 90% positivo, 10% negativo), use técnicas de oversampling ou pesos de classe:
import torch
# Pesos para compensar desbalanceamento (mais peso para a classe minoritária)
class_weights = torch.tensor([1.0, 9.0]) # [peso_classe_0, peso_classe_1]
Próximos Passos
- Fine-tuning com LoRA para modelos de 7B+ parâmetros: use QLoRA (quantização 4-bit + LoRA) para treinar modelos grandes em GPUs acessíveis.
- RLHF básico: ajuste o modelo com feedback humano usando a biblioteca
trl. - Publicar no Hub: compartilhe seu modelo fine-tunado na comunidade Hugging Face com
model.push_to_hub("seu-usuario/nome-do-modelo"). - Inferência eficiente: use quantização INT8 ou INT4 para acelerar a inferência em produção sem perda significativa de qualidade.
Sobre o autor
Paulo Cesar
Desenvolvedor sênior com mais de 20 anos de experiência em desenvolvimento de software, IA aplicada, automação e sistemas financeiros. Criador do Inteligência em Código, onde compartilha conhecimento prático sobre inteligência artificial para desenvolvedores brasileiros.