Ir al contenido
wannacode

Validación cruzada

En resumen: La validación cruzada divide los datos en varios pliegues y entrena y comprueba el modelo muchas veces; da una puntuación media con su dispersión en lugar de un único número con suerte.

Problema

«¿El árbol o la recta?»

La tienda quiere saber cuántos pedidos hará cada cliente en los próximos 4 meses. Con eso decide a quién mandar una tarjeta de descuento y cuánta mercancía pedir. Las características salen de los pedidos anteriores al 1 de julio de 2026, como en la lección sobre clasificación: número de pedidos, días desde el último, cuánto tiempo lleva comprando y el pedido medio. El objetivo es el número de pedidos del 1 de julio al 31 de octubre.

Tú dices «regresión lineal», y tu colega dice «un árbol de 3 niveles». Los ejecutas en 6 divisiones distintas en entrenamiento y prueba (random_state de 0 a 5) y mides el MAE en pedidos por cliente:

Pythondatos: las tablas de la tienda

Hay 1279 clientes, y hacen de media 2,70 pedidos en los 4 meses. Con las divisiones 0, 1, 2 y 4 el árbol se equivoca menos, y con la 3 y la 5 la recta. Si hubieras ejecutado solo la división 2, tu colega habría ganado con 1,25 frente a 1,31. Con la división 5 ganas tú. ¿Quién tiene razón?

La idea

Una comprobación es una muestra

Los datos de prueba son 256 clientes, elegidos al azar. Como la media de 30 pedidos en la lección sobre muestras, el error en 256 clientes también tiene ruido: otra muestra da otro número. El error de la recta salta de 1,31 a 1,43 solo por qué clientes cayeron en la prueba.

La diferencia entre los dos modelos son unas centésimas. El ruido de la división está en torno a diez centésimas. Con una comprobación no puedes decir cuál es mejor, igual que no puedes saber el pedido medio con 30 pedidos.

1. Con la división 0 el árbol se equivoca en 1,31 pedidos y la recta en 1,35. ¿Qué se deduce?

La idea

Validación cruzada

La solución es comprobar muchas veces, con cada cliente exactamente una vez en la prueba. Divides los clientes en 5 pliegues (folds). Entrenas con 4 y mides en el quinto, y luego cambias cuál es la prueba. Obtienes 5 errores para cada modelo. Esto se llama validación cruzada.

KFold divide en pliegues, y cross_val_score entrena y mide el modelo en cada uno. Con shuffle=True los clientes se mezclan antes de dividir:

Pythondatos: las tablas de la tienda

Cuatro veces aprendes con 1023 clientes y compruebas con 256, una vez con 1024 y 255. scikit-learn siempre busca el número más grande, por eso el error viene con un menos (neg_mean_absolute_error), y el menos de delante lo devuelve a su sitio.

La línea base (la media, DummyRegressor) se equivoca en 2,70 ± 0,09. La recta se equivoca en 1,34 ± 0,06, y el árbol en 1,38 ± 0,09. El número después del ± es la dispersión de los cinco errores (std). Los dos modelos superan con claridad a la línea base, y la diferencia entre ellos (0,04) es menor que la dispersión. La conclusión honesta es «la recta es un poco mejor, pero no con claridad». Cuando dos modelos están tan cerca, te quedas con el más simple.

Más precisamente, para curiosos

Los dos modelos se comparan con los mismos pliegues: por eso kf es un solo objeto, con un random_state. cv=5 también divide en 5, pero sin mezclar: el primer pliegue son los primeros 256 clientes en orden. Si la tabla está ordenada (por número, por la fecha del primer pedido), los pliegues se diferencian entre sí y la puntuación se sesga.

Pruébalo

¿Es mejor el bosque?

Tu colega propone un bosque aleatorio (RandomForestRegressor con 50 árboles, max_depth=5). Compruébalo con validación cruzada sobre los mismos pliegues kf. Guarda en forest_mae y line_mae los cinco errores (MAE, positivos) del bosque y de la recta, en gap la diferencia del error medio (bosque menos recta) y en spread la dispersión de los errores del bosque.

Pythondatos: las tablas de la tienda

La idea

Sobreajuste

¿Por qué el árbol tiene 3 niveles y no 20? Cuanto más profundo, más fino divide a los clientes. Aquí está el error en los clientes de entrenamiento y en los de prueba para profundidades de 1 a 12:

Pythondatos: las tablas de la tienda

En los clientes de entrenamiento el error baja cada vez: de 1,87 a 0,41. En los clientes de prueba baja hasta la profundidad 3 (1,50) y luego crece hasta 1,91. Un árbol sin límite llega a 23 niveles y se equivoca en 0,001 con los clientes de entrenamiento, casi cero, y en 2,00 con los de prueba.

El árbol profundo no ha aprendido más sobre los clientes. Se ha aprendido de memoria a cada cliente de entrenamiento junto con su ruido: un cliente pidió por casualidad más, otro menos. Esto se llama sobreajuste. La señal es una gran diferencia entre el error de entrenamiento y el error de prueba. Por eso el error en los datos con los que aprendió el modelo no dice nada sobre clientes nuevos.

Pruébalo

Elige la profundidad

