Capítulo 1: Abordagem Tradicional de Fine-Tuning
Atualizando Todos os Parâmetros do Modelo
O fine-tuning tradicional envolve a atualização de todos os parâmetros treináveis do modelo durante o treinamento. Isso proporciona a máxima flexibilidade para adaptar modelos a novas tarefas.
Como Funciona o Fine-Tuning Completo
O Loop de Treinamento
- Forward Pass: Os dados passam por todo o modelo
- Cálculo da Loss: Compara a previsão com o alvo
- Backward Pass: Calcula os gradientes para todos os parâmetros
- Atualização dos Pesos: Aplica os gradientes para atualizar todos os pesos
- Repetir: Continua até a convergência
Exemplo Completo de Treinamento
import torch
from transformers import (
AutoModelForCausalLM,
AutoTokenizer,
TrainingArguments,
Trainer,
DataCollatorForLanguageModeling,
)
from datasets import load_dataset
# Load model and tokenizer
model_name = "meta-llama/Llama-3.1-8B"
tokenizer = AutoTokenizer.from_pretrained(model_name)
tokenizer.pad_token = tokenizer.eos_token
# Load model - all parameters trainable by default
model = AutoModelForCausalLM.from_pretrained(
model_name,
torch_dtype=torch.bfloat16, # Use bf16 for memory efficiency
device_map="auto",
)
# Verify all parameters are trainable
trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
total_params = sum(p.numel() for p in model.parameters())
print(f"Trainable: {trainable_params:,} / {total_params:,} = 100%")
# Prepare dataset
dataset = load_dataset("databricks/dolly-15k", split="train[:1000]")
def format_instruction(example):
return {
"text": f"### Instruction:\n{example['instruction']}\n\n### Response:\n{example['response']}"
}
dataset = dataset.map(format_instruction)
def tokenize(example):
return tokenizer(
example["text"],
truncation=True,
max_length=512,
padding="max_length",
)
tokenized_dataset = dataset.map(tokenize, remove_columns=dataset.column_names)
# Training configuration
training_args = TrainingArguments(
output_dir="./llama-full-ft",
num_train_epochs=3,
per_device_train_batch_size=1, # Limited by memory
gradient_accumulation_steps=16, # Simulate batch size of 16
learning_rate=2e-5,
weight_decay=0.01,
warmup_ratio=0.03,
logging_steps=10,
save_strategy="epoch",
bf16=True, # Use bfloat16
gradient_checkpointing=True, # Save memory
optim="adamw_torch_fused", # Efficient optimizer
)
# Create trainer
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized_dataset,
data_collator=DataCollatorForLanguageModeling(tokenizer, mlm=False),
)
# Train
trainer.train()
# Save
model.save_pretrained("./llama-full-ft-final")
tokenizer.save_pretrained("./llama-full-ft-final")
Componentes-Chave Explicados
Taxa de Aprendizado (Learning Rate)
learning_rate = 2e-5 # Common range: 1e-5 to 5e-5
Para fine-tuning completo, utilize taxas de aprendizado menores do que em PEFT:
- Muito alta: O modelo diverge ou esquece o pré-treinamento
- Muito baixa: O treinamento é lento, pode não se adaptar o suficiente
Decaimento de Peso (Weight Decay)
weight_decay = 0.01 # Regularization to prevent overfitting
O weight decay previne overfitting penalizando pesos muito altos.
Warmup
warmup_ratio = 0.03 # 3% of training steps
Aumenta gradualmente a taxa de aprendizado no início para um treinamento mais estável.
Gradient Checkpointing
gradient_checkpointing = True # Trade compute for memory
Recalcula ativações durante o backward pass ao invés de armazená-las—economiza cerca de 30% de memória.
Opções de Otimizador
| Otimizador | Memória | Velocidade | Qualidade |
|---|---|---|---|
| AdamW | Alta | Rápido | Melhor |
| Adam 8-bit | Média | Média | Boa |
| SGD | Baixa | Lento | Requer ajuste |
| Adafactor | Baixa | Média | Boa |
# Standard AdamW
optim = "adamw_torch"
# Fused AdamW (faster)
optim = "adamw_torch_fused"
# 8-bit Adam (less memory)
optim = "adamw_8bit"
# Adafactor (no momentum states)
optim = "adafactor"
Dinâmica do Treinamento
Curva de Loss
Epoch 1: Training loss decreasing rapidly
(Model adapting to new data)
Epoch 2: Training loss stabilizing
(Watch validation loss for overfitting)
Epoch 3: Possible overfitting
(Training loss still drops, validation may increase)
Monitorando o Treinamento
# Using Weights & Biases
training_args = TrainingArguments(
# ... other args ...
report_to="wandb",
run_name="llama-full-ft-experiment-1",
)
Principais métricas para acompanhar:
- Training Loss: Deve diminuir
- Validation Loss: Deve diminuir, mas observe divergência em relação ao training loss
- Gradient Norm: Picos podem indicar instabilidade
- Learning Rate: Verifique se warmup e decaimento estão funcionando
Treinamento com Precisão Mista
Por Que Usar Precisão Mista?
# Half the memory, often faster training
bf16 = True # Preferred for newer GPUs (Ampere+)
# OR
fp16 = True # For older GPUs
| Precisão | Memória | Alcance | Estabilidade |
|---|---|---|---|
| float32 | 4 bytes | Completo | Máxima |
| bfloat16 | 2 bytes | Completo | Alta |
| float16 | 2 bytes | Limitado | Média |
Float16 tem um alcance numérico mais estreito e pode causar overflow/underflow. BFloat16 mantém o alcance do float32 com menor precisão, tornando-o mais estável.
Salvando e Carregando
Durante o Treinamento (Checkpoints)
training_args = TrainingArguments(
save_strategy="steps",
save_steps=500,
save_total_limit=3, # Keep only last 3 checkpoints
)
Após o Treinamento
# Save the full model
model.save_pretrained("./final-model")
tokenizer.save_pretrained("./final-model")
# Later, load it
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained("./final-model")
Vantagens do Fine-Tuning Completo
Máxima Personalização
Todos os parâmetros podem ser ajustados para sua tarefa.
Potencialmente Maior Qualidade
Sem restrições de pesos congelados.
Simplicidade
Não é necessário configurar adapters.
Saída Unificada
Arquivo único de modelo, sem overhead de adapter na inferência.
Desvantagens
Alta Demanda de Memória
É necessário armazenar modelo, gradientes e estados do otimizador.
Risco de Esquecimento Catastrófico
Pode perder conhecimento pré-treinado.
Custo Elevado
Requer recursos significativos de GPU.
Treinamento Lento
Todos os parâmetros precisam ser atualizados.
Quando Usar Fine-Tuning Completo
| Caso de Uso | Recomendação |
|---|---|
| Máxima qualidade necessária | ✅ Full FT |
| Restrição de recursos | ❌ Use PEFT |
| Experimentação rápida | ❌ Use PEFT |
| Produção em escala | ⚠️ Considere trade-offs |
| Pesquisa/benchmark | ✅ Full FT |
Principais Pontos
✅ Fine-tuning completo atualiza todos os parâmetros do modelo
✅ Requer muita memória de GPU (modelo + gradientes + otimizador)
✅ Use precisão mista e gradient checkpointing para reduzir memória
✅ Taxas de aprendizado menores que PEFT (1e-5 a 5e-5)
✅ Monitore overfitting com validation loss
✅ Melhor opção para máxima personalização quando há recursos disponíveis
Próximos Passos
Continue para Capítulo 2: Requisitos Computacionais para entender exatamente as necessidades de memória e processamento.
Verificação Rápida
Antes de avançar, certifique-se de que você consegue responder:
- Quais são os quatro principais componentes armazenados na memória da GPU durante o treinamento?
- Por que o bfloat16 é frequentemente preferido ao float16?
- Qual a faixa típica de taxa de aprendizado para fine-tuning completo?