Capítulo 1: Enfoque Tradicional de Fine-Tuning
Actualización de Todos los Parámetros del Modelo
El fine-tuning tradicional implica actualizar cada parámetro entrenable del modelo durante el entrenamiento. Esto proporciona la máxima flexibilidad para adaptar los modelos a nuevas tareas.
Cómo Funciona el Fine-Tuning Completo
El Bucle de Entrenamiento
- Forward Pass: Los datos fluyen a través de todo el modelo
- Cálculo de la Pérdida: Comparar la predicción con el objetivo
- Backward Pass: Calcular los gradientes para todos los parámetros
- Actualización de Pesos: Aplicar los gradientes para actualizar todos los pesos
- Repetir: Continuar hasta la convergencia
Ejemplo Completo de Entrenamiento
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 Clave Explicados
Tasa de Aprendizaje
learning_rate = 2e-5 # Common range: 1e-5 to 5e-5
Para el fine-tuning completo, utilice tasas de aprendizaje más bajas que en PEFT:
- Demasiado alta: El modelo diverge o "olvida" el pre-entrenamiento
- Demasiado baja: El entrenamiento es lento, puede que no se adapte lo suficiente
Decaimiento de Pesos (Weight Decay)
weight_decay = 0.01 # Regularization to prevent overfitting
El decaimiento de pesos previene el sobreajuste penalizando los pesos grandes.
Warmup
warmup_ratio = 0.03 # 3% of training steps
Aumenta gradualmente la tasa de aprendizaje al inicio para un entrenamiento estable.
Gradient Checkpointing
gradient_checkpointing = True # Trade compute for memory
Vuelve a calcular las activaciones durante el backward pass en lugar de almacenarlas—ahorra ~30% de memoria.
Opciones de Optimizador
| Optimizador | Memoria | Velocidad | Calidad |
|---|---|---|---|
| AdamW | Alta | Rápida | Mejor |
| Adam 8-bit | Media | Media | Buena |
| SGD | Baja | Lenta | Requiere ajuste |
| Adafactor | Baja | Media | Buena |
# 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 del Entrenamiento
Curva de Pérdida
Epoch 1: La pérdida de entrenamiento disminuye rápidamente
(El modelo se adapta a los nuevos datos)
Epoch 2: La pérdida de entrenamiento se estabiliza
(Vigile la pérdida de validación para detectar sobreajuste)
Epoch 3: Posible sobreajuste
(La pérdida de entrenamiento sigue bajando, la de validación puede aumentar)
Monitoreo del Entrenamiento
# Using Weights & Biases
training_args = TrainingArguments(
# ... other args ...
report_to="wandb",
run_name="llama-full-ft-experiment-1",
)
Métricas clave a observar:
- Pérdida de Entrenamiento: Debe disminuir
- Pérdida de Validación: Debe disminuir, pero vigile si diverge de la de entrenamiento
- Norma del Gradiente: Picos pueden indicar inestabilidad
- Tasa de Aprendizaje: Verifique que el warmup y el decaimiento funcionen
Entrenamiento en Precisión Mixta
¿Por Qué Usar Precisión Mixta?
# Half the memory, often faster training
bf16 = True # Preferred for newer GPUs (Ampere+)
# OR
fp16 = True # For older GPUs
| Precisión | Memoria | Rango | Estabilidad |
|---|---|---|---|
| float32 | 4 bytes | Completo | Máxima |
| bfloat16 | 2 bytes | Completo | Alta |
| float16 | 2 bytes | Limitado | Media |
Float16 tiene un rango más estrecho y puede causar desbordamientos o subdesbordamientos. BFloat16 mantiene el rango de float32 con menor precisión, lo que la hace más estable.
Guardado y Carga
Durante el Entrenamiento (Checkpoints)
training_args = TrainingArguments(
save_strategy="steps",
save_steps=500,
save_total_limit=3, # Keep only last 3 checkpoints
)
Después del Entrenamiento
# 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")
Ventajas del Fine-Tuning Completo
Máxima Personalización
Todos los parámetros pueden ajustarse a su tarea.
Potencialmente la Mayor Calidad
Sin restricciones de pesos congelados.
Simplicidad
No se necesita configuración de adaptadores.
Salida Unificada
Archivo de modelo único, sin sobrecarga de adaptadores en inferencia.
Desventajas
Altos Requisitos de Memoria
Se debe almacenar el modelo, los gradientes y los estados del optimizador.
Riesgo de Olvido Catastrófico
Puede perder el conocimiento pre-entrenado.
Costoso
Requiere recursos significativos de GPU.
Entrenamiento Lento
Cada parámetro debe ser actualizado.
Cuándo Usar Fine-Tuning Completo
| Caso de Uso | Recomendación |
|---|---|
| Se requiere la máxima calidad | ✅ Fine-Tuning Completo |
| Recursos limitados | ❌ Use PEFT |
| Experimentación rápida | ❌ Use PEFT |
| Producción a gran escala | ⚠️ Considere los trade-offs |
| Investigación/benchmarking | ✅ Fine-Tuning Completo |
Puntos Clave
✅ El fine-tuning completo actualiza todos los parámetros del modelo
✅ Requiere memoria significativa de GPU (modelo + gradientes + optimizador)
✅ Use precisión mixta y gradient checkpointing para reducir memoria
✅ Tasas de aprendizaje más bajas que PEFT (1e-5 a 5e-5)
✅ Monitoree el sobreajuste con la pérdida de validación
✅ Mejor opción para máxima personalización cuando los recursos lo permiten
Próximos Pasos
Continúe a Capítulo 2: Requisitos Computacionales para comprender las necesidades exactas de memoria y cómputo.
Comprobación Rápida
Antes de continuar, asegúrese de poder responder:
- ¿Cuáles son los cuatro componentes principales que se almacenan en la memoria de la GPU durante el entrenamiento?
- ¿Por qué a menudo se prefiere bfloat16 sobre float16?
- ¿Cuál es el rango típico de tasa de aprendizaje para el fine-tuning completo?