Elige la profundidad del árbol con validación cruzada. Guarda en cv_mae (una Series con la profundidad de 1 a 10 como índice) el MAE medio de cross_val_score con los pliegues kf, y en best_depth la profundidad con el menor error.

Pythondatos: las tablas de la tienda

La idea

Cuando el orden es el tiempo

A los clientes se les puede mezclar: un cliente no sabe nada de otro. A los días no. Si predices los pedidos de mañana, el modelo solo tendrá el pasado. Los pliegues mezclados también lo entrenarían con días posteriores a los de prueba, y eso es la fuga de datos de la lección anterior.

TimeSeriesSplit divide por tiempo: aprende con el comienzo y comprueba con los días siguientes, luego aprende con más y comprueba con otros aún posteriores. Un ejemplo: los pedidos de un día a partir del día de la semana, los pedidos de hace 7 días y la media de los 7 días anteriores:

Pythondatos: las tablas de la tienda

Los primeros 7 días no tienen «hace 7 días» y se descartan, quedan 358. Cada pliegue de prueba son 59 días después de la parte de entrenamiento, y la parte de entrenamiento crece: 2 meses al principio, 10 al final. La recta se equivoca de media en 4,51 ± 0,18 pedidos por día.

Pruébalo

¿Supera a la línea base?

Compara la recta con la línea base (DummyRegressor, la media de los días de entrenamiento) con TimeSeriesSplit de 5 pliegues. Guarda en line_mae y base_mae los cinco errores (MAE, positivos) de cada una.

Pythondatos: las tablas de la tienda

La idea

La prueba se toca una sola vez

Si pruebas 10 modelos con los datos de prueba y te quedas con el mejor, la prueba participó en la elección. Uno de los diez tendrá suerte justo con estos clientes, y su error parecerá mejor de lo que es.

Por eso el orden es:

  1. Apartas una parte de prueba al comienzo y no la miras.
  2. Eliges el modelo y sus ajustes (profundidad, características) con validación cruzada solo sobre la parte de entrenamiento.
  3. Entrenas el modelo elegido con toda la parte de entrenamiento y lo mides una vez en la prueba. Ese es el número que le dices a la jefa.
Pythondatos: las tablas de la tienda

Con los 1023 clientes de entrenamiento la línea base se equivoca en 2,74 ± 0,17, la recta en 1,32 ± 0,06, y el árbol en 1,36 ± 0,07. La recta tiene el menor error y es más simple: la eliges. Los 256 clientes de prueba siguen intactos.

Más precisamente, para curiosos

Si después de la prueba cambias el modelo y mides otra vez en la prueba, la prueba pasa a ser parte de la elección. Entonces necesitas una nueva parte de prueba: datos nuevos, por ejemplo el mes siguiente. Por eso, para un modelo de pedidos, la comprobación más honesta es el tiempo posterior al entrenamiento, como en TimeSeriesSplit.

La idea

Cuándo no hace falta un modelo

Un modelo cuesta tiempo: datos, entrenamiento, vigilar que no se estropee. A veces no vale la pena:

  • Pocos datos. Con unas pocas decenas de clientes el error de la validación cruzada salta tanto que no puedes decir si el modelo supera a la media.
  • Una regla sin modelo hace lo mismo. «El cliente comprará al mismo ritmo» es una división. Si el modelo no la supera con claridad, la regla es mejor: todos la entienden y nada puede romperse.
  • El coste de un error. Si un error es caro (rechazas un crédito, bloqueas una tarjeta), el modelo tiene que estar muy seguro, o una persona tiene que revisar.
  • Nadie actúa según la predicción. Si del número no sale ninguna decisión (quién recibe un cupón, cuánta mercancía), el modelo es solo un ejercicio.

La recta tiene un coeficiente de 0,469 para el número de pedidos hasta ahora, y las demás características apenas participan. Así que es casi «la mitad de los pedidos hasta ahora»: 8 meses antes, 4 meses después. Compruébalo en el ejercicio.

Ejercicio

El veredicto

La validación cruzada sobre la parte de entrenamiento eligió la recta. Entrénala con X_train y compruébala una sola vez en la prueba: guarda en model el modelo entrenado, en test_mae su error (MAE) en los clientes de prueba y en base_mae el error de la línea base en la prueba.

Pythondatos: las tablas de la tienda

¿La recta supera a una regla sin modelo? La regla es «el cliente comprará al mismo ritmo»: para 4 meses, la mitad de sus pedidos de los 8 meses anteriores. Guarda en rule la predicción de la regla para cada cliente de prueba, y en rule_mae su error en la prueba.

Pythondatos: las tablas de la tienda

Test

Ponte a prueba

1. ¿Por qué la validación cruzada da una puntuación más fiable que una sola división?
2. Un árbol sin límite se equivoca en 0,001 pedidos en los clientes de entrenamiento y en 2,00 en los de prueba. ¿Qué significa?
3. Predices los pedidos de un día. ¿Por qué TimeSeriesSplit y no KFold(shuffle=True)?
4. Comprobaste 10 modelos con los datos de prueba y elegiste el mejor. ¿Cuán honesto es su error de prueba?
5. La regla «la mitad de los pedidos hasta ahora» se equivoca en 1,41, y la recta en 1,38. ¿Qué es sensato?

Creado por Ivo Gergov como parte de la campaña de Evo Tech por una educación accesible en programación ·