Práticas Recomendadas do Hugging Face Transformers
Guia abrangente para usar a biblioteca Hugging Face Transformers, incluindo carregamento de modelos, tokenização, fluxos de fine-tuning, uso de pipelines, conjuntos de dados personalizados e otimização de deployment.
Referência Rápida
Quando usar este skill:
- Carregar e usar transformers pré-treinados (BERT, GPT, T5, LLaMA, etc.)
- Fine-tuning de modelos em dados personalizados
- Implementar tarefas de PLN (classificação, QA, geração, etc.)
- Otimizar inferência (quantização, ONNX, etc.)
- Depurar problemas de tokenização
- Usar pipelines do Hugging Face
- Implantar transformers em produção
Modelos abordados:
- Encoders: BERT, RoBERTa, DeBERTa, ALBERT
- Decoders: GPT-2, GPT-Neo, LLaMA, Mistral
- Encoder-Decoders: T5, BART, Flan-T5
- Visão: ViT, CLIP, Stable Diffusion
Parte 1: Padrões de Carregamento de Modelos
Padrão 1: Carregamento Básico de Modelo
from transformers import AutoModel, AutoTokenizer
# Carregar modelo e tokenizer
model_name = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModel.from_pretrained(model_name)
# Para tarefas específicas
from transformers import AutoModelForSequenceClassification
model = AutoModelForSequenceClassification.from_pretrained(
model_name,
num_labels=3 # Para classificação de 3 classes
)
Padrão 2: Carregamento com Configuração Específica
from transformers import AutoConfig, AutoModel
# Modificar a configuração
config = AutoConfig.from_pretrained("bert-base-uncased")
config.hidden_dropout_prob = 0.2 # Dropout personalizado
config.attention_probs_dropout_prob = 0.2
# Carregar modelo com configuração personalizada
model = AutoModel.from_pretrained("bert-base-uncased", config=config)
# Ou criar modelo do zero com a configuração
model = AutoModel.from_config(config)
Padrão 3: Carregamento de Modelos Quantizados (Eficiência de Memória)
from transformers import AutoModel, BitsAndBytesConfig
import torch
# Quantização de 8 bits (redução de 50% da memória)
quantization_config = BitsAndBytesConfig(load_in_8bit=True)
model = AutoModel.from_pretrained(
"meta-llama/Llama-2-7b-hf",
quantization_config=quantization_config,
device_map="auto" # Posicionamento automático em dispositivos
)
# Quantização de 4 bits (redução de 75% da memória)
quantization_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_compute_dtype=torch.float16,
bnb_4bit_quant_type="nf4",
bnb_4bit_use_double_quant=True
)
model = AutoModel.from_pretrained(
"meta-llama/Llama-2-13b-hf",
quantization_config=quantization_config,
device_map="auto"
)
Padrão 4: Carregamento a partir de Caminho Local
# Salvar modelo localmente
model.save_pretrained("./meu-modelo")
tokenizer.save_pretrained("./meu-modelo")
# Carregar do caminho local
model = AutoModel.from_pretrained("./meu-modelo")
tokenizer = AutoTokenizer.from_pretrained("./meu-modelo")
Parte 2: Práticas Recomendadas de Tokenização
Padrões Críticos de Tokenização
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
# ✅ CORRETO: Todos os argumentos necessários
tokens = tokenizer(
texto,
padding=True, # Preencher até o maior da sequência
truncation=True, # Truncar para max_length
max_length=512, # Comprimento máximo da sequência
return_tensors="pt" # Retornar tensores PyTorch
)
# Acessar componentes
input_ids = tokens['input_ids'] # IDs dos tokens
attention_mask = tokens['attention_mask'] # Máscara de preenchimento
token_type_ids = tokens.get('token_type_ids') # IDs de segmento (BERT)
# ❌ ERRADO: Faltando argumentos críticos
tokens = tokenizer(texto) # Sem padding, truncamento ou formato de tensor!
Tokenização em Lote
# Tokenizar múltiplos textos eficientemente
textos = ["Primeiro texto", "Segundo texto", "Terceiro texto"]
tokens = tokenizer(
textos,
padding=True, # Preencher todos até o maior do lote
truncation=True,
max_length=128,
return_tensors="pt"
)
# Forma resultante: [tamanho_do_lote, comprimento_máximo]
print(tokens['input_ids'].shape) # torch.Size([3, max_len_no_lote])
Manipulação de Tokens Especiais
# Adicionar tokens especiais
tokenizer.add_special_tokens({
'additional_special_tokens': ['[PERSONALIZADO]', '[MARCADOR]']
})
# Redimensionar embeddings do modelo para corresponder
model.resize_token_embeddings(len(tokenizer))
# Codificar com tokens especiais preservados
texto = "Olá [PERSONALIZADO] mundo"
tokens = tokenizer(texto, add_special_tokens=True)
# Decodificar
decodificado = tokenizer.decode(tokens['input_ids'][0], skip_special_tokens=False)
Tokenização para Diferentes Tarefas
# Classificação de texto (sequência única)
tokens = tokenizer(
"Este filme foi ótimo!",
padding="max_length",
truncation=True,
max_length=128,
return_tensors="pt"
)
# Resposta a perguntas (par de sequências)
pergunta = "Qual é a capital da França?"
contexto = "A França é um país na Europa. Paris é sua capital."
tokens = tokenizer(
pergunta,
contexto,
padding="max_length",
truncation="only_second", # Truncar apenas o contexto
max_length=384,
return_tensors="pt"
)
# Geração de texto (modelos decoder-only)
prompt = "Era uma vez"
tokens = tokenizer(prompt, return_tensors="pt")
# Nenhum padding necessário para entrada de geração
Parte 3: Fluxos de Fine-Tuning
Padrão 1: Fine-Tuning Simples com Trainer
from transformers import (
AutoModelForSequenceClassification,
AutoTokenizer,
Trainer,
TrainingArguments
)
from datasets import load_dataset
# 1. Carregar conjunto de dados
dataset = load_dataset("glue", "mrpc")
# 2. Carregar modelo
model = AutoModelForSequenceClassification.from_pretrained(
"bert-base-uncased",
num_labels=2
)
tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
# 3. Tokenizar conjunto de dados
def tokenize_function(exemplos):
return tokenizer(
exemplos["sentence1"],
exemplos["sentence2"],
padding="max_length",
truncation=True,
max_length=128
)
tokenized_datasets = dataset.map(tokenize_function, batched=True)
# 4. Definir argumentos de treinamento
training_args = TrainingArguments(
output_dir="./resultados",
evaluation_strategy="epoch",
learning_rate=2e-5,
per_device_train_batch_size=16,
per_device_eval_batch_size=16,
num_train_epochs=3,
weight_decay=0.01,
logging_dir="./logs",
logging_steps=100,
save_strategy="epoch",
load_best_model_at_end=True,
metric_for_best_model="accuracy",
)
# 5. Definir métricas
from datasets import load_metric
import numpy as np
metric = load_metric("accuracy")
def compute_metrics(eval_pred):
logits, labels = eval_pred
predictions = np.argmax(logits, axis=-1)
return metric.compute(predictions=predictions, references=labels)
# 6. Criar Trainer
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized_datasets["train"],
eval_dataset=tokenized_datasets["validation"],
compute_metrics=compute_metrics,
)
# 7. Treinar
trainer.train()
# 8. Salvar
trainer.save_model("./modelo-finetuned")
Padrão 2: Fine-Tuning com LoRA (Eficiente em Parâmetros)
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import LoraConfig, get_peft_model, TaskType
# Carregar modelo base
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-2-7b-hf",
load_in_8bit=True, # 8 bits para eficiência de memória
device_map="auto"
)
# Configurar LoRA
lora_config = LoraConfig(
task_type=TaskType.CAUSAL_LM,
r=8, # Rank LoRA
lora_alpha=32, # Alpha LoRA
lora_dropout=0.1,
target_modules=["q_proj", "v_proj"], # Quais camadas adaptar
)
# Aplicar LoRA
model = get_peft_model(model, lora_config)
# Verificar parâmetros treináveis
model.print_trainable_parameters()
# Saída: trainable params: 4.2M || all params: 6.7B || trainable%: 0.062%
# Treinar com Trainer (mesmo de antes)
# Apenas parâmetros LoRA são atualizados!
Padrão 3: Loop de Treinamento Personalizado
import torch
from torch.utils.data import DataLoader
from transformers import AdamW, get_scheduler
# Preparar dataloaders
train_dataloader = DataLoader(tokenized_datasets["train"], batch_size=16, shuffle=True)
eval_dataloader = DataLoader(tokenized_datasets["validation"], batch_size=16)
# Otimizador
optimizer = AdamW(model.parameters(), lr=2e-5)
# Agendador de taxa de aprendizado
num_epochs = 3
num_training_steps = num_epochs * len(train_dataloader)
lr_scheduler = get_scheduler(
"linear",
optimizer=optimizer,
num_warmup_steps=500,
num_training_steps=num_training_steps
)
# Loop de treinamento
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
for epoch in range(num_epochs):
model.train()
for batch in train_dataloader:
batch = {k: v.to(device) for k, v in batch.items()}
outputs = model(**batch)
loss = outputs.loss
loss.backward()
optimizer.step()
lr_scheduler.step()
optimizer.zero_grad()
# Avaliação
model.eval()
for batch in eval_dataloader:
batch = {k: v.to(device) for k, v in batch.items()}
with torch.no_grad():
outputs = model(**batch)
# Calcular métricas
Parte 4: Uso de Pipelines (API de Alto Nível)
Pipeline de Classificação de Texto
from transformers import pipeline
# Carregar pipeline
classifier = pipeline(
"text-classification",
model="distilbert-base-uncased-finetuned-sst-2-english"
)
# Predição única
resultado = classifier("Eu amo este produto!")
# [{'label': 'POSITIVE', 'score': 0.9998}]
# Predição em lote
resultados = classifier([
"Ótimo serviço!",
"Experiência terrível",
"Qualidade mediana"
])
Pipeline de Resposta a Perguntas
qa_pipeline = pipeline("question-answering", model="distilbert-base-uncased-distilled-squad")
resultado = qa_pipeline(
question="Qual é a capital da França?",
context="A França é um país na Europa. Sua capital é Paris, uma bela cidade."
)
# {'score': 0.98, 'start': 49, 'end': 54, 'answer': 'Paris'}
Pipeline de Geração de Texto
generator = pipeline("text-generation", model="gpt2")
outputs = generator(
"Era uma vez",
max_length=50,
num_return_sequences=3,
temperature=0.7,
top_k=50,
top_p=0.95,
do_sample=True
)
for output in outputs:
print(output['generated_text'])
Pipeline de Classificação Zero-Shot
classifier = pipeline("zero-shot-classification", model="facebook/bart-large-mnli")
resultado = classifier(
"Este é um curso sobre programação Python.",
candidate_labels=["educação", "tecnologia", "negócios", "esportes"]
)
# {'sequence': '...', 'labels': ['educação', 'tecnologia', ...], 'scores': [0.85, 0.12, ...]}
Parte 5: Otimização de Inferência
Otimização 1: Processamento em Lote
# ❌ LENTO: Processar um de cada vez
for texto in textos:
output = model(**tokenizer(texto, return_tensors="pt"))
# ✅ RÁPIDO: Processar em lotes
tamanho_lote = 32
for i in range(0, len(textos), tamanho_lote):
lote = textos[i:i+tamanho_lote]
inputs = tokenizer(lote, padding=True, truncation=True, return_tensors="pt")
outputs = model(**inputs)
Otimização 2: Precisão Mista (AMP)
from torch.cuda.amp import autocast, GradScaler
scaler = GradScaler()
for batch in dataloader:
optimizer.zero_grad()
# Passagem direta em precisão mista
with autocast():
outputs = model(**batch)
loss = outputs.loss
# Passagem reversa com gradientes escalados
scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()
Otimização 3: Exportação ONNX
from transformers import AutoModelForSequenceClassification, AutoTokenizer
from optimum.onnxruntime import ORTModelForSequenceClassification
# Exportar para ONNX
model = AutoModelForSequenceClassification.from_pretrained("bert-base-uncased")
model.save_pretrained("./onnx-model", export=True)
# Carregar modelo ONNX (inferência mais rápida)
ort_model = ORTModelForSequenceClassification.from_pretrained("./onnx-model")
# Inferência (2-3x mais rápida)
tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
inputs = tokenizer("Olá mundo", return_tensors="pt")
outputs = ort_model(**inputs)
Otimização 4: Quantização Dinâmica
import torch
# Quantizar modelo para int8
modelo_quantizado = torch.quantization.quantize_dynamic(
model,
{torch.nn.Linear}, # Quantizar camadas Lineares
dtype=torch.qint8
)
# Modelo 4x menor, inferência 2-3x mais rápida em CPU
Parte 6: Problemas Comuns e Soluções
Problema 1: CUDA sem Memória
Problema: RuntimeError: CUDA out of memory
Soluções:
# Solução 1: Reduzir tamanho do lote
training_args = TrainingArguments(
per_device_train_batch_size=8, # Era 32
gradient_accumulation_steps=4, # Lote efetivo = 8*4 = 32
)
# Solução 2: Usar checkpointing de gradientes
model.gradient_checkpointing_enable()
# Solução 3: Usar modelo de 8 bits
from transformers import BitsAndBytesConfig
quantization_config = BitsAndBytesConfig(load_in_8bit=True)
model = AutoModel.from_pretrained("model-name", quantization_config=quantization_config)
# Solução 4: Limpar cache
import torch
torch.cuda.empty_cache()
Problema 2: Tokenização Lenta
Problema: Tokenização é o gargalo
Soluções:
# Solução 1: Usar tokenizers rápidos
tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased", use_fast=True)
# Solução 2: Tokenizar conjunto de dados uma vez, armazenar em cache
tokenized_dataset = dataset.map(
tokenize_function,
batched=True,
num_proc=4, # Processamento paralelo
remove_columns=dataset.column_names,
load_from_cache_file=True # Armazenar resultados em cache
)
# Solução 3: Usar lotes maiores para tokenização
tokenizer(
textos,
padding=True,
truncation=True,
max_length=512,
return_tensors="pt",
batched=True, # Processar múltiplos textos de uma vez
batch_size=1000
)
Problema 3: Resultados Inconsistentes
Problema: Modelo produz resultados diferentes para a mesma entrada
Solução:
# Definir sementes para reprodutibilidade
import random
import numpy as np
import torch
random.seed(42)
np.random.seed(42)
torch.manual_seed(42)
torch.cuda.manual_seed_all(42)
# Para modelos de geração, defina também
outputs = model.generate(
**inputs,
do_sample=False # Desabilitar amostragem para resultados determinísticos
)