← Todo el material
Curso Avanzado 23 de agosto de 2026

Fine-tuning — Módulo 11: destilación, continued pretraining y otras técnicas

Undécimo módulo del Curso de Fine-tuning: el catálogo más allá del SFT y el DPO. Destilación de un modelo grande a uno chico y su economía real, continued pretraining para dominio o idioma sin olvido catastrófico, expansión del tokenizador, RAFT para que el modelo use bien el contexto recuperado, model merging con SLERP y TIES, y entrenar tool-calling y salida estructurada.

☰ Índice del contenido

Con el módulo 10 cerramos el ciclo completo: dataset, entrenamiento, evaluación, merge, cuantización y serving. Con eso ya puedes hacer la gran mayoría de los fine-tunings que un equipo necesita. Este módulo es el resto: el catálogo de técnicas que resuelven problemas que el SFT y el DPO no tocan. No es una lista de cosas que hay que hacer —la mayoría de los proyectos no necesita ninguna—, es un mapa para reconocer cuándo tu problema es uno de estos y no gastar seis semanas atacándolo con la herramienta equivocada.

La tesis del módulo

Cada técnica de esta lista existe porque hay un problema que el SFT no puede resolver por definición, no porque el SFT esté mal hecho. Si tu modelo no sabe el dominio, ningún volumen de ejemplos de instrucción se lo va a enseñar; si tu modelo es demasiado caro, ningún prompt lo va a hacer barato. Reconocer cuál es tu problema es todo el trabajo.

Destilación: el mejor negocio del curso

Destilación (knowledge distillation) es entrenar un modelo chico —el alumno— para que imite el comportamiento de un modelo grande —el maestro—. En el mundo de los LLM la versión que se usa casi siempre no es la académica de igualar distribuciones de probabilidad, sino la práctica y sencilla: el maestro genera un dataset y el alumno se entrena con SFT sobre él. Es literalmente el flujo del módulo 6, con la única diferencia de que las respuestas ideales las escribió un modelo grande en vez de una persona.

START ──► ┌────────────────┐
          │ prompts reales │  de tu tráfico o de tu dominio
          └───────┬────────┘
                  │
                  ▼
          ┌────────────────┐
          │    MAESTRO     │  modelo grande, caro, bueno
          │  genera N res- │
          │  puestas c/u   │
          └───────┬────────┘
                  │
                  ▼
          ┌────────────────┐
          │   FILTRO       │  el paso que decide todo:
          │ verificar y    │  descartar lo malo
          │ descartar      │
          └───────┬────────┘
                  │ dataset limpio
                  ▼
          ┌────────────────┐
          │    ALUMNO      │  modelo chico, barato
          │  SFT + LoRA    │
          └───────┬────────┘
                  │
                  ▼
              evaluar ──► END

La economía, que es el punto

Números redondos y como órdenes de magnitud. Supón un servicio que clasifica y responde 500 mil consultas al mes, y que hoy corre sobre un modelo grande de API a unos pocos dólares por millón de tokens.

EtapaModelo grande de APIAlumno 7B afinado y servido
Costo de generar el dataset—Pago único, decenas de USD
Costo de entrenar—Pago único, unas horas de GPU
Costo por millón de tokensAlto1 a 2 órdenes de magnitud menor
Latencia p95SegundosCientos de ms
Datos salen de tu infraSíNo
Techo de calidadEl del maestroCerca del maestro en la tarea afinada

La clave está en la última fila y hay que leerla con cuidado: el alumno se acerca al maestro en la tarea específica que se destiló, no en general. Un 7B destilado de un modelo grande para clasificar tickets puede empatar con el maestro en clasificar tickets, y va a ser notoriamente peor en cualquier otra cosa. Eso no es un defecto: es exactamente el trato. Cambias generalidad por costo, y si tu producto hace una cosa, la generalidad no la estabas usando.

Por eso la destilación suele ser la técnica más rentable del curso: es la única que ataca directamente la factura de inferencia, que es donde está el dinero real de un producto con tráfico.

