README · by ansango
← Volver al libro

Finetuning: decisiones y memoria

Cuándo fine-tunear un modelo y cuándo no, comparación con RAG, los cuellos de botella de memoria y la matemática que necesitas entender

~7 min de lectura
Resumen

Esta nota cubre la primera mitad del capítulo 7: la decisión de cuándo fine-tunear un modelo y cuándo no, cómo se compara con RAG, los cuellos de botella de memoria que hacen que fine-tunear sea caro y complejo, la matemática que los explica (backpropagation, tamaños de peso, cuantización) y por qué eso determina todo el diseño de las técnicas de fine-tuning. La segunda mitad (técnicas concretas: PEFT, LoRA, model merging) está en Técnicas de finetuning.

El lugar del fine-tuning en la cascada

El libro insiste en una jerarquía clara de técnicas de adaptación, de menos a más costosa:

  1. Prompt engineering: gratis, inmediato.
  2. RAG: low-cost, requiere datos.
  3. Fine-tuning: medio-alto coste, requiere datos etiquetados y GPUs.
  4. Entrenar desde cero: altísimo coste, requiere dataset masivo y equipo.
“El 90% de las veces que alguien dice ‘necesito fine-tunear’, la respuesta real es ‘necesito un mejor prompt o un mejor RAG’.”

El libro repite esto porque la tentación de saltar a fine-tuning es constante y casi siempre equivocada.

Cuándo fine-tunear

El libro da razones legítimas para fine-tunear:

1. Estilo y tono específicos

Si necesitas que el modelo siempre responda en un estilo muy concreto (formato, vocabulario, nivel de formalidad) que el prompting no logra fijar, fine-tuning ayuda.

2. Comportamientos complejos

Si necesitas que el modelo siga un flujo específico de pasos o use herramientas de formas concretas que el prompting no controla, fine-tuning puede enseñar el patrón.

3. Reducir latencia y coste

Un modelo más pequeño fine-tuneado para tu caso puede reemplazar a un modelo grande genérico, con menor latencia y menor coste.

4. Datos propietarios

Si tienes datos muy valiosos y quieres “destilarlos” en un modelo más pequeño, fine-tuning es el camino.

5. Mejoras incrementales

Después de que prompt engineering y RAG se han estabilizado, fine-tuning puede dar un salto del 5-15% en métricas específicas.

Cuándo NO fine-tunear

El libro lista anti-patrones frecuentes:

❌ Creer que fine-tuning añade conocimiento

Si el modelo no sabe algo, fine-tuning no lo va a saber mágicamente. Fine-tuning enseña comportamiento, no conocimiento. Si necesitas inyectar conocimiento, usa RAG.

Caso real

Una empresa intentó fine-tunear un modelo con su documentación interna para que “supiera” sobre sus productos. Resultado: el modelo alucinaba con más confianza. La solución correcta: RAG sobre la documentación.

❌ Fine-tunear con pocos datos

Con menos de 1000 ejemplos de calidad, fine-tuning raramente vale la pena. Tu tiempo se gasta en datos, no en entrenamiento.

❌ Fine-tunear sin un pipeline de evaluación

Fine-tuning sin manera de medir el antes/después es tirar dinero. Antes de empezar, necesitas tu dataset de evaluación.

❌ Fine-tunear porque “es lo que hace la competencia”

Presión social no es una razón técnica. Si tu problema está resuelto con prompt + RAG, quédate ahí.

❌ Fine-tunear un modelo recién sacado

Espera unos meses a que la comunidad Identifique bugs y mejores prácticas. Fine-tuning sobre un modelo inmaduro es construir sobre arena.

Fine-tuning vs RAG

El libro dedica tiempo a esta comparación porque es la decisión más recurrente.

DimensiónRAGFine-tuning
Coste inicialBajoAlto
Latencia+200-500msSin overhead
Conocimiento actualizadoAutomáticoRequiere reentrenar
Estilo de comportamientoLimitadoExcelente
Datos privadosPermanece en tu infraPuede salir (cuidado)
ReproducibilidadTotalNecesita versionar el modelo
DebuggingInspeccionableCaja negra

Cuándo RAG

Cuándo fine-tuning

Lo mejor de ambos mundos

Muchos sistemas en producción usan RAG + fine-tuning:

  1. RAG para aportar conocimiento actualizado.
  2. Fine-tuning para fijar estilo y comportamiento.
La pirámide del AI engineer

El libro lo resume con una imagen potente: en la base está prompt engineering, luego RAG, luego fine-tuning. Solo subes de nivel cuando el actual no llega a la métrica objetivo.

Memory bottlenecks

El libro abre la segunda mitad del capítulo con la matemática de memoria del fine-tuning, porque es lo que determina qué técnicas son posibles.

Por qué fine-tuning es caro de memoria

