Ir al contenido

Validación cruzada y selección de modelos

En simple: lee esto primero

Validación cruzada = turnarse para que cada parte de los datos sirva una vez de «examen», y así elegir bien la configuración del modelo.

Imagínalo así

Divides tus ejercicios de práctica en 5 grupos. Estudias con 4 y te tomas un mini-examen con el 5.º; repites cambiando cuál grupo es el examen. Tu nota real es el promedio de los 5 mini-exámenes.

Lo que tienes que llevarte
  • Sirve para elegir cosas que el modelo no aprende solo: λ, el grado del polinomio, la profundidad del árbol…
  • El conjunto de test no se toca hasta el final; si lo usas para decidir, tu nota final sale inflada.
  • Data leakage (fuga): cuando información del examen se «cuela» en el estudio, p. ej. escalar los datos usando también el test.
Palabras raras de esta página
K-fold
partir los datos en K grupos y rotar cuál es el de prueba.
hiperparámetro
una configuración que eliges tú (no la aprende el modelo).
regla de 1 SE
entre modelos casi igual de buenos, quedarse con el más simple.
fuga de datos
hacer trampa sin querer: el modelo «ve» algo del examen antes de tiempo.

Más abajo viene la versión completa, con fórmulas. Si una palabra aparece subrayada con puntos, pasa el mouse (o tócala) para ver qué significa.

Todo modelo tiene hiperparámetros: settings que eliges, no que se aprenden (λ de ridge/lasso, el grado, C y γ del SVM, el α de poda). Una sola partición train/validación es ruidosa: el «mejor λ» depende de qué puntos cayeron en validación, y con pocos datos cada punto separado es precioso.

  1. Parte los datos (de entrenamiento) en KK bloques iguales (típico K=5K=5 o 1010).
  2. Para cada kk: entrena con los K−1K-1 bloques restantes y valida en el bloque kk.
  3. Promedia los KK errores ⇒ un score para ese valor del hiperparámetro.
  4. Repite sobre una grilla de valores (λ en escala log) y quédate con el mínimo.
  5. Re-entrena con todos los datos de entrenamiento usando el valor elegido y evalúa una vez en el test.
CV(λ)=1K∑k=1KErrork(λ),λ⋆=arg⁡min⁡λCV(λ)\text{CV}(\lambda) = \frac1K\sum_{k=1}^K \text{Error}_k(\lambda),\qquad \lambda^\star = \arg\min_\lambda \text{CV}(\lambda)

Cada punto se usa para validar exactamente una vez: una estimación más honesta y de menor varianza del error de generalización.

Validación cruzada K-fold

Cada bloque se turna como validación; se entrena con los K−1 restantes y se promedian los K errores. El test set no participa: queda aparte y se usa una sola vez al final.

fold 1
fold 2
fold 3
fold 4
fold 5
test
(guardado)
entrenavalida (se rota)

Una sola partición es una lotería

Ridge con polinomio de grado 9 sobre 30 puntos. Las curvas finas son 8 particiones 50/50 al azar; la gruesa es el promedio 5-fold (la banda es ±1 error estándar).

-6-4-200.20.40.60.811.2log₁₀ λRMSE de validación
una partición 50/50 (marca: su «mejor λ»)media 5-fold ± 1 SEregla de 1 SE
λ con mínimo CV10^-2.2
λ por regla 1-SE10^-1.6
Las marcas de las particiones individuales se esparcen varios órdenes de magnitud: el «mejor λ» depende de qué puntos cayeron en validación. Promediar folds da una curva suave y estable. Regla de 1 SE: entre los λ cuyo error está a menos de un error estándar del mínimo, toma el más fuerte (modelo más simple, gratis).

La curva CV vs λ tiene otra vez forma de U: poca penalización sobreajusta (izquierda), demasiada subajusta (derecha). Se grafica con ±1 error estándar entre folds.

Regla de 1 SE: entre todos los λ cuyo error CV está a menos de un SE del mínimo, elige el más fuerte (el modelo más simple). Las diferencias menores a un SE son ruido: obtienes un modelo más simple, estable e interpretable gratis. La misma regla se usa para podar árboles.

Hay fuga cuando información de evaluación influye en el entrenamiento, y los scores salen optimistas. Formas típicas:

  • Elegir hiperparámetros mirando el test.
  • Estandarizar (o imputar, o seleccionar features) usando todo el dataset antes de partir. El escalador se ajusta solo con los folds de entrenamiento y luego se aplica a validación/test (en scikit-learn: Pipeline + GridSearchCV).
  • Series de tiempo partidas al azar (hay que respetar el orden temporal).

La maldición del ganador (y por qué el test se usa una vez)

Las etiquetas son puro ruido: ningún modelo puede superar 50% de verdad. Aun así, si pruebas muchos modelos y te quedas con el mejor en validación, su score de validación sale inflado. En el test nuevo vuelve a ~50%.

00.20.40.60.81accuracy en validación
accuracy de validación de cada modeloel «ganador» en validaciónel ganador en un test nuevo
mejor accuracy en validación66.0%
ese modelo en test nuevo51.6%
optimismo (sesgo de selección)+14.4 pts
Con 100 intentos y 50 puntos de validación, el mejor «acierta» 66% por pura suerte. Más intentos o menos datos de validación ⇒ más optimismo. Por eso: (1) el score que usaste para elegir no es una estimación honesta; (2) el test se reserva y se usa una sola vez; (3) K-fold reduce el ruido de la validación, y la CV anidada mide el procedimiento completo de selección.

Barriendo el grado de 0 a 18: el RMSE de entrenamiento no deja de bajar mientras el de validación traza la U; se elige el mínimo de validación (grado 8 en la slide). Es el trade-off sesgo–varianza resuelto con datos.

Tienes 1000 ejemplos. Reservas 200 para test. Con los 800 restantes haces 5-fold CV para 10 valores de λ. ¿Cuántos modelos entrenas en total y con cuántos puntos cada uno?

Ver solución
  • CV: 5 folds×10 valores=505 \text{ folds} \times 10 \text{ valores} = 50 modelos, cada uno con 800⋅45=640800\cdot\tfrac45 = 640 puntos (validando en 160).
  • Luego 1 modelo final con los 800 puntos y el λ elegido, evaluado una vez en los 200 de test.
  • Total: 51 entrenamientos.

Autoevaluación

0/4 correctas · 0 respondidas
1. En K-fold, ¿cuántas veces se usa cada punto como validación?
2. ¿Cuándo se ajusta el StandardScaler correctamente?
3. La regla de 1 SE elige…
4. ¿Por qué no basta una sola partición train/validación?
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.linear_model import Ridge
from sklearn.model_selection import GridSearchCV, KFold, cross_val_score
import numpy as np
# el escalador vive DENTRO del pipeline: en cada fold se ajusta solo con train
pipe = make_pipeline(PolynomialFeatures(9), StandardScaler(), Ridge())
grid = GridSearchCV(pipe, {'ridge__alpha': np.logspace(-6, 2, 30)},
cv=KFold(5, shuffle=True, random_state=0),
scoring='neg_root_mean_squared_error')
grid.fit(X_tr, y_tr) # el test no aparece aquí
print(grid.best_params_, -grid.best_score_)
print('test:', -grid.score(X_test, y_test)) # una sola vez, al final
# CV anidada: estima qué tan bueno es TODO el procedimiento de selección
outer = cross_val_score(grid, X_tr, y_tr, cv=5)

Fuente ·ML_Course_regularization_and_model_selection.pdf (slides 27–34) · ML_Course/session-03c-model-selection-cv.ipynb · ML_Course/session-01d-train-val-test.ipynb