El paso que decide el resultado: filtrar

Generar el dataset es fácil; el trabajo está en no meterle basura al alumno.

"""Destilación: generar el dataset con el maestro y filtrar.

El filtro es el módulo entero. Un alumno entrenado con las alucinaciones
del maestro aprende a alucinar con más convicción y menos costo.
"""
import json

from openai import OpenAI

cliente = OpenAI()
MAESTRO = "gpt-4o"


def generar(prompt: str, n: int = 3) -> list[str]:
    """N respuestas al mismo prompt: permite filtrar por consenso."""
    r = cliente.chat.completions.create(
        model=MAESTRO,
        messages=[{"role": "user", "content": prompt}],
        n=n,
        temperature=0.7,
    )
    return [c.message.content for c in r.choices]


def filtrar(prompt: str, respuestas: list[str]) -> str | None:
    """Tres filtros en orden de costo creciente.

    1) Verificable: si la tarea tiene respuesta comprobable (código que
       compila, JSON que parsea, número que cuadra), usa eso. Es el
       filtro más barato y el más confiable.
    2) Consenso: si las N respuestas coinciden, el maestro estaba
       seguro. Si divergen, probablemente no sabía.
    3) Juez: recién acá se paga un LLM extra para puntuar.
    """
    validas = [r for r in respuestas if es_verificable_ok(prompt, r)]
    if not validas:
        return None

    if hay_consenso(validas, umbral=0.8):
        return max(validas, key=len)   # entre las que concuerdan

    puntajes = [(juez(prompt, r), r) for r in validas]
    mejor_puntaje, mejor = max(puntajes)
    return mejor if mejor_puntaje >= 4.0 else None


if __name__ == "__main__":
    prompts = [l.strip() for l in open("prompts_reales.txt",
                                       encoding="utf-8") if l.strip()]
    dataset, descartados = [], 0

    for p in prompts:
        elegida = filtrar(p, generar(p))
        if elegida is None:
            descartados += 1
            continue
        dataset.append({"messages": [
            {"role": "user", "content": p},
            {"role": "assistant", "content": elegida},
        ]})

    with open("dataset_destilado.jsonl", "w", encoding="utf-8") as f:
        for d in dataset:
            f.write(json.dumps(d, ensure_ascii=False) + "\n")

    # Una tasa de descarte del 20-40% es SANA. Si descartas el 2%,
    # tu filtro no está filtrando; si descartas el 80%, el maestro
    # no sirve para esta tarea o tu filtro es demasiado estricto.
    print(f"{len(dataset)} ejemplos, {descartados} descartados")

La jerarquía de filtros importa. Si tu tarea tiene una verificación objetiva —el código corre, el JSON parsea, el SQL devuelve el resultado esperado, la suma cuadra—, úsala y olvídate del resto. Ese es el escenario ideal de la destilación y es por lejos donde mejor funciona, porque el filtro es gratis y perfecto. El consenso entre N muestras es el segundo mejor: si el maestro dice lo mismo tres veces con temperatura 0,7, probablemente sabe; si dice tres cosas distintas, probablemente está inventando. El juez LLM es el último recurso, con la rúbrica explícita y las precauciones de RAG — Módulo 10.

Los prompts, por su parte, deben venir de tu tráfico real. Destilar sobre prompts inventados produce un alumno excelente en prompts inventados.

Cuidado — la parte legal, que no es opcional. Los términos de uso de varios proveedores de modelos cerrados prohíben explícitamente usar sus salidas para entrenar modelos que compitan con ellos. Antes de destilar de una API comercial, lee los términos vigentes de ese proveedor, porque cambian y varían entre ellos. Las rutas sin ese problema: destilar desde un modelo open weight con licencia permisiva (varias familias abiertas incluso lo permiten de forma explícita), o destilar desde tu propio modelo grande. No es un detalle burocrático: es el tipo de riesgo que aparece en una due diligence y que puede obligar a botar el modelo entero.

Continued pretraining: cuando falta el dominio

