12 · Evaluación sin fugas
Un recomendador se juzga por usuario según lo que ordena arriba. Dividir celdas aleatorias de la matriz filtra el futuro e ignora el tiempo — en su lugar, aparta la interacción más reciente de cada usuario.
El recsys es un problema de ranking evaluado por usuario, no una regresión sobre celdas aleatorias. Una división aleatoria train/test deja que un modelo entrene con el futuro de un usuario para predecir su pasado — fuga. El protocolo honesto es leave-one-out (apartar la interacción más reciente de cada usuario) o un corte temporal global, entrenando solo con lo anterior.
Sin esto:
Evalúa tu modelo con una división de celdas aleatoria y reportarás un número precioso que se desmorona en producción — mediste un modelo que ya vio la respuesta.
En aprendizaje supervisado barajas filas y divides. El recsys rompe las dos mitades de esa costumbre. Primero, la unidad que te importa no es una valoración individual — es la lista ordenada que cada usuario realmente ve, así que debes evaluar por usuario. Segundo, las interacciones ocurren en el tiempo: un usuario valora hoy lo que valorará mañana solo después de haberlo visto. Ambos hechos prohíben la división ingenua.
Por qué una división de celdas aleatoria filtra. Toma el 20% de las celdas llenas al azar como conjunto de prueba y entrena con el otro 80%. Para un solo usuario, eso dispersa algunas de sus interacciones posteriores al entrenamiento y algunas anteriores a la prueba. El modelo entrena de hecho con el futuro del usuario para predecir su pasado — y la factorización de matrices explota esto encantada: un factor de usuario/ítem ajustado en parte sobre una interacción apartada la predecirá bien, claro. El error reportado es optimista y no sobrevive al contacto con un servicio real, hacia adelante en el tiempo.
Los protocolos honestos:
- Leave-one-out (LOO): para cada usuario, aparta exactamente una interacción — la más reciente — como el único ítem 'relevante' de prueba de ese usuario. Entrena con todo lo demás. La evaluación entonces pregunta: ¿el modelo ordena ese ítem apartado en lo alto entre los ítems que el usuario no había tocado? Es el estándar para métricas de ranking (próxima lección).
- División temporal global: elige un timestamp de corte; todo lo anterior es entrenamiento, todo lo posterior es prueba. La más realista — imita 'entrena el lunes, sirve el martes' — y la única que respeta la causalidad de todos los usuarios a la vez.
Abajo tomamos historiales de interacción por usuario con timestamps, construimos una división leave-one-out (la interacción más nueva de cada usuario va a prueba) y mostramos explícitamente por qué una división aleatoria habría filtrado.
Python (in browser)
Leave-one-out por tiempo: la interacción más reciente de cada usuario se vuelve su único ítem 'relevante' de prueba; todo lo anterior entrena. Una elección aleatoria filtra el futuro para la mayoría de usuarios.
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
El LeaveOneOut de Surprise hace el hold-out por usuario por ti; una división de producción real ordena por la columna de timestamp y corta en un único momento global.
¿Por qué una división de celdas aleatoria (20% de las valoraciones llenas apartadas al azar) es una forma de evaluación con fugas para un recomendador?
- El recsys se evalúa por usuario sobre una lista ordenada — no como una regresión sobre celdas aleatorias de la matriz.
- Las divisiones de celdas aleatorias filtran el futuro al entrenamiento y sobre-reportan la precisión.
- Usa leave-one-out (aparta la interacción más reciente de cada usuario) o un corte temporal global, entrenando solo con el pasado.
Cada benchmark de recsys offline (leave-one-out de MovieLens, RecBole, la evaluación interna de Spotify) usa hold-outs por usuario o temporales para evitar fugas.
Si lo quitas: Sin una división sin fugas, las métricas offline se desacoplan del rendimiento en vivo y despliegas modelos que lucen geniales y sirven mal.