Pular para o conteúdo principal

Capítulo 2: PEFT vs Fine-Tuning Completo

Entendendo os Trade-offs​

Escolher entre PEFT e fine-tuning completo exige compreender seus respectivos pontos fortes e limitações.


Comparação Abrangente​

AspectoFine-Tuning CompletoPEFT
Memória4-6x o tamanho do modelo0,1-0,5x o tamanho do modelo
VelocidadeMais lento2-3x mais rápido
Teto de QualidadeMais altoUm pouco menor
Risco de EsquecimentoAltoNenhum
HardwareGPUs carasGPUs de consumidor
Troca de AdaptadorNãoSim
Overhead na InferênciaNenhumMínimo (ou merge)

Comparação de Memória​

Detalhamento​

def calculate_memory(params_billions, method):
"""Calculate approximate training memory."""
params = params_billions * 1e9

if method == "full_ft":
# Weights + Gradients + Optimizer (2 states) + Activations
weights = params * 2 # bf16
gradients = params * 2
optimizer = params * 4 * 2 # fp32, 2 states
activations = params * 0.5 # Rough estimate
return (weights + gradients + optimizer + activations) / 1e9

elif method == "lora":
# Frozen weights + small LoRA gradients/optimizer
weights = params * 2
lora_params = params * 0.01 # ~1% trainable
lora_training = lora_params * (2 + 2 + 8) # grad + optimizer
return (weights + lora_training) / 1e9

elif method == "qlora":
# 4-bit weights + LoRA training
weights = params * 0.5 # 4-bit
lora_params = params * 0.01
lora_training = lora_params * (2 + 2 + 8)
return (weights + lora_training) / 1e9

# Results for 7B model
print(f"Full FT: {calculate_memory(7, 'full_ft'):.1f} GB") # ~77 GB
print(f"LoRA: {calculate_memory(7, 'lora'):.1f} GB") # ~15 GB
print(f"QLoRA: {calculate_memory(7, 'qlora'):.1f} GB") # ~4 GB

Comparação de Qualidade​

Resultados Empíricos​

Pesquisas mostram consistentemente que PEFT atinge qualidade comparável:

TarefaFine-Tuning CompletoLoRADiferença
Classificação de Texto94,2%93,8%-0,4%
Sumarização45,3 ROUGE44,9 ROUGE-0,9%
Seguir Instruções8,2/108,0/10-2,4%
Geração de Código67,1%65,8%-1,9%

Quando o PEFT Fica Aquém​

O PEFT pode ter desempenho inferior em:

  • Mudanças de domínio significativas: Pré-treinamento → domínio muito especializado
  • Novos formatos de saída: Muito diferentes de qualquer dado de treinamento
  • Qualidade máxima possível: Quando cada 0,1% importa
  • Novos comportamentos complexos: Padrões profundamente embutidos
# Example: PEFT might struggle with
# 1. Medical model from general LLM (major domain shift)
# 2. Entirely new language not in pretraining
# 3. Competition benchmarks where every point counts

Comparação de Velocidade de Treinamento​

Métricas de Throughput​

MétodoTempo por ÉpocaVelocidade Relativa
Fine-Tuning Completo60 min1x
LoRA25 min2,4x
QLoRA30 min2x

Por que o PEFT é Mais Rápido​

  1. Menos cálculos de gradiente: Só calcula gradientes para parâmetros treináveis
  2. Menor uso de banda de memória: Menos bytes para transferir
  3. Atualizações menores do otimizador: Menos estados para manter
# Gradient computation scales with trainable parameters
# Full FT: 7 billion gradient computations
# LoRA: 70 million gradient computations (100x fewer)

Comparação de Flexibilidade​

PEFT: Múltiplos Adaptadores​

# Load base model once
base_model = AutoModelForCausalLM.from_pretrained("llama-3.1-8b")

# Create different adapters
adapters = {
"customer_support": "adapters/support/",
"code_review": "adapters/code/",
"translation": "adapters/translate/",
}

# Runtime adapter switching
for task, adapter_path in adapters.items():
model = PeftModel.from_pretrained(base_model, adapter_path)
# Use model for task
model.unload() # Remove adapter

Fine-Tuning Completo: Modelos Separados​

# Each task requires loading a different full model
models = {
"customer_support": "models/support-full/", # 15GB
"code_review": "models/code-full/", # 15GB
"translation": "models/translate-full/", # 15GB
}
# Total: 45GB storage, high switching cost