Hay un síntoma inconfundible: el modelo no conoce el vocabulario. No es que responda con el formato equivocado ni con el tono equivocado; es que las palabras de tu dominio no significan nada para él. Textos legales de un país específico, normas técnicas de una industria, historia clínica con abreviaturas propias, un idioma con poca representación en el preentrenamiento. Le puedes dar mil ejemplos de instrucción y va a seguir sin saber qué es una “resolución exenta” o cómo se conjuga bien en aymara.

Ese es el único caso donde el SFT no alcanza por definición, y donde corresponde continued pretraining (también llamado domain-adaptive pretraining): seguir el preentrenamiento del modelo con texto crudo de tu dominio. No hay instrucciones, no hay pares pregunta-respuesta, no hay loss masking: es el objetivo original de predecir el token siguiente sobre texto plano, explicado en Transformers — Módulo 6.

Cuántos tokens

Órdenes de magnitud. El preentrenamiento original de un modelo moderno son billones de tokens; lo que tú vas a hacer es una fracción minúscula de eso.

ObjetivoTokens de dominioRealidad práctica
Jerga y estilo de un dominio acotado~100 M – 1.000 MAlcanzable con un corpus corporativo grande
Dominio técnico completo~1.000 M – 10.000 MRequiere un corpus serio y varios días de GPU
Idioma nuevo con poca representación~10.000 M+Territorio de laboratorio o consorcio

La conclusión honesta que hay que sacar de esa tabla: la mayoría de los equipos no tiene el corpus para hacer esto. Un archivo con toda la documentación de tu empresa suele ser 10 o 50 millones de tokens, que está por debajo del umbral donde el continued pretraining hace diferencia. Si ese es tu caso, la respuesta correcta casi siempre es RAG, no continued pretraining: el conocimiento se recupera en tiempo de consulta en vez de intentar meterlo en los pesos. El Curso de RAG completo trata de eso.

El olvido catastrófico y el truco de la mezcla

El riesgo específico del continued pretraining se llama olvido catastrófico (catastrophic forgetting): al entrenar largo sobre una distribución nueva, el modelo pierde capacidades que tenía. El caso más doloroso es perder la capacidad de seguir instrucciones —entrenaste sobre texto plano y el modelo volvió a ser un completador de texto que ya no responde preguntas.

El truco estándar es simple y funciona: mezclar datos generales con los del dominio. En vez de entrenar 100% con tu corpus, entrenas con una proporción de texto general de calidad. La receta habitual de la literatura está en el rango de un 5% a 30% de datos generales en la mezcla, y la elección dentro de ese rango depende de cuánto te importe conservar las capacidades originales.

   SIN MEZCLA (100% dominio)

   capacidades   ████████████░░░░░░░░  dominio: sube
   generales     ████░░░░░░░░░░░░░░░░  general: SE CAE


   CON MEZCLA (85% dominio + 15% general)

   capacidades   ██████████░░░░░░░░░░  dominio: sube igual
   generales     ███████████░░░░░░░░░  general: se conserva

Las otras dos mitigaciones que se usan en la práctica: learning rate bajo (un orden de magnitud menor que el del preentrenamiento original) y hacerlo con LoRA en vez de full fine-tuning, porque un adapter de rango bajo tiene mucha menos capacidad de destruir lo que ya estaba. Y siempre, siempre, un SFT después: el continued pretraining deja un completador de texto, y hay que devolverle el comportamiento de asistente con una etapa de instrucción encima.

START ──► ┌───────────────┐
          │ modelo base   │
          │ instruct      │
          └───────┬───────┘
                  │
                  ▼
          ┌───────────────┐
          │ continued     │  texto crudo del dominio
          │ pretraining   │  + 15% general
          └───────┬───────┘
                  │  ← acá el modelo YA NO obedece
                  ▼
          ┌───────────────┐
          │  SFT          │  le devuelve el comportamiento
          │  (módulo 6)   │  de asistente
          └───────┬───────┘
                  │
                  ▼
             evaluar ──► END

Expandir el vocabulario del tokenizador

