Cómo hacer validación cruzada en Machine Learning
La validación cruzada es una de las formas más fiables de evaluar un modelo de Machine Learning. En lugar de confiar en una única división entre entrenamiento y prueba, el modelo se evalúa varias veces en partes diferentes de los datos, lo que produce una estimación mucho más estable de su rendimiento real. Esta técnica es especialmente valiosa cuando tienes pocos datos, porque aprovecha todos los ejemplos tanto para entrenar como para probar. A continuación verás cómo hacer validación cruzada en Machine Learning con scikit-learn, con ejemplos sencillos y listos para ejecutar.
Requisitos previos
- Python 3 instalado en tu ordenador.
- Las librerías scikit-learn y NumPy instaladas (
pip install scikit-learn). - Saber qué es un modelo y la diferencia entre datos de entrenamiento y de prueba.
- Un editor de código o Jupyter Notebook para ejecutar los ejemplos.
Paso 1: Preparar los datos
Para este ejemplo usamos el conjunto de datos iris, que ya viene incluido en scikit-learn y no necesita ninguna descarga. Separamos las características (X) de la clase que queremos predecir (y). Las características son las columnas que describen cada flor —el largo y el ancho de los pétalos y los sépalos— y la clase es la especie que queremos identificar.
from sklearn.datasets import load_iris
data = load_iris()
X = data.data
y = data.target
print(X.shape)
El resultado (150, 4) indica 150 ejemplos y 4 características. Es un conjunto pequeño y equilibrado, perfecto para aprender.
Paso 2: Elegir el modelo
La validación cruzada funciona con cualquier modelo de scikit-learn. Vamos a usar una regresión logística como ejemplo. El parámetro max_iter garantiza que el entrenamiento tiene suficientes iteraciones para converger.
from sklearn.linear_model import LogisticRegression
model = LogisticRegression(max_iter=200)
Fíjate en que todavía no hemos entrenado nada: la función de validación cruzada se encargará de ello por nosotros. Si prefieres otro algoritmo, solo tienes que sustituir esta línea; el resto del código se mantiene igual.
Paso 3: Aplicar la validación cruzada
La función cross_val_score hace todo el trabajo. Con cv=5 divide los datos en 5 partes (llamadas folds), entrena el modelo 5 veces y, en cada una, usa una parte diferente para la prueba y el resto para el entrenamiento. De ahí viene el nombre k-fold. Al repetir la evaluación en varias divisiones, se reduce el riesgo de un resultado engañoso causado por una división especialmente fácil o difícil.
from sklearn.model_selection import cross_val_score
scores = cross_val_score(model, X, y, cv=5)
print(scores)
El resultado es una lista con 5 valores de exactitud, uno por cada fold. Cada valor está entre 0 y 1. Usar 5 o 10 folds es la opción más habitual.
Paso 4: Interpretar los resultados
Un solo número no basta: lo que importa es la media y la variación entre los folds. La media es la mejor estimación del rendimiento del modelo; la desviación típica muestra si ese rendimiento es consistente.
print("Mean accuracy:", scores.mean().round(3))
print("Std deviation:", scores.std().round(3))
Una desviación típica baja significa que el modelo se comporta de forma parecida en todas las partes de los datos, señal de un resultado fiable. Una alta sugiere que el modelo es inestable o que hay pocos datos. Por ejemplo, una media de 0.97 con una desviación de 0.02 indica que el modelo acierta alrededor del 97% de las veces de forma consistente.
Paso 5: Tener más control con StratifiedKFold
En los problemas de clasificación conviene que cada fold mantenga la misma proporción de clases que el conjunto completo. StratifiedKFold hace exactamente eso, y con shuffle=True y random_state los resultados se vuelven reproducibles. Sin estratificación, un fold podría quedarse con demasiados ejemplos de una sola especie, distorsionando la evaluación.
from sklearn.model_selection import StratifiedKFold
kfold = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_val_score(model, X, y, cv=kfold)
print(scores.mean().round(3))
Consejo: usa siempre el mismo random_state para poder comparar experimentos de forma justa.
Verificar el resultado
Si todo ha ido bien, print(scores) muestra un array con 5 números entre 0 y 1, y la media aparece como un único valor (por ejemplo, en torno a 0.97 en el conjunto iris). Confirma que la media está cerca de los valores individuales de los folds. Si aparece un aviso de convergencia, aumenta max_iter; si los valores varían mucho, puede que necesites más datos o un modelo diferente.
Conclusión
Ya puedes evaluar un modelo de forma robusta, sin depender de la suerte de una única división de los datos. El siguiente paso natural es combinar la validación cruzada con GridSearchCV para elegir los mejores hiperparámetros, o usar cross_val_predict para inspeccionar las predicciones fold a fold. Prueba a cambiar LogisticRegression por un RandomForestClassifier: ¿subirá la exactitud media?