Cuando dividimos los datos en entrenamiento y prueba, el resultado puede cambiar bastante según qué casos hayan quedado en cada grupo. Con datasets pequeños o medianos, esa variación puede ser importante.
Eso significa que un modelo podría parecer muy bueno con una partición y más regular con otra. Entonces surge una pregunta razonable: ¿cómo obtener una evaluación más estable?
La respuesta más usada es la validación cruzada.
La validación cruzada consiste en dividir el dataset en varias partes llamadas folds. En cada vuelta:
Luego el proceso se repite cambiando qué fold queda reservado para validación. Al final se obtienen varias métricas y se analiza el promedio.
Si usamos validación cruzada con 5 folds:
Así, todos los datos participan tanto en entrenamiento como en validación, pero nunca cumplen ambas funciones al mismo tiempo en la misma iteración.
La validación cruzada ayuda a:
No reemplaza por completo al conjunto de prueba final cuando queremos una evaluación definitiva, pero sí es una herramienta excelente para seleccionar enfoques durante el desarrollo.
Vamos a usar un problema sencillo de clasificación: predecir si un estudiante aprobará según sus horas de estudio y la cantidad de prácticas realizadas.
Aplicaremos un modelo KNN y lo evaluaremos con validación cruzada de 4 folds. Veremos la exactitud obtenida en cada vuelta y el promedio final.
import pandas as pd
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn.neighbors import KNeighborsClassifier
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"]
modelo = KNeighborsClassifier(n_neighbors=3)
cv = StratifiedKFold(n_splits=4, shuffle=True, random_state=42)
scores = cross_val_score(modelo, X, y, cv=cv, scoring="accuracy")
print("Exactitud en cada fold:", scores)
print("Promedio de exactitud:", scores.mean())
print("Desviación estándar:", scores.std())
modelo.fit(X, y)
nuevo_estudiante = pd.DataFrame({
"horas_estudio": [7],
"practicas": [4]
})
prediccion = modelo.predict(nuevo_estudiante)[0]
print("Predicción para el nuevo estudiante:", prediccion)
Salida resumida esperada:
Exactitud en cada fold: [...]
Promedio de exactitud: ...
Desviación estándar: ...
Predicción para el nuevo estudiante: ...
En lugar de hacer una única división entre entrenamiento y prueba, cross_val_score repite el entrenamiento y la evaluación varias veces.
Como usamos StratifiedKFold, cada fold intenta conservar una proporción parecida entre aprobados y no aprobados. Eso es importante en clasificación, porque evita particiones muy desbalanceadas.
StratifiedKFold(n_splits=4, shuffle=True, random_state=42): define una validación cruzada de 4 folds, mezclando los datos y manteniendo el equilibrio entre clases.cross_val_score(...): entrena y evalúa el modelo una vez por cada fold.scores: guarda la exactitud obtenida en cada iteración.scores.mean(): calcula el rendimiento promedio.scores.std(): muestra cuánta variación hubo entre folds.Ese último punto es muy útil: no solo interesa el promedio, también interesa si los resultados son estables o cambian demasiado.
Si el promedio de exactitud es alto y la desviación estándar es baja, el modelo parece rendir bien de forma consistente.
Si el promedio es aceptable, pero la desviación es muy grande, hay una señal de inestabilidad: el modelo funciona bien con algunos folds y peor con otros.
En ese caso conviene revisar:
La validación cruzada es especialmente recomendable cuando:
Es habitual combinarla más adelante con herramientas como GridSearchCV, que veremos en otro tema.
cross_val_score permite aplicarla de forma simple en Scikit-learn.