Un problema colateral de los dominios y los idiomas poco representados: el tokenizador los parte en pedazos absurdos. Una palabra técnica común en tu industria puede consumir ocho tokens donde una palabra inglesa equivalente consume uno. Eso encarece cada request, achica el contexto efectivo y le complica el aprendizaje al modelo.

La solución es expandir el vocabulario: entrenar tokens nuevos para los términos frecuentes de tu dominio y agregarlos al tokenizador.

"""Expandir el vocabulario con términos del dominio.

Se hace ANTES del continued pretraining. Los embeddings nuevos
arrancan vacíos y necesitan tokens de entrenamiento para servir.
"""
from transformers import AutoModelForCausalLM, AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-7B-Instruct")
modelo = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5-7B-Instruct")

# Antes: mide cuántos tokens consume tu jerga.
for termino in ["resolución exenta", "silencio administrativo"]:
    print(termino, len(tokenizer.encode(termino, add_special_tokens=False)))

nuevos = ["resolución exenta", "silencio administrativo", "toma de razón"]

# Tokeniza los términos ANTES de agregarlos: así conservamos los ids de
# los subtokens viejos que cada término reemplaza.
subtokens = {t: tokenizer.encode(t, add_special_tokens=False) for t in nuevos}

agregados = tokenizer.add_tokens(nuevos)

# La matriz de embeddings crece: hay filas nuevas sin entrenar.
modelo.resize_token_embeddings(len(tokenizer))

# Inicializar los embeddings nuevos con el PROMEDIO de los embeddings
# de los subtokens que reemplazan converge mucho más rápido que
# dejarlos aleatorios. Es un truco barato y bien establecido.
import torch

emb = modelo.get_input_embeddings().weight.data
with torch.no_grad():
    for i, termino in enumerate(nuevos):
        fila_nueva = len(tokenizer) - agregados + i
        emb[fila_nueva] = emb[subtokens[termino]].mean(dim=0)

Y el aviso que acompaña siempre a esta técnica: expandir el vocabulario obliga a continued pretraining. Los embeddings nuevos arrancan sin significado y necesitan ver esos tokens en contexto muchas veces para volverse útiles. Agregar tokens y hacer solo un SFT chico deja el modelo peor que antes, porque cambiaste la tokenización de tu jerga por una representación que el modelo no entiende. Si no vas a hacer continued pretraining, no expandas el vocabulario.

RAFT: entrenar para usar el contexto recuperado

RAFT (Retrieval-Augmented Fine-Tuning) ataca un problema muy concreto de quien opera un RAG: el modelo recibe cinco chunks recuperados, tres son relevantes y dos son distractores plausibles, y el modelo se deja arrastrar por los distractores o cita el que no era.

La idea es entrenar al modelo con ejemplos que se parecen a lo que va a ver en producción, incluyendo la basura. Cada ejemplo de entrenamiento tiene:

  • La pregunta.
  • Un contexto que mezcla documentos relevantes y distractores (documentos del mismo corpus, sobre temas parecidos, que no responden la pregunta).
  • Una respuesta que cita explícitamente los fragmentos relevantes y ignora los distractores.

Y el detalle que hace la técnica: una fracción de los ejemplos —típicamente alrededor de un tercio— tiene contexto solo con distractores, sin ningún documento relevante. La respuesta correcta ahí es abstenerse. Eso es lo que le enseña al modelo a decir “no tengo esa información” en vez de armar algo con lo que haya.

"""Construir un dataset RAFT a partir de un golden dataset de RAG.

La proporción `p_sin_oraculo` es la perilla del método: cuántos
ejemplos NO contienen la respuesta y por lo tanto entrenan la
abstención.
"""
import json
import random

P_SIN_ORACULO = 0.3    # ~1/3 de ejemplos sin documento relevante
N_DISTRACTORES = 4

PLANTILLA = """Responde usando SOLO los documentos entregados.
Cita el documento que respalda cada afirmación con [doc N].
Si los documentos no contienen la respuesta, di exactamente:
"No tengo esa información en los documentos disponibles."

{documentos}

Pregunta: {pregunta}"""


