11. Conjuntos de entrenamiento, validación y prueba

11.1 Por qué no alcanza con “entrenar y medir”

Cuando empezamos en Machine Learning, suele parecer suficiente dividir los datos en dos partes:

  • una para entrenar;
  • otra para medir.

Esa idea es correcta como punto de partida, pero se vuelve insuficiente cuando además queremos comparar modelos o ajustar parámetros. En ese caso, si miramos demasiadas veces el conjunto de prueba, terminamos adaptando nuestras decisiones a ese conjunto y dejamos de medir de forma objetiva.

11.2 El rol de cada conjunto

La separación clásica en tres grupos ayuda a resolver ese problema:

  • Entrenamiento: se usa para que el modelo aprenda patrones.
  • Validación: se usa para comparar variantes del modelo y elegir configuraciones.
  • Prueba: se usa al final, una sola vez, para estimar cómo rendirá el modelo en datos no vistos.

La idea central es esta: el conjunto de prueba debe mantenerse “virgen” hasta el final. Si lo usamos antes, dejamos de tener una medida confiable del rendimiento real.

11.3 Una analogía simple

Imaginemos que estás preparando un examen:

  • el entrenamiento sería estudiar y practicar;
  • la validación sería hacer simulacros para ver qué estrategia funciona mejor;
  • la prueba sería el examen real.

Si usaras las preguntas exactas del examen real para decidir cómo prepararte, la nota final dejaría de ser una evaluación honesta. En Machine Learning pasa algo parecido.

11.4 Ejemplo claro: elegir el mejor valor de K

Vamos a usar un clasificador KNN. Este algoritmo necesita un parámetro importante: n_neighbors, es decir, cuántos vecinos considerar. En lugar de elegirlo al azar, vamos a probar varios valores y usar el conjunto de validación para decidir.

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score

datos = pd.DataFrame({
    "horas_estudio": [1, 2, 2, 3, 4, 5, 6, 6, 7, 8, 9, 10],
    "practicas":     [0, 1, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6],
    "aprobo":        [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1]
})

X = datos[["horas_estudio", "practicas"]]
y = datos["aprobo"]

# 1) Separación inicial: entrenamiento+validación / prueba
X_temp, X_test, y_temp, y_test = train_test_split(
    X, y, test_size=0.25, random_state=42, stratify=y
)

# 2) Separación interna: entrenamiento / validación
X_train, X_val, y_train, y_val = train_test_split(
    X_temp, y_temp, test_size=0.33, random_state=42, stratify=y_temp
)

print("Entrenamiento:", X_train.shape, y_train.shape)
print("Validación:", X_val.shape, y_val.shape)
print("Prueba:", X_test.shape, y_test.shape)

mejor_k = None
mejor_score = -1

for k in [1, 3, 5]:
    modelo = KNeighborsClassifier(n_neighbors=k)
    modelo.fit(X_train, y_train)
    y_val_pred = modelo.predict(X_val)
    score = accuracy_score(y_val, y_val_pred)
    print(f"Exactitud con k={k} en validación: {score}")

    if score > mejor_score:
        mejor_score = score
        mejor_k = k

print("\nMejor valor de k:", mejor_k)

# 3) Evaluación final con el mejor k sobre prueba
modelo_final = KNeighborsClassifier(n_neighbors=mejor_k)
modelo_final.fit(X_train, y_train)
y_test_pred = modelo_final.predict(X_test)

print("Exactitud final en prueba:", accuracy_score(y_test, y_test_pred))

nuevo_estudiante = pd.DataFrame({
    "horas_estudio": [7],
    "practicas": [4]
})

prediccion = modelo_final.predict(nuevo_estudiante)[0]
print("Predicción para el nuevo estudiante:", prediccion)

Salida resumida esperada:

Entrenamiento: ...
Validación: ...
Prueba: ...
Exactitud con k=1 en validación: ...
Exactitud con k=3 en validación: ...
Exactitud con k=5 en validación: ...

Mejor valor de k: ...
Exactitud final en prueba: ...
Predicción para el nuevo estudiante: 1

11.5 Qué está ocurriendo en el ejemplo

El flujo del programa sigue una lógica muy importante:

  • primero se aparta un conjunto de prueba;
  • luego, con lo que queda, se separan entrenamiento y validación;
  • se prueban varias opciones de k usando solo validación;
  • recién al final se consulta el conjunto de prueba.

Eso evita elegir el modelo mirando directamente el rendimiento en prueba.

11.6 Explicación detallada del código

  • X_temp, X_test, y_temp, y_test: crea una primera división donde el conjunto de prueba queda reservado.
  • X_train, X_val, y_train, y_val: divide el bloque restante en entrenamiento y validación.
  • for k in [1, 3, 5]: recorre distintas configuraciones del algoritmo.
  • accuracy_score(y_val, y_val_pred): mide cuál configuración funciona mejor en validación.
  • mejor_k: guarda la opción que logró el mejor resultado.
  • modelo_final: entrena el modelo con el valor elegido y lo evalúa finalmente en prueba.

11.7 La idea más importante del tema

El conjunto de validación no existe para “decorar” el flujo. Existe para que podamos tomar decisiones sin contaminar la evaluación final.

Si cambias el modelo, los parámetros, las variables o el preprocesamiento mirando el conjunto de prueba, ya no estás evaluando: estás optimizando contra ese conjunto. En consecuencia, el valor final deja de representar cómo responderá el modelo frente a datos realmente nuevos.

11.8 Cuándo usar esta estrategia

La separación en entrenamiento, validación y prueba es muy útil cuando:

  • quieres comparar varios algoritmos;
  • quieres ajustar hiperparámetros;
  • quieres elegir entre distintas transformaciones de datos;
  • quieres reservar una evaluación final honesta.

En datasets pequeños, a veces esta división deja muy pocos datos para aprender. En esos casos, suele usarse validación cruzada, que veremos más adelante.

11.9 Errores frecuentes

  • Usar el conjunto de prueba para elegir el modelo: ese es el error más común y más dañino.
  • No separar validación: obliga a decidir “a ojo” o usando prueba de manera incorrecta.
  • Medir muchas veces sobre prueba: cada consulta adicional reduce la objetividad de la evaluación.
  • Suponer que una sola partición basta siempre: en datasets pequeños conviene considerar validación cruzada.

11.10 Qué deberías retener

  • Entrenamiento sirve para aprender.
  • Validación sirve para elegir.
  • Prueba sirve para estimar el rendimiento final.
  • El conjunto de prueba debe reservarse hasta el final.
  • Separar bien los datos mejora la calidad de la evaluación y evita conclusiones engañosas.