Comparação de Inferência​

PEFT com Adaptadores​

# Slight overhead from adapter computation
# Latency: ~5-10% slower per token

# Option 1: Keep adapter separate
model = PeftModel.from_pretrained(base, adapter)
output = model.generate(input_ids)

# Option 2: Merge for zero overhead
model = model.merge_and_unload() # No adapter overhead
output = model.generate(input_ids)

PEFT Mesclado = Velocidade de Fine-Tuning Completo​

Após a mesclagem:

W_merged = W_base + B × A # One-time computation
# Inference is identical to full fine-tuned model

Análise de Custos​

Custos de Treinamento​

MétodoGPU NecessáriaTempoCusto (On-Demand)
Fine-Tuning Completo (7B)A100-80GB4 h$24
LoRA (7B)A100-40GB1,5 h$6
QLoRA (7B)RTX 40902 h$2

Considerações de Longo Prazo​

Cenário: Fine-tuning para 10 tarefas diferentes

Fine-Tuning Completo:
- 10 execuções de treinamento em GPUs caras
- 10 modelos completos para armazenar (150GB)
- Carregar modelo diferente para cada tarefa

PEFT:
- 10 execuções de treinamento em GPUs mais baratas
- 1 modelo base + 10 adaptadores (15GB + 200MB)
- Troca rápida de adaptador

Framework de Decisão​

Escolha PEFT Quando:​

✅ Memória da GPU é limitada (< 40GB)
✅ Precisa experimentar rapidamente
✅ Precisa de variações para múltiplas tarefas
✅ Deseja preservar as capacidades do modelo base
✅ Custo é um fator importante
✅ Produção exige troca de adaptadores

Escolha Fine-Tuning Completo Quando:​

✅ Qualidade máxima é crítica
✅ Mudança de domínio significativa é necessária
✅ Recursos não são uma restrição
✅ Precisa de modelo para um único propósito
✅ Vai fazer deploy como modelo mesclado de qualquer forma
✅ Pesquisa exige controle total


Estratégias Híbridas​

Comece com PEFT, Evolua se Necessário​

# Phase 1: Quick experimentation with LoRA
lora_model = train_with_lora(dataset_small)
evaluate(lora_model) # Check if quality is sufficient

# Phase 2: If PEFT isn't enough, try full FT
if quality_gap > threshold:
full_ft_model = train_full(dataset_full)

PEFT para Iteração, Fine-Tuning Completo para Finalização​

# Iterate on data/hyperparameters with PEFT (fast, cheap)
for experiment in experiments:
model = train_lora(config=experiment)
score = evaluate(model)
if score > best_score:
best_config = experiment

# Final training with full FT using best settings
final_model = train_full(config=best_config)

Recomendações Práticas​

Para a Maioria dos Casos: Comece com PEFT​

# Default recommendation for most practitioners
from peft import LoraConfig, get_peft_model

config = LoraConfig(
r=16,
lora_alpha=32,
target_modules=["q_proj", "v_proj", "k_proj", "o_proj"],
lora_dropout=0.05,
)

model = get_peft_model(base_model, config)
# Train and evaluate

Para Produção: Considere Mesclar​

# After training, merge for deployment
model = model.merge_and_unload()
model.save_pretrained("./production-model")
# Now behaves exactly like full fine-tuned model

Principais Conclusões​

✅ PEFT alcança 90-99% da qualidade do fine-tuning completo com 80-95% menos memória

✅ Fine-tuning completo tem teto de qualidade maior, mas a um custo significativo

✅ PEFT permite troca de adaptadores; fine-tuning completo gera modelos fixos

✅ PEFT mesclado tem desempenho de inferência idêntico ao fine-tuning completo

✅ Comece com PEFT, só faça fine-tuning completo se necessário

✅ Considere o custo total incluindo armazenamento, troca e iteração


Próximos Passos​

Continue para Capítulo 3: Visão Geral dos Métodos PEFT para explorar todas as técnicas PEFT disponíveis.


Verificação Rápida​

Antes de seguir, certifique-se de que pode responder:

  1. Quanta memória o PEFT economiza em comparação ao fine-tuning completo?
  2. Quando o fine-tuning completo pode superar o PEFT?
  3. Qual a vantagem dos adaptadores PEFT mesclados?