Fine-tuning un LLM requiere cuatro tipos de memoria en GPU:

  1. Pesos del modelo: los parámetros que se cargan en VRAM.
  2. Gradientes: lo que calcula backpropagation (un tensor del tamaño de los pesos).
  3. Optimizador: estado adicional del optimizador (Adam requiere 2x el tamaño de los pesos).
  4. Activaciones: los outputs intermedios de cada capa durante el forward pass.

Cálculo de memoria

Para un modelo con N parámetros:

ComponenteMemoria (FP32)Memoria (FP16)
Pesos4N bytes2N bytes
Gradientes4N bytes2N bytes
Optimizador (Adam)8N bytes4N bytes
Total (sin activaciones)16N bytes8N bytes
Modelo de 7B parámetros

Memoria para fine-tuning en FP16: 8 × 7B = 56 GB. Solo los pesos, gradientes y optimizador. Sin contar activaciones. Resultado: necesitas al menos 2 GPUs A100 (80 GB cada una) o 1 H100.

Por qué esto importa

La memoria limita qué modelos puedes fine-tunear:

VRAM no es opcional

Si tu modelo no cabe en VRAM, no puedes fine-tunear. Punto. Las técnicas que se ven en la próxima nota (LoRA, cuantización) son, en esencia, trucos para reducir estas cuatro cifras.

Backpropagation y parámetros entrenables

El libro explica por qué algunos parámetros son más “entrenables” que otros.

Cómo funciona backpropagation

  1. Forward pass: input → modelo → output → loss.
  2. Backward pass: del loss hacia atrás, calcular el gradiente de cada peso con respecto al loss.
  3. Optimizer step: actualizar cada peso con su gradiente.

Qué hace Adam (el optimizador por defecto)

Adam mantiene dos estados adicionales por cada peso:

Por eso Adam requiere 8N bytes (2 estados × 4 bytes por estado × N parámetros).

Optim memory-efficient (Adafactor, etc.)

Optimizadores como Adafactor o Lion reducen el estado del optimizador, pero a costa de convergencia más sensible.

Representaciones numéricas

La precisión numérica afecta dramáticamente a la memoria.

FP32 (32 bits)

Precisión completa. 4 bytes por peso. Estándar histórico.

FP16 (16 bits)

Media precisión. 2 bytes por peso. Permite entrenar el doble de parámetros en la misma VRAM. Riesgo: underflow/overflow en gradientes muy pequeños.

BF16 (Brain Floating Point)

Como FP16 pero con más rango en el exponente. El estándar actual para fine-tuning. Sin apenas pérdida de calidad frente a FP32.

INT8 (8 bits)

Enteros de 8 bits. 1 byte por peso. Se usa para inferencia, no para entrenamiento (los gradientes necesitan más precisión).

INT4 (4 bits)

Cuartos de byte. Medio byte por peso. Para inferencia con cuantización agresiva.

Cuantización

La cuantización es la técnica que reduce la memoria convirtiendo pesos de precisión alta a baja.

Por qué funciona

Los pesos de un modelo entrenado no necesitan 16 bits para almacenar información útil. Distribución de pesos es aproximadamente normal con valores típicos en un rango estrecho. 4 bits capturan lo esencial.

Tipos de cuantización

Post-training quantization (PTQ)

Cuantizar después del entrenamiento, sin más datos.

Quantization-aware training (QAT)

Entrenar con la cuantización en mente, simulándola durante el forward pass.

GPTQ, AWQ, GGUF

Algoritmos populares de cuantización optimizados:

Bitsandbytes

La librería más usada para cuantización en entrenamiento:

from transformers import AutoModelForCausalLM
from bitsandbytes.optim import AdamW8bit

model = AutoModelForCausalLM.from_pretrained(
    "model_name",
    load_in_4bit=True,  # cuantizar a INT4 al cargar
    device_map="auto"
)

optimizer = AdamW8bit(model.parameters())  # optimizador de 8 bits

Zero-redundancy optimizer (ZeRO)

DeepSpeed ZeRO parte el estado del optimizador, gradientes y pesos entre múltiples GPUs:

Permite entrenar modelos más grandes en clusters de GPUs.

Mixed precision training

Técnica estándar: usar FP16/BF16 para el forward pass y FP32 para los gradientes (master weights).

from torch.cuda.amp import autocast, GradScaler

scaler = GradScaler()
for batch in dataloader:
    optimizer.zero_grad()
    with autocast(dtype=torch.bfloat16):
        loss = model(batch)
    scaler.scale(loss).backward()
    scaler.step(optimizer)
    scaler.update()
BF16 > FP16 para entrenamiento

Si tu hardware lo soporta (Ampere+, TPU v3+), BF16 es casi siempre mejor que FP16 para fine-tuning. Más rango, menos problemas, misma velocidad.

Gradient checkpointing

Para reducir el consumo de activaciones (que pueden ser mayores que los pesos), no guardar todas las activaciones, recalcularlas en el backward pass.

model.gradient_checkpointing_enable()

Resumen en tres frases

Próximos pasos