Capítulo 2: PEFT vs Fine-Tuning Completo
Comprendiendo los Compromisos
Elegir entre PEFT y el fine-tuning completo requiere comprender sus respectivas fortalezas y limitaciones.
Comparación Integral
| Aspecto | Fine-Tuning Completo | PEFT |
|---|---|---|
| Memoria | 4-6x tamaño del modelo | 0.1-0.5x tamaño del modelo |
| Velocidad | Más lento | 2-3x más rápido |
| Techo de Calidad | Más alto | Ligeramente menor |
| Riesgo de Olvido | Alto | Ninguno |
| Hardware | GPUs costosas | GPUs de consumidor |
| Intercambio de Adaptadores | No | Sí |
| Sobrecarga en Inferencia | Ninguna | Mínima (o fusionar) |
Comparación de Memoria
Desglose Detallado
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
Comparación de Calidad
Resultados Empíricos
La investigación muestra consistentemente que PEFT logra una calidad comparable:
| Tarea | Fine-Tuning Completo | LoRA | Diferencia |
|---|---|---|---|
| Clasificación de Texto | 94.2% | 93.8% | -0.4% |
| Resumen | 45.3 ROUGE | 44.9 ROUGE | -0.9% |
| Seguimiento de Instrucciones | 8.2/10 | 8.0/10 | -2.4% |
| Generación de Código | 67.1% | 65.8% | -1.9% |
Cuándo PEFT se Queda Corto
PEFT puede tener un rendimiento inferior en:
- Cambios de dominio significativos: Pre-entrenamiento → dominio muy especializado
- Nuevos formatos de salida: Muy diferentes a cualquier dato de entrenamiento
- Calidad máxima posible: Cuando cada 0.1% es importante
- Nuevos comportamientos complejos: Patrones profundamente incrustados
# 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
Comparación de Velocidad de Entrenamiento
Métricas de Rendimiento
| Método | Tiempo por Época | Velocidad Relativa |
|---|---|---|
| Fine-Tuning Completo | 60 min | 1x |
| LoRA | 25 min | 2.4x |
| QLoRA | 30 min | 2x |
Por Qué PEFT es Más Rápido
- Menos cálculos de gradientes: Solo se calculan gradientes para los parámetros entrenables
- Menor ancho de banda de memoria: Menos bytes a transferir
- Actualizaciones de optimizador más pequeñas: Menos estados a mantener
# Gradient computation scales with trainable parameters
# Full FT: 7 billion gradient computations
# LoRA: 70 million gradient computations (100x fewer)
Comparación de Flexibilidad
PEFT: Múltiples 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
Comparación en Inferencia
PEFT con 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 Fusionado = Velocidad de Fine-Tuning Completo
Después de fusionar:
W_merged = W_base + B × A # One-time computation
# Inference is identical to full fine-tuned model
Análisis de Costos
Costos de Entrenamiento
| Método | GPU Necesaria | Tiempo | Costo (On-Demand) |
|---|---|---|---|
| Fine-Tuning Completo (7B) | A100-80GB | 4 hr | $24 |
| LoRA (7B) | A100-40GB | 1.5 hr | $6 |
| QLoRA (7B) | RTX 4090 | 2 hr | $2 |
Consideraciones a Largo Plazo
Escenario: Fine-tuning para 10 tareas diferentes
Fine-Tuning Completo:
- 10 ejecuciones de entrenamiento en GPUs costosas
- 10 modelos completos para almacenar (150GB)
- Cargar un modelo diferente por tarea
PEFT:
- 10 ejecuciones de entrenamiento en GPUs más económicas
- 1 modelo base + 10 adaptadores (15GB + 200MB)
- Cambio rápido de adaptador
Marco de Decisión
Elija PEFT Cuando:
✅ La memoria de GPU es limitada (< 40GB)
✅ Se necesita experimentar rápidamente
✅ Se requieren múltiples variantes de tareas
✅ Se desea preservar las capacidades del modelo base
✅ El costo es un factor significativo
✅ La producción requiere intercambio de adaptadores
Elija Fine-Tuning Completo Cuando:
✅ La máxima calidad es crítica
✅ Se requiere un cambio de dominio significativo
✅ Los recursos no son una limitación
✅ Se necesita un modelo de propósito único
✅ Se desplegará como modelo fusionado de todos modos
✅ La investigación requiere control total
Estrategias Híbridas
Comience con PEFT, Mejore si es Necesario
# 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 Iteración, Fine-Tuning Completo para el Final
# 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)
Recomendaciones Prácticas
Para la Mayoría de los Casos: Comience con 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 Producción: Considere Fusionar
# After training, merge for deployment
model = model.merge_and_unload()
model.save_pretrained("./production-model")
# Now behaves exactly like full fine-tuned model
Puntos Clave
✅ PEFT logra entre el 90-99% de la calidad del fine-tuning completo con 80-95% menos memoria
✅ El fine-tuning completo tiene un techo de calidad más alto pero a un costo significativo
✅ PEFT permite el intercambio de adaptadores; el fine-tuning completo crea modelos fijos
✅ PEFT fusionado tiene un rendimiento de inferencia idéntico al fine-tuning completo
✅ Comience con PEFT, pase a fine-tuning completo solo si es necesario
✅ Considere el costo total incluyendo almacenamiento, intercambio y iteración
Próximos Pasos
Continúe a Capítulo 3: Descripción General de Métodos PEFT para explorar todas las técnicas PEFT disponibles.
Comprobación Rápida
Antes de continuar, asegúrese de poder responder:
- ¿Cuánta memoria ahorra PEFT en comparación con el fine-tuning completo?
- ¿Cuándo podría el fine-tuning completo superar a PEFT?
- ¿Cuál es la ventaja de los adaptadores PEFT fusionados?