def construir_ejemplo(caso: dict, corpus: dict) -> dict:
    """`caso` viene del golden dataset del curso de RAG:
    pregunta, chunks_relevantes, respuesta_ideal."""
    relevantes = [corpus[cid] for cid in caso["chunks_relevantes"]]

    # Los distractores se sacan del MISMO corpus y del mismo tema.
    # Distractores aleatorios de otro dominio son demasiado fáciles
    # y no enseñan nada: el modelo ya los descartaba.
    distractores = random.sample(
        [t for cid, t in corpus.items()
         if cid not in caso["chunks_relevantes"]],
        N_DISTRACTORES,
    )

    if random.random() < P_SIN_ORACULO:
        docs = distractores
        respuesta = ("No tengo esa información en los documentos "
                     "disponibles.")
    else:
        docs = relevantes + distractores
        respuesta = caso["respuesta_ideal"]

    random.shuffle(docs)   # la posición del relevante debe variar
    bloque = "\n\n".join(f"[doc {i+1}] {d}" for i, d in enumerate(docs))

    return {"messages": [
        {"role": "user",
         "content": PLANTILLA.format(documentos=bloque,
                                     pregunta=caso["pregunta"])},
        {"role": "assistant", "content": respuesta},
    ]}

El random.shuffle(docs) no es cosmético. Si el documento relevante siempre queda primero, el modelo aprende “la respuesta está en el primero” y en producción, donde el reranker no es perfecto, falla. Variar la posición es lo que entrena la capacidad de buscar dentro del contexto en vez de asumir el orden.

RAFT es una de las técnicas de mejor retorno para quien ya tiene un RAG funcionando, porque el golden dataset del módulo 10 del Curso de RAG ya es casi el dataset que necesita: preguntas, chunks relevantes anotados y respuesta ideal. Construir el set RAFT desde ahí son las cincuenta líneas de arriba.

Model merging: sumar modelos sin entrenar

Model merging es combinar los pesos de dos o más modelos afinados desde el mismo base para obtener uno que herede algo de cada uno. No hay entrenamiento: es aritmética sobre los pesos, corre en CPU y toma minutos.

Los tres métodos que vale la pena conocer:

SLERP (Spherical Linear Interpolation). Interpola entre dos modelos siguiendo el arco de la esfera en vez de la línea recta, lo que preserva mejor la magnitud de los vectores de peso. Es el método más simple y solo mezcla dos modelos.

TIES (TrIm, Elect Sign, and Merge). Diseñado para mezclar varios modelos resolviendo el problema central del merging: cuando dos afinados quieren mover el mismo peso en direcciones opuestas, se cancelan. TIES trabaja sobre los task vectors (la diferencia entre cada modelo afinado y el base) en tres pasos: recorta los deltas chicos, elige el signo dominante por parámetro y promedia solo los que concuerdan con ese signo.

DARE (Drop And REscale). Descarta aleatoriamente una fracción alta de los deltas y reescala los que quedan para compensar. Suena absurdo y funciona: los task vectors de un fine-tuning son muy redundantes. Se usa combinado con TIES (dare_ties).

# merge.yaml — configuración para mergekit
# Se ejecuta con: mergekit-yaml merge.yaml ./modelo-mezclado
merge_method: ties
base_model: Qwen/Qwen2.5-7B-Instruct   # el base COMÚN a los dos
dtype: bfloat16

models:
  - model: ./modelos/qwen-soporte-v1
    parameters:
      weight: 0.6      # cuánto pesa este modelo en la mezcla
      density: 0.5     # fracción de deltas que se conserva (el "trim")
  - model: ./modelos/qwen-legal-v1
    parameters:
      weight: 0.4
      density: 0.5

parameters:
  normalize: true      # normaliza los pesos para que sumen 1

