18 · Construyendo datos de fine-tuning
El fine-tuning es tan bueno como su dataset. El formato es una lista de conversaciones de chat; el trabajo es validación, deduplicación y un split train/val limpio — justo la parte aburrida y decisiva.
Un dataset de fine-tuning es una lista de CONVERSACIONES, cada una una lista de mensajes {role, content}. El modelo aprende a producir el turno del asistente dado todo lo anterior. La ingeniería no es la llamada de entrenamiento — es validar el formato, quitar duplicados y separar train/val para que tu métrica no sea mentira.
Sin esto:
Si te saltas la validación y la deduplicación, entrenas con filas malformadas y duplicados que se filtran a tu conjunto de validación, así que el modelo aprende ruido y tu puntaje de evaluación miente sobre qué tan bueno es.
Hacer fine-tuning de un modelo de chat significa mostrarle muchas conversaciones y dejar que aprenda a producir la respuesta del asistente. El formato de facto (usado por OpenAI, y cercano a lo que esperan los chat templates de transformers) es un objeto JSON por ejemplo, cada uno con una lista messages:
{"messages": [
{"role": "system", "content": "Eres un bot de soporte conciso."},
{"role": "user", "content": "¿Cómo restablezco mi contraseña?"},
{"role": "assistant", "content": "Abre Configuracion -> Seguridad -> Restablecer contraseña."}
]}
El modelo se entrena para predecir el turno del asistente dados los turnos de sistema + usuario anteriores. Así que tu dataset es literalmente un montón de pares "aquí está la situación, aquí está la respuesta ideal" vestidos de chat.
Ahora la parte que de verdad decide si el fine-tuning funciona — y no es glamorosa:
- Validación de formato. Cada ejemplo necesita una lista
messagesno vacía, valores deroleválidos,contentno vacío y al menos un turnoassistant(si no, no hay nada que aprender). Las filas malformadas o bien hacen fallar el job o enseñan basura en silencio. - Deduplicación. Los ejemplos duplicados sobre-pesan lo que dicen y — peor — el mismo ejemplo puede caer en train y en validación, así que tu puntaje se infla por memorización. Deduplica por contenido normalizado.
- Split train/val. Aparta una porción con la que el modelo nunca entrena, para medir si generaliza. El split debe ser determinista (con semilla) para que las corridas sean comparables, y la deduplicación debe ocurrir antes del split para que ningún ejemplo cruce la frontera.
La calidad gana a la cantidad aquí: unos cientos de ejemplos limpios, consistentes y deduplicados suelen superar a miles ruidosos.
Abajo está el pipeline completo sobre un dataset diminuto. A propósito contiene un ejemplo malformado (sin turno de asistente), un ejemplo con contenido vacío y un duplicado, para que veas cómo dispara cada guarda. Lee el reporte impreso: te dice exactamente qué filas se rechazaron y por qué, luego deduplica, luego hace un split con semilla, basado en hash para que el mismo ejemplo caiga siempre del mismo lado — sin fugas.
Python (in browser)
El trabajo real del fine-tuning: validar cada conversación, descartar duplicados y filas malformadas, y luego separar de forma determinista para que ningún ejemplo se filtre entre train/val.
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
Una dimensión de calidad que el código no puede atrapar del todo es la consistencia de etiquetas: si dos preguntas de usuario casi idénticas reciben respuestas de asistente contradictorias en tu dataset, le estás enseñando al modelo a ser inconsistente. Una verificación automática barata es agrupar ejemplos por su turno de usuario (normalizado) y marcar cualquier grupo cuyas respuestas de asistente difieran — sacando a la luz las filas que un humano debe reconciliar antes de entrenar.
La llamada de entrenamiento del proveedor es una línea; el dataset que le das es todo el trabajo. Valida, deduplica y separa antes de este punto — la API no arreglará tus datos.
¿Por qué debes deduplicar el dataset ANTES del split train/validación?
- Los datos de fine-tuning son una lista de conversaciones: cada ejemplo es un objeto {messages:[{role,content}...]}, y el modelo aprende a producir el turno del asistente.
- Valida cada fila (messages no vacíos, roles válidos, content no vacío, al menos un turno de asistente) y rechaza el resto — basura entra, basura se aprende.
- Deduplica por contenido normalizado, LUEGO haz un split train/val determinista (con semilla/hash) para que ningún ejemplo se filtre y tu métrica sea honesta.
Todo modelo afinado — desde chatbots de voz de marca hasta clasificadores que emiten JSON — se produce de un JSONL de conversaciones de chat que alguien validó, deduplicó y separó.
Si lo quitas: Si te saltas la higiene de datos, entrenas con filas malformadas y filtras duplicados a la validación, así que el modelo aprende ruido y tu puntaje de evaluación miente.