C
Contextología
Context Engineering

Cómo hacer fine-tuning de un LLM paso a paso

20 de abril de 2025· 5 min read

Esta es la guía que me hubiera gustado tener la primera vez que hice fine-tuning. Sin rodeos, con los errores incluidos.

Antes de empezar: ¿realmente necesitas fine-tuning?

Fine-tuning tiene sentido cuando:

  • Necesitas consistencia de formato que el prompting no garantiza
  • El comportamiento del modelo no es el correcto después de optimizar el prompt
  • Tienes volumen suficiente para que el ahorro de tokens justifique el costo

No tiene sentido cuando el problema es de información (usa RAG) o cuando no has agotado el prompting primero.

Si tienes dudas, lee este artículo sobre cuándo usar fine-tuning vs RAG antes de continuar.

Paso 1: Prepara los datos

Formato requerido por OpenAI

{"messages": [
  {"role": "system", "content": "Tu system prompt aquí"},
  {"role": "user", "content": "Pregunta del usuario"},
  {"role": "assistant", "content": "Respuesta ideal"}
]}

Un archivo JSONL con una entrada por línea. Cada entrada es una conversación completa.

Cuántos ejemplos necesitas

  • Mínimo viable: 50 ejemplos (resultados inconsistentes)
  • Para resultados decentes: 100-200 ejemplos
  • Para comportamiento robusto: 500-1000 ejemplos
  • Para casos muy específicos: A veces 50 bien elegidos superan a 500 mediocres

La calidad supera a la cantidad. 100 ejemplos perfectos > 500 mediocres.

Cómo conseguir los datos

Opción 1 — Datos existentes: Si ya tienes conversaciones reales (logs de soporte, Q&A, etc.), filtra los de alta calidad. Son los mejores porque son representativos de uso real.

Opción 2 — Generados por GPT-4: Usa el modelo potente para generar los pares de entrenamiento. Tienes control total del formato pero pueden ser genéricos.

Opción 3 — Escribir a mano: Los mejores para comportamiento muy específico. Los peores en escala.

El approach recomendado: Empieza con un prompt bien diseñado en GPT-4 o Claude, genera 200-300 ejemplos, filtra manualmente los 100 mejores, y ahí tienes tu conjunto inicial.

Criterios de calidad para los ejemplos

Cada ejemplo debe:

  • Representar un caso de uso real
  • Tener la respuesta exactamente como quieres que sea
  • Variar suficientemente del anterior (no copies con mínimas diferencias)
  • No contener errores (un ejemplo malo contamina el entrenamiento)

Validar el formato antes de enviar

import json

def validate_training_file(filepath: str) -> None:
    errors = []
    with open(filepath) as f:
        for i, line in enumerate(f):
            try:
                entry = json.loads(line)
                if "messages" not in entry:
                    errors.append(f"Línea {i}: falta 'messages'")
                for msg in entry["messages"]:
                    if "role" not in msg or "content" not in msg:
                        errors.append(f"Línea {i}: mensaje sin role o content")
            except json.JSONDecodeError:
                errors.append(f"Línea {i}: JSON inválido")
    
    if errors:
        print("Errores encontrados:")
        for e in errors: print(f"  {e}")
    else:
        print(f"✓ {i+1} ejemplos válidos")

Paso 2: Lanza el entrenamiento

Con la API de OpenAI

from openai import OpenAI
client = OpenAI()

# 1. Sube el archivo
with open("training_data.jsonl", "rb") as f:
    upload = client.files.create(file=f, purpose="fine-tune")

print(f"File ID: {upload.id}")

# 2. Lanza el job
job = client.fine_tuning.jobs.create(
    training_file=upload.id,
    model="gpt-4o-mini-2024-07-18",
    hyperparameters={
        "n_epochs": 3,  # Default, generalmente funciona bien
    }
)

print(f"Job ID: {job.id}")

Monitoreo del training

# Revisa el estado
import time

while True:
    job = client.fine_tuning.jobs.retrieve(job_id)
    print(f"Estado: {job.status}")
    
    if job.status in ["succeeded", "failed", "cancelled"]:
        break
    
    time.sleep(60)

if job.status == "succeeded":
    print(f"Modelo: {job.fine_tuned_model}")

El entrenamiento típicamente tarda 15-60 minutos dependiendo del tamaño del dataset.

Parámetros importantes

n_epochs: Cuántas veces pasa el modelo por todos los datos. 3 es el default y funciona para la mayoría de casos. Si el modelo overfitta (comportamiento demasiado mecánico), reduce a 1-2.

learning_rate_multiplier: Por defecto auto. No toques esto a menos que sepas lo que haces.

Paso 3: Evalúa el modelo resultante

Este paso es el más importante y el más frecuentemente saltado.

Compara contra el modelo base

Ejecuta ambos modelos (el base y el fine-tuned) en tu eval set:

def evaluate_models(test_cases: list, models: list) -> dict:
    results = {model: [] for model in models}
    
    for case in test_cases:
        for model in models:
            response = client.chat.completions.create(
                model=model,
                messages=[
                    {"role": "system", "content": case["system"]},
                    {"role": "user", "content": case["input"]}
                ]
            )
            results[model].append({
                "input": case["input"],
                "expected": case["expected"],
                "actual": response.choices[0].message.content
            })
    
    return results

Qué medir

Consistencia de formato: ¿El modelo fine-tuned respeta siempre el formato requerido?

Calidad del contenido: ¿Las respuestas son mejores, iguales o peores que el base?

Regresiones: ¿Hay tipos de consultas que funcionaban bien y ahora son peores?

Señales de overfitting

  • Las respuestas suenan "mecánicas" o demasiado similares a los ejemplos de entrenamiento
  • Funciona muy bien en ejemplos parecidos a los de entrenamiento pero mal en casos nuevos
  • El modelo "rellena" respuestas con patrones del entrenamiento aunque no sean relevantes

Si hay overfitting: reduce n_epochs o añade más variedad a los datos de entrenamiento.

Paso 4: Decisión de producción

Antes de usar el modelo fine-tuned en producción, verifica:

  • [ ] Supera al modelo base en tus métricas clave
  • [ ] No tiene regresiones en casos que antes funcionaban bien
  • [ ] El ahorro de costo/latencia justifica el mantenimiento
  • [ ] Tienes un plan para re-entrenar cuando los datos cambien

El costo de mantenimiento

El mayor coste oculto del fine-tuning no es el entrenamiento inicial: es el re-entrenamiento.

Cuando cambien tus requisitos (y cambiarán), necesitas:

  1. Actualizar los datos de entrenamiento
  2. Re-lanzar el entrenamiento
  3. Re-evaluar el nuevo modelo
  4. Migrar en producción

Si tus requisitos cambian frecuentemente, el prompting es más ágil.

Checklist antes de hacer fine-tuning

  • [ ] He intentado solucionar el problema con prompt engineering primero
  • [ ] Tengo al menos 100 ejemplos de alta calidad
  • [ ] Tengo un eval set para medir antes y después
  • [ ] Entiendo el costo de entrenamiento e inferencia
  • [ ] Tengo un plan para mantener el modelo actualizado

Si has marcado todo: procede. Si no: vuelve al paso correspondiente.

Pon en práctica lo que has aprendido

Selector de modelo IA

¿Fine-tuning o RAG? El selector te ayuda a decidir.

Abrir herramienta gratuita →

Recibe lo mejor de Contextología

Diseño de contexto, agentes y workflows de IA directamente en tu correo.