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
- La tesis del módulo
- Destilación: el mejor negocio del curso
- La economía, que es el punto
- El paso que decide el resultado: filtrar
- Continued pretraining: cuando falta el dominio
- Cuántos tokens
- El olvido catastrófico y el truco de la mezcla
- Expandir el vocabulario del tokenizador
- RAFT: entrenar para usar el contexto recuperado
- Model merging: sumar modelos sin entrenar
- Entrenar tool-calling y salida estructurada
- La tabla que resume el módulo
- Errores comunes
- La frase para llevarse
- Qué viene
- Material relacionado
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.
| Etapa | Modelo grande de API | Alumno 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 tokens | Alto | 1 a 2 órdenes de magnitud menor |
| Latencia p95 | Segundos | Cientos de ms |
| Datos salen de tu infra | Sí | No |
| Techo de calidad | El del maestro | Cerca 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.
| Objetivo | Tokens de dominio | Realidad práctica |
|---|---|---|
| Jerga y estilo de un dominio acotado | ~100 M – 1.000 M | Alcanzable con un corpus corporativo grande |
| Dominio técnico completo | ~1.000 M – 10.000 M | Requiere 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:
- 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.
- Incluye llamadas que fallan. Un turno
toolcon 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. - Aplica loss masking correctamente (módulo 3): la loss va sobre los turnos del assistant, incluidos los
tool_calls, y no sobre los turnostool, que son entrada del entorno y no algo que el modelo deba aprender a generar.
La tabla que resume el módulo
| Técnica | Qué problema resuelve | Costo relativo | Cuándo vale la pena |
|---|---|---|---|
| Destilación | El modelo bueno es muy caro o lento | Bajo (generar + un SFT) | Casi siempre, si tienes volumen. La más rentable |
| Continued pretraining | El modelo no conoce el dominio ni su vocabulario | Alto (mil millones de tokens) | Solo con corpus enorme; si no, RAG |
| Expandir vocabulario | La jerga consume demasiados tokens | Alto (obliga a pretraining) | Junto con continued pretraining, nunca solo |
| RAFT | El modelo se deja llevar por distractores del RAG | Bajo (reusa el golden dataset) | Operas un RAG y ya lo evalúas |
| Model merging | Quieres combinar dos especialistas | Mínimo (minutos de CPU) | Prueba barata; siempre medir el resultado |
| Tool-calling / salida estructurada | El modelo elige mal la herramienta | Medio | Despué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
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.