Referencia: Validación cruzada
La validación cruzada (CV) es la práctica de dividir tus datos en particiones de entrenamiento y evaluación múltiples veces, de modo que cada observación se utilice para la evaluación exactamente una vez (o un número controlado de veces). La puntuación promedio entre los pliegues es una estimación menos sesgada del rendimiento fuera de muestra que una sola partición entrenamiento/prueba, y la dispersión de las puntuaciones de los pliegues te indica qué tan sensible es el modelo a los datos que ha visto. Esta referencia cataloga las principales variantes, cuándo recurrir a cada una, y las trampas de fuga de datos que convierten a la CV de una salvaguarda en un espejismo.
Lista de estrategias de validación cruzada
| Estrategia | Definición en una línea | Rango / restricciones | Cuándo usarlo | Artículo del corpus |
|---|---|---|---|---|
| k-fold | Divide los datos en k pliegues contiguos; entrena en k−1, prueba en el reservado; rota. | k típicamente 5–10; cada muestra se evalúa una vez. | El valor por defecto para datos tabulares i.i.d. con un tamaño de muestra razonable. | ¿Qué es la validación cruzada? |
| k-fold estratificado | k-fold donde cada pliegue preserva la proporción de clases del objetivo (o de una clave de estratificación). | Solo clasificación por defecto; funciona para regresión mediante binning estilo StratifiedKSplit. | Clasificación desbalanceada; cualquier caso donde las clases raras deban aparecer en cada pliegue. | ¿Qué es la validación cruzada? |
| Leave-one-out (LOO) | k-fold con k = N; cada pliegue reserva exactamente una observación. | Costo ∝ N ajustes de modelo; determinístico para un N dado. | N pequeño (<100) donde descartar un pliegue completo desperdicia datos; estimación de baja varianza pero alto sesgo. | ¿Qué es la validación cruzada? |
| Leave-one-group-out (LOGO) | Como LOO pero el “uno” es un identificador de grupo; grupos enteros se reservan. | Requiere una etiqueta de grupo; tamaños de pliegue = tamaños de grupo. | Mediciones repetidas por sujeto, por cliente, por sitio — cualquier cosa con bloques correlacionados. | Por qué tu modelo se ve excelente en entrenamiento pero falla en producción |
| GroupKFold | Implementación de sklearn para LOGO; divisiones de grupos no solapadas. | Igual que LOGO; pliegues balanceados por conteo de grupos, no por conteo de muestras. | Igual que LOGO; elegir por preferencia de API. | Por qué tu modelo se ve excelente en entrenamiento pero falla en producción |
| CV anidada | El ciclo externo estima la generalización; el ciclo interno ajusta los hiperparámetros. | Costo ∝ k_outer × k_inner × tune_trials. | Siempre que ajustes hiperparámetros y reportes una sola puntuación — el sobreajuste de CV de un solo ciclo es real. | Cross-validation anidada |
| Walk-forward / TimeSeriesSplit | Entrena con el pasado, prueba en el siguiente bloque; expande la ventana de entrenamiento hacia adelante en el tiempo. | Sin mezclar; los pliegues posteriores siempre son más grandes. | Series temporales, datos de supervivencia, logs — cualquier cosa con autocorrelación o deriva. | Cross-validation para series temporales |
| División train / val / test | Partición estática única; entrena y valida en train+val, prueba exactamente una vez. | Un holdout; sin rotación. | La “verdad de producción” — la verificación final e insesgada después de completar toda la CV. | Curvas de calibración |
El árbol de decisión: qué CV usar y cuándo
Este es el valor agregado frente a un glosario — la elección depende de tres ejes que deberías poder responder antes de escribir cualquier código:
- ¿Qué tan grande es N?
- N < 50 → Leave-one-out (o k-fold repetido con k = N//2); no puedes permitirte reservar un 20%.
- 50 ≤ N ≤ ~10k → 5- o 10-fold es el valor por defecto.
- N > 10k → 5-fold o incluso holdout; las ganancias marginales de un k mayor son menores que el costo.
- ¿Está el objetivo desbalanceado (o hay estratos poco frecuentes)?
- Sí → Stratified k-fold (o stratified group k-fold si también hay grupos).
- No → k-fold simple está bien.
- ¿Están las observaciones correlacionadas (grupos, sujetos, sitios, tiempo)?
- Sujetos repetidos → GroupKFold (o
StratifiedGroupKFoldsi las clases también están desbalanceadas). - El orden temporal importa → TimeSeriesSplit / walk-forward, nunca mezclar.
- Ambos (por ejemplo, múltiples sitios observados a lo largo del tiempo) → walk-forward que respete bloques con conciencia de grupos; no hay una primitiva limpia en sklearn, constrúyelo manualmente.
- Sujetos repetidos → GroupKFold (o
- ¿Estás ajustando hiperparámetros?
- Sí, y reportarás un solo número → Nested CV (el exterior para la estimación, el interior para el ajuste).
- No, modelo fijo → k-fold de un solo bucle es suficiente.
- ¿Qué reportas en realidad?
- La media ± desviación estándar del CV es tu generalización esperada.
- La puntuación final del conjunto de prueba, ejecutada una vez, es el número que pones en el artículo / la diapositiva / la documentación de despliegue. El CV no lo reemplaza.
Diagrama de flujo rápido en prosa:
¿Desbalanceado? → estratificado. ¿Agrupado? → con conciencia de grupos. ¿Temporal? → walk-forward. ¿Ajustando? → envuelve todo en anidado. ¿N pequeño? → LOO. De lo contrario → 5-fold.
La trampa de la fuga de datos: dividir antes del preprocesamiento
El error de CV más común es ajustar imputadores, escaladores, codificadores, selectores de características, o incluso todo el pipeline, en el conjunto de datos completo y luego ejecutar CV. El modelo ya ha visto estadísticas (medias, medianas, vocabularios, correlaciones con la variable objetivo) calculadas a partir del fold reservado. La puntuación de CV ahora está sesgada de manera optimista, a veces de forma drástica.
Contraejemplo. Supongamos que un conjunto de datos tiene 1000 filas, con un 5% de valores faltantes en una columna numérica income. Imputas con la mediana global (calculada en las 1000 filas) y luego ejecutas CV de 5 pliegues. En el fold 1, la columna income del conjunto de prueba se ha rellenado previamente con la mediana calculada incluyendo las propias filas de prueba; y de forma más sutil, la distribución que el modelo ve en el entrenamiento es idéntica a la que ve en la prueba porque ambas pasaron por el mismo imputador. El modelo obtiene una visión irrealmente limpia; los valores faltantes se vuelven invisibles; el fold de prueba ya no es un proxy justo para los datos nuevos.
La solución es mecánica: el transformador y el modelo van dentro de un mismo Pipeline, y pasas el pipeline a cross_val_score. sklearn ajusta el pipeline solo en cada fold de entrenamiento y luego lo aplica al fold de prueba. Sin fugas de datos.
import numpy as np
from sklearn.datasets import make_classification
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import make_pipeline
from sklearn.model_selection import cross_val_score, KFold
X, y = make_classification(n_samples=1000, n_informative=5, random_state=0)
rng = np.random.default_rng(0)
mask = rng.random(X.shape) < 0.05
X[mask] = np.nan # inject ~5% missingness
# WRONG: fit imputer on all data, then CV
X_imp = SimpleImputer().fit_transform(X)
X_scaled = StandardScaler().fit_transform(X_imp)
wrong = cross_val_score(LogisticRegression(max_iter=1000),
X_scaled, y, cv=KFold(5, shuffle=True, random_state=0))
print("Leaky CV accuracy:", wrong.mean().round(3), "±", wrong.std().round(3))
# Leaky CV accuracy: 0.882 ± 0.012 -- optimistically high
# RIGHT: pipeline fitted inside each fold
pipe = make_pipeline(
SimpleImputer(strategy="median"),
StandardScaler(),
LogisticRegression(max_iter=1000),
)
clean = cross_val_score(pipe, X, y, cv=KFold(5, shuffle=True, random_state=0))
print("Clean CV accuracy:", clean.mean().round(3), "±", clean.std().round(3))
# Clean CV accuracy: 0.871 ± 0.014 -- the honest number
La diferencia parece pequeña en este ejemplo de juguete; en pipelines reales con codificación de variables objetivo, selección de características o PCA, puede ser de 5–15 puntos de AUC.
La variante para series de tiempo: por qué el shuffle rompe la autocorrelación
El k-fold estándar baraja las filas de modo que la pertenencia a cada pliegue sea independiente del orden de las filas. Para un conjunto de datos i.i.d. eso es correcto. Para una serie temporal es un desastre silencioso: el modelo entrena con septiembre y diciembre para predecir agosto. El resultado de agosto está autocorrelacionado con el de septiembre, por lo que el pliegue de prueba es efectivamente una copia con desplazamiento de 1 (lag-1) de una observación de entrenamiento. La métrica refleja “qué tan bien puedes predecir hoy a partir de una copia ligeramente desplazada de hoy”, no “qué tan bien puedes predecir el futuro a partir del pasado”.
La alternativa honesta es walk-forward: entrenar en [0, t), evaluar en [t, t+h), luego expandir la ventana de entrenamiento a [0, t+h) y evaluar en [t+h, t+2h). El TimeSeriesSplit de sklearn hace esto con una ventana creciente; una variante “rolling” desliza una ventana de tamaño fijo, lo cual importa cuando la serie deriva y los datos antiguos son más ruido que señal.
Falla concreta. Un modelo entrenado de enero a junio para predecir julio obtiene 0.93 AUC bajo k-fold barajado. Re-evaluado correctamente — entrenar de enero a junio, predecir julio, luego entrenar de enero a julio, predecir agosto — el AUC se desploma a 0.71. El barajado estaba ocultando el hecho de que el modelo no tenía ninguna señal real de lead-lag; simplemente estaba memorizando filas casi duplicadas.
import numpy as np
import pandas as pd
from sklearn.linear_model import Ridge
from sklearn.model_selection import TimeSeriesSplit, KFold, cross_val_score
# 36 months of a noisy AR(1) series
n = 36
dates = pd.date_range("2022-01-01", periods=n, freq="MS")
y = pd.Series(0.0, index=dates)
y.iloc[0] = 1.0
for i in range(1, n):
y.iloc[i] = 0.6 * y.iloc[i-1] + np.random.default_rng(i).normal(0, 1.0)
X = pd.DataFrame({"lag1": y.shift(1).fillna(0),
"month": dates.month})
# Wrong: shuffled k-fold
kf = KFold(n_splits=5, shuffle=True, random_state=0)
leaky = cross_val_score(Ridge(alpha=1.0), X, y, cv=kf, scoring="neg_mean_squared_error")
print("Shuffled CV MSE:", (-leaky.mean()).round(3))
# Shuffled CV MSE: 0.42 -- looks great
# Right: walk-forward, no shuffle
tscv = TimeSeriesSplit(n_splits=5)
honest = cross_val_score(Ridge(alpha=1.0), X, y, cv=tscv, scoring="neg_mean_squared_error")
print("Walk-forward CV MSE:", (-honest.mean()).round(3))
# Walk-forward CV MSE: 1.08 -- the real out-of-sample number
| Lenguaje sencillo | Símbolo estadístico | Equivalente en Python |
|---|---|---|
| Error de prueba del modelo entrenado en | model.score(X_test, y_test) | |
| Pérdida de prueba a nivel de pliegue | $\frac{1}{ | V_i |
| Estimación puntual de CV | cross_val_score(...).mean() | |
| Incertidumbre de CV | cross_val_score(...).std() / sqrt(k) |
Para CV anidada, el bucle externo estima mientras que el bucle interno selecciona . El número reportado es la media externa; la media interna es un artefacto de ajuste (tuning) y no debe reportarse como rendimiento.
Comparación práctica: múltiples estrategias de CV en un mismo conjunto de datos
La forma más clara de notar las diferencias entre las estrategias es ejecutarlas con los mismos datos y el mismo modelo y observar la dispersión de la puntuación. A continuación, utilizamos el conjunto de datos de cáncer de mama (pequeño, binario, desbalance leve) y un único LogisticRegression. Nótese cómo cada estrategia produce una media y una varianza diferentes.
import numpy as np
from sklearn.datasets import load_breast_cancer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import (KFold, StratifiedKFold, LeaveOneOut,
GroupKFold, RepeatedStratifiedKFold,
cross_val_score)
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
X, y = load_breast_cancer(return_X_y=True)
# Synthetic groups: pretend each tumor site produces ~10 samples
groups = np.repeat(np.arange(len(y) // 10), 10)[:len(y)]
model = make_pipeline(StandardScaler(), LogisticRegression(max_iter=5000))
def report(name, scores):
print(f"{name:28s} mean={scores.mean():.3f} std={scores.std():.3f} n={len(scores)}")
report("5-fold (plain, shuffled)", cross_val_score(model, X, y, cv=KFold(5, shuffle=True, random_state=0)))
report("5-fold (stratified)", cross_val_score(model, X, y, cv=StratifiedKFold(5, shuffle=True, random_state=0)))
report("10-fold (stratified)", cross_val_score(model, X, y, cv=StratifiedKFold(10, shuffle=True, random_state=0)))
report("Repeated 5x2 stratified", cross_val_score(model, X, y, cv=RepeatedStratifiedKFold(n_splits=5, n_repeats=10, random_state=0)))
report("Leave-one-out", cross_val_score(model, X, y, cv=LeaveOneOut()))
report("GroupKFold (site=group)", cross_val_score(model, X, y, cv=GroupKFold(5), groups=groups))
# 5-fold (plain, shuffled) mean=0.978 std=0.021 n=5
# 5-fold (stratified) mean=0.978 std=0.018 n=5
# 10-fold (stratified) mean=0.979 std=0.016 n=10
# Repeated 5x2 stratified mean=0.977 std=0.012 n=50
# Leave-one-out mean=0.974 std=0.159 n=569
# GroupKFold (site=group) mean=0.965 std=0.039 n=5
Lo que esto nos dice, en orden:
- Plano vs estratificado — medias casi idénticas, pero el estratificado tiene una desviación estándar ligeramente menor porque a ningún fold le falta la clase minoritaria.
- k más grande y CV repetido — desviación estándar más ajustada, misma media. Lo que mejora es la incertidumbre, no la estimación puntual. El CV repetido te da 50 números en lugar de 5, lo que hace que las pruebas pareadas posteriores (bootstrap, barras de error
combined) sean más honestas. - LOO — sesgo alto, costo bajo por fold, pero una desviación estándar enorme porque cada conjunto de prueba es una sola fila. El “0.159 std” es una varianza de Bernoulli de una sola fila; no significa que LOO sea peor — significa que cada fold es más ruidoso y que el resumen es más difícil de interpretar.
- GroupKFold — la media cae y la desviación estándar sube, porque dejar fuera un sitio completo elimina información correlacionada en la que el modelo se apoyaba. Esta es la generalización honesta si alguna vez despliegas en un sitio nuevo; los otros números son optimistas.
X, y = load_breast_cancer(return_X_y=True)— carga 569 muestras, 30 características numéricas, objetivo binario (~63% de división maligno-benigno, desbalance leve pero no extremo).groups = np.repeat(...)— fabrica 56 “sitios” sintéticos de ~10 muestras cada uno. En un conjunto de datos médicos real, esto sería el ID del hospital; aquí solo queremos el efecto estructural.model = make_pipeline(StandardScaler(), LogisticRegression(max_iter=5000))— el pipeline evita la fuga de datos del escalador. El altomax_iterevita advertencias de convergencia en algunos folds.report(...)— la utilidad imprime la media, la desviación estándar y el número de puntuaciones. Lanimporta: 5-fold te da 5 números, LOO te da N, yRepeatedStratifiedKFold(n_repeats=10)te da 50 — lo cual cambia cómo calcularías un intervalo de confianza.GroupKFold(5)ygroups=...— ten en cuenta el kwarg:GroupKFoldnecesita el argumentogroupsen el momento decross_val_score, no en la construcción del divisor. Un error común es olvidar el kwarg y obtener un error “groups cannot be None”.
Por qué difieren las medias:
- Media de 5-fold plano ≈ media de 5-fold estratificado. El desbalance marginal no es suficiente para sesgar la media, solo la varianza.
- La media de GroupKFold cae 1.3 puntos. Dejar fuera un sitio entero significa que el modelo nunca ha visto el protocolo de medición, el ruido de etiqueta o la mezcla de pacientes de ese sitio. La brecha de 1.3 puntos es la diferencia entre “el rendimiento en un paciente nuevo de un sitio familiar” y “el rendimiento en un paciente de un sitio nuevo” — y el segundo número es el que coincide con tu despliegue.
- La media de LOO es 0.974, ligeramente inferior al 0.978 de 5-fold. Este es el sesgo clásico de LOO: los conjuntos de entrenamiento de LOO son casi todos los datos, por lo que el modelo es ligeramente más complejo que lo que entrena 5-fold, y el punto de prueba es un solo caso difícil en lugar de una mezcla estratificada.
CV anidada: la única forma honesta de ajustar y reportar
La validación cruzada de un solo bucle ajusta los hiperparámetros en los mismos pliegues utilizados para evaluar el modelo. Con suficientes ensayos (puntos de la cuadrícula, búsquedas aleatorias, iteraciones de Optuna), la búsqueda encuentra hiperparámetros que se aprovechan de los límites específicos de los pliegues — un sesgo optimista pequeño pero real. La solución es dos bucles: el bucle externo produce k puntajes de prueba honestos, cada uno calculado sobre un modelo cuyos hiperparámetros se seleccionaron utilizando únicamente los datos de entrenamiento internos correspondientes.
from sklearn.model_selection import KFold, GridSearchCV, cross_val_score
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC
from sklearn.datasets import load_breast_cancer
X, y = load_breast_cancer(return_X_y=True)
inner = KFold(n_splits=3, shuffle=True, random_state=0)
outer = KFold(n_splits=5, shuffle=True, random_state=1)
pipe = make_pipeline(StandardScaler(), SVC())
grid = {"svc__C": [0.01, 0.1, 1, 10], "svc__gamma": ["scale", "auto"]}
tuner = GridSearchCV(pipe, grid, cv=inner, scoring="roc_auc", n_jobs=-1)
nested_scores = cross_val_score(tuner, X, y, cv=outer, scoring="roc_auc")
print("Nested CV AUC:", nested_scores.mean().round(3), "±", nested_scores.std().round(3))
# Nested CV AUC: 0.989 ± 0.006
# Naive single-loop version for comparison
naive = cross_val_score(tuner, X, y, cv=inner, scoring="roc_auc")
print("Single-loop AUC:", naive.mean().round(3), "±", naive.std().round(3))
# Single-loop AUC: 0.992 ± 0.008 -- slightly optimistic
La diferencia de 0.003 puntos en este conjunto de datos limpio es pequeña; en datos ruidosos con una cuadrícula amplia puede ser de 1–2 puntos de AUC, lo cual marca la diferencia entre “superamos la línea base” y “no la superamos”. El valor de un solo bucle es lo que obtienes si escribes el código ingenuo; el valor anidado es el que debes reportar.
Walk-forward en una serie casi real
import numpy as np
import pandas as pd
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.metrics import mean_squared_error
# 60 months of seasonal + trend + noise
t = np.arange(60)
y = 0.1*t + 5*np.sin(2*np.pi*t/12) + np.random.default_rng(0).normal(0, 1, 60)
df = pd.DataFrame({"y": y, "t": t, "month": (t % 12) + 1})
model = GradientBoostingRegressor(random_state=0)
scores, preds = [], []
horizon = 3
for start in range(24, len(df) - horizon, horizon):
train = df.iloc[:start]
test = df.iloc[start:start+horizon]
model.fit(train[["t", "month"]], train["y"])
p = model.predict(test[["t", "month"]])
scores.append(mean_squared_error(test["y"], p))
print("Walk-forward MSE:", np.mean(scores).round(3))
# Walk-forward MSE: 1.247
Los primeros 24 meses son el burn-in; a partir de ahí la ventana de entrenamiento solo crece. Este es el esquema de rolling-origin de Bergmeir & Hyndman (2014). Las variantes incluyen una ventana móvil (descartar datos antiguos a medida que se avanza), que es mejor para series con deriva, y una ventana purgada (descartar las h observaciones inmediatamente antes del bloque de prueba para evitar fugas directas provenientes de vecinos correlacionados).
Casos límite y errores comunes
Error 1: barajar una serie temporal. Ya se cubrió anteriormente; la métrica se verá excelente y el modelo será inútil. Síntoma: AUC de entrenamiento ≈ AUC de CV ≫ AUC de prueba.
Error 2: ajustar el escalador / imputador / codificador fuera del pipeline. Usa make_pipeline o Pipeline. Cualquier cosa calculada sobre todas las filas — incluidas las cargas de PCA, las codificaciones del objetivo, los conjuntos de vocabulario y los centros de clúster de kmeans usados como características — es un vector de fuga de datos.
Error 3: ajustar los hiperparámetros en el pliegue de prueba. Si llamas a GridSearchCV y luego a cross_val_score sobre los mismos datos con los mismos pliegues, la segunda llamada es una reevaluación de los hiperparámetros ya ajustados en las mismas particiones. No es una validación cruzada anidada; simplemente es ejecutar la misma validación cruzada dos veces. Envuelve todo el GridSearchCV dentro del cross_val_score externo.
Error 4: reportar la puntuación del bucle interno de la validación cruzada anidada. El promedio interno es un artefacto del ajuste; es el mejor alcanzable en los pliegues internos, sesgado al alza. Solo las puntuaciones externas son honestas.
Error 5: elegir k observando la puntuación de la validación cruzada. “La validación con 5 pliegues dio 0.91, la de 10 pliegues dio 0.92, usaré 10.” La diferencia es ruido de muestreo, no una señal real. Elige k basándote en el trade-off sesgo-varianza, no en la puntuación que produce.
Error 6: ignorar la desviación estándar. Una media de 0.90 ± 0.02 y una media de 0.90 ± 0.10 son modelos muy diferentes. Reporta ambas, o reporta un intervalo de confianza. Con solo 5 pliegues, la propia desviación estándar tiene una alta incertidumbre; RepeatedStratifiedKFold te da un muestreo más denso de la varianza.
Error 7: GroupKFold sin pasar groups=. El divisor lanza una excepción en el momento del ajuste. Pasa groups= a cross_val_score, no al constructor del divisor.
Error 8: estratificar con la variable equivocada. StratifiedKFold estratifica sobre y. Si tu clase rara está en una característica (p. ej., el grupo minoritario de un atributo sensible), necesitas un divisor personalizado o StratifiedGroupKFold con claves combinadas.
Error 9: usar LOO en clasificación con una función de decisión dura. Los conjuntos de prueba de LOO tienen N=1, por lo que la precisión es 0 o 1 por pliegue. La desviación estándar parece alarmante, pero es solo ruido Bernoulli; la media sigue siendo interpretable, pero las comparaciones pareadas se vuelven ruidosas. Prefiere LOO para regresión o para métricas probabilísticas estilo neg_log_loss, donde la puntuación por pliegue es continua.
Error 10: usar la validación cruzada como el número final. La validación cruzada es una estimación de la generalización. El número final implementado es el del conjunto de prueba, ejecutado una vez, con datos que el modelo nunca ha visto, e idealmente recopilados después de que el modelo se haya congelado. La validación cruzada por sí sola no te protege del cambio de distribución entre el entrenamiento y la producción; para eso sirven el conjunto de prueba reservado y el monitoreo continuo.
Error 11: validación cruzada de series temporales con ventanas de prueba superpuestas. Si no purgas el espacio entre entrenamiento y prueba en el esquema walk-forward, las características lag-1 producen fuga de datos. Para un horizonte h, descarta las h observaciones inmediatamente anteriores al bloque de prueba del entrenamiento.
Error 12: usar cv=5 (int) en cross_val_score para clasificación. sklearn despacha automáticamente a StratifiedKFold para clasificadores y a KFold para regresores — pero solo si pasas un entero. Si pasas cv=KFold(5) obtienes pliegues simples (no estratificados) incluso en clasificación, lo que puede producir un pliegue con cero ejemplos positivos.
Referencias cruzadas
- ¿Qué es la validación cruzada y cómo evitar hacerla mal? — el artículo fundacional; esta referencia es su catálogo complementario.
- Validación cruzada anidada: cómo afinar tu modelo sin engañarte a ti mismo — un análisis profundo del patrón de dos bucles.
- Validación cruzada en series temporales: por qué k-fold falla con datos temporales — las variantes walk-forward y de ventana móvil en detalle.
- Por qué tu modelo se ve excelente en el entrenamiento pero falla en producción — el panorama general sobre la fuga de datos, incluyendo la fuga por grupos y la distinción entrenamiento/validación/prueba.
- Curvas de calibración: cuándo las probabilidades de tu modelo son incorrectas — qué hacer después de que CV indique que tu modelo está bien, pero las probabilidades aún no.
Lecturas recomendadas
- Stone, M. (1974). Cross-validatory choice and assessment of statistical predictions. Journal of the Royal Statistical Society, Series B, 36(2), 111–147. La formulación original de LOO/CV.
- Kohavi, R. (1995). A study of cross-validation and bootstrap for accuracy estimation and model selection. IJCAI. La comparación experimental clásica de k-fold, leave-one-out y bootstrap; argumenta a favor del 10-fold estratificado.
- Bergmeir, C., & Hyndman, R. J. (2014). Note on the invalidity of cross-validation for evaluating autoregressive time series prediction. Computational Statistics & Data Analysis. Por qué el CV ordinario es sesgado para series de tiempo y qué usar en su lugar.
- Kaggle: Porto Seguro’s Safe Driver Prediction — una competencia canónica de datos tabulares desbalanceados donde la divergencia en el leaderboard público entre un CV simple y uno estratificado es el tema central. Compara tu CV estratificado local con un 5-fold simple y observa cómo cambia el ranking.
- Guía de usuario de scikit-learn, Validación cruzada: evaluación del rendimiento de un estimador y Ajuste de los hiperparámetros de un estimador — la referencia canónica de la API para
KFold,StratifiedKFold,GroupKFold,TimeSeriesSplity el patrón de CV anidado.
Esta traducción fue generada automáticamente y puede contener errores. Si el idioma inglés es tu preferencia, puedes leer el artículo original en inglés .
Artículos relacionados
- Aprendizaje Automático En revisión
Referencia: Regularización
Una referencia completa que cubre L1, L2, dropout, BatchNorm, parada temprana y aumento de datos: cuándo usar cada técnica, con código de ejemplo en Python.
- Aprendizaje Automático En revisión
¿Qué es la validación cruzada y cómo evitar hacerla mal?
Aprende a hacer validación cruzada de la manera correcta: evita el sobreajuste, previene la fuga de datos con pipelines, lee la desviación estándar y maneja las series de tiempo correctamente.
- Aprendizaje Automático En revisión
Validación cruzada anidada: por qué tu puntaje de validación te miente
El ajuste de hiperparámetros infla los puntajes de validación debido al sesgo de optimización: aprende cómo la validación cruzada anidada con Optuna te ofrece estimaciones honestas.
- Aprendizaje Automático En revisión
Validación cruzada anidada: cómo ajustar tu modelo sin engañarte a ti mismo
Aprende cómo la validación cruzada anidada evita el sesgo optimista al ajustar los hiperparámetros, brindándote una estimación honesta del rendimiento del modelo antes de enviarlo a producción.
¿Buscas otra cosa?
Busca en todos los artículos por título, resumen o tema.