Y ahora la expectativa realista, que es lo importante de esta sección: el merging produce un modelo que es un compromiso, no una suma. En general el resultado es un poco peor que cada especialista en su propia tarea, y mejor que cada uno en la tarea del otro. A veces sale sorprendentemente bien; a veces sale peor que ambos. Es barato de probar y obligatoriamente hay que medirlo —el merge no tiene loss ni métrica propia, así que la única forma de saber si sirvió es la evaluación del módulo 8.

Requisito duro: todos los modelos deben venir del mismo base, con la misma arquitectura y el mismo tokenizador. Mergear dos modelos de familias distintas no da error inmediato en algunos casos y produce basura siempre.

Entrenar tool-calling y salida estructurada

El último ítem del catálogo es también el más pedido en producto: que el modelo llame herramientas bien y devuelva JSON válido.

Lo primero que hay que decir es la alternativa, porque en muchos casos es la respuesta correcta: la salida estructurada se puede garantizar sin entrenar nada. La decodificación restringida —guided_json en vLLM, outlines, json_schema en las APIs comerciales— fuerza al modelo a producir solo tokens que mantienen el JSON válido según el esquema. La validez es del 100% por construcción, no por entrenamiento. Si tu problema es “a veces devuelve JSON roto”, ese es el arreglo, y no cuesta una GPU.

Entrenar tiene sentido para lo que la decodificación restringida no puede darte: que el modelo elija bien qué herramienta llamar y con qué argumentos. Un esquema forzado garantiza que el JSON parsee; no garantiza que el modelo haya decidido llamar a consultar_stock en vez de crear_pedido.

"""Dataset de tool-calling en el formato de messages con tool_calls.

El formato debe ser EL MISMO que usa el runtime al servir. Si vas a
servir con la API de chat completions de vLLM, entrena con este.
"""
ejemplo = {
    "messages": [
        {"role": "system",
         "content": "Eres el asistente de pedidos de Retail Ejemplo."},
        {"role": "user",
         "content": "¿Tienen stock del modelo XR-40 en Concepción?"},
        # El turno del assistant NO tiene content: tiene tool_calls.
        {"role": "assistant",
         "tool_calls": [{
             "id": "call_1",
             "type": "function",
             "function": {
                 "name": "consultar_stock",
                 "arguments": '{"sku": "XR-40", "sucursal": "Concepcion"}',
             },
         }]},
        # El resultado de la herramienta vuelve como rol tool.
        {"role": "tool", "tool_call_id": "call_1",
         "content": '{"stock": 3, "sucursal": "Concepcion"}'},
        # Y recién acá el assistant responde en lenguaje natural.
        {"role": "assistant",
         "content": "Sí, quedan 3 unidades del XR-40 en Concepción."},
    ],
    # Las definiciones de las tools viajan con el ejemplo: el chat
    # template las inyecta en el prompt igual que en producción.
    "tools": [{
        "type": "function",
        "function": {
            "name": "consultar_stock",
            "description": "Consulta el stock de un SKU en una sucursal.",
            "parameters": {
                "type": "object",
                "properties": {
                    "sku": {"type": "string"},
                    "sucursal": {"type": "string"},
                },
                "required": ["sku", "sucursal"],
            },
        },
    }],
}

Los tres detalles que deciden si esto funciona:

  1. Incluye ejemplos donde NO se llama a ninguna herramienta. Si todos tus ejemplos terminan en una llamada, el modelo aprende a llamar siempre y va a inventar una consulta de stock cuando el usuario diga “hola”. Un tercio de ejemplos conversacionales sin tool call corrige eso.
  2. Incluye llamadas que fallan. Un turno tool con un error y un assistant que se recupera con elegancia enseña un comportamiento que en producción vas a necesitar todos los días.
  3. Aplica loss masking correctamente (módulo 3): la loss va sobre los turnos del assistant, incluidos los tool_calls, y no sobre los turnos tool, que son entrada del entorno y no algo que el modelo deba aprender a generar.

La tabla que resume el módulo

TécnicaQué problema resuelveCosto relativoCuándo vale la pena
DestilaciónEl modelo bueno es muy caro o lentoBajo (generar + un SFT)Casi siempre, si tienes volumen. La más rentable
Continued pretrainingEl modelo no conoce el dominio ni su vocabularioAlto (mil millones de tokens)Solo con corpus enorme; si no, RAG
Expandir vocabularioLa jerga consume demasiados tokensAlto (obliga a pretraining)Junto con continued pretraining, nunca solo
RAFTEl modelo se deja llevar por distractores del RAGBajo (reusa el golden dataset)Operas un RAG y ya lo evalúas
Model mergingQuieres combinar dos especialistasMínimo (minutos de CPU)Prueba barata; siempre medir el resultado
Tool-calling / salida estructuradaEl modelo elige mal la herramientaMedioDespués de descartar decodificación restringida

Ojo: la tentación de este módulo es querer aplicar tres técnicas a la vez porque todas suenan interesantes. Cada una agrega una etapa de entrenamiento, un artefacto que versionar y una forma nueva de que algo falle en silencio. La disciplina del módulo 8 sigue vigente: una técnica a la vez, medida contra el mismo set. Si no puedes mostrar el número que mejoró, no lo pongas en producción.

Errores comunes

  • Destilar sin filtrar. El alumno aprende todo lo que el maestro produjo, incluidas sus alucinaciones, y las reproduce con la misma seguridad a una fracción del costo. Una tasa de descarte del 20% al 40% es señal de que el filtro está trabajando.
  • Destilar de una API comercial sin leer los términos de uso. Varios proveedores prohíben usar sus salidas para entrenar modelos competidores. Es un riesgo que aparece tarde y puede obligar a botar el modelo completo. Verifica antes, o destila desde un modelo open weight.
  • Hacer continued pretraining con 20 millones de tokens. Está uno o dos órdenes de magnitud por debajo del umbral donde la técnica hace diferencia. Con ese volumen, la respuesta correcta es RAG.
  • Continued pretraining 100% con datos del dominio. El modelo olvida las capacidades generales y deja de seguir instrucciones. Mezcla entre un 5% y un 30% de datos generales y haz siempre un SFT después.
  • Expandir el vocabulario sin continued pretraining. Los embeddings nuevos arrancan sin significado y el modelo queda peor que antes, porque tu jerga ahora se representa con tokens que no entiende.
  • RAFT sin distractores del mismo dominio. Distractores aleatorios de otro tema son triviales de descartar y no enseñan nada. Los distractores útiles son documentos parecidos que no responden la pregunta, o sea justo lo que devuelve tu retriever.
  • Mergear modelos de bases distintos. Requiere misma arquitectura, mismo base y mismo tokenizador. Fuera de eso, el resultado es ruido con forma de modelo.
  • Fine-tunear para “que devuelva JSON válido”. Eso lo garantiza la decodificación restringida por construcción y sin entrenar. Entrena solo si el problema es que el modelo elige mal la herramienta o los argumentos.

La frase para llevarse

“Casi todas estas técnicas existen porque alguien confundió su problema: quiso enseñarle conocimiento con instrucciones, o abaratar el modelo con prompts. Reconocer cuál es tu problema vale más que dominar las seis técnicas.”

Qué viene

Módulo 12: el fine-tuning en producción. Sabes entrenar, evaluar, servir y ampliar el repertorio. Falta la parte que hace que todo eso sea sostenible en el tiempo: versionar el dataset y el adapter como una unidad —porque un adapter sin su dataset es irreproducible—, la reproducibilidad real y el determinismo que en GPU no existe, cuándo corresponde reentrenar y cuándo no, el costo verdadero de cada iteración con una tabla de ejemplo, y las dos preguntas incómodas que nadie hace hasta que es tarde: qué dice la licencia del modelo base y de dónde salieron los datos con los que entrenaste.

Material relacionado

Fine-tuningDestilaciónRAFTModel mergingTool calling

Preguntas sobre este curso

Regístrate gratis para hacer preguntas y guardar tu progreso.

✉ Entérate de lo nuevo

Recibe un aviso cuando publique nuevos cursos, workshops o investigaciones. Sin spam.