11 · Aprender factores con SGD (Funk SVD)
El SVD sobre datos imputados está sesgado por el valor de relleno. Funk SVD lo arregla: aprende P y Q directamente sobre las valoraciones OBSERVADAS con SGD, regularizado, más sesgos de usuario/ítem.
No imputes. Aprende P y Q minimizando el error cuadrático regularizado solo sobre las entradas OBSERVADAS, con SGD: para cada valoración conocida, predice p·q, toma el error, y empuja p y q en la dirección del gradiente. Añade sesgos de usuario/ítem para la parte de una valoración que es solo 'este usuario valora alto' o 'este ítem es querido'.
Sin esto:
Este es el caballo de batalla del recsys de feedback explícito — el post de blog de Simon Funk que reformó el Netflix Prize. Escala a cientos de millones de valoraciones porque cada paso de SGD toca solo un vector de usuario y uno de ítem.
El SVD truncado tenía una falla real: debe rellenar las entradas faltantes primero, y luego trata esos valores inventados como verdad. Con una matriz 99% dispersa, la descomposición ajusta sobre todo tu imputación, no valoraciones reales.
Funk SVD (Simon Funk, 2006) lo arregla con una idea bellamente simple: mira solo las valoraciones que realmente observaste. Define la pérdida sobre el conjunto observado K:
L = Σ_(u,i)∈K ( r_ui − p_u·q_i )² + λ ( ‖p_u‖² + ‖q_i‖² )
El segundo término es regularización L2 — mantiene los factores pequeños para que no sobreajusten a usuarios/ítems con pocas valoraciones. Minimizamos L con descenso de gradiente estocástico: baraja las valoraciones observadas, y para cada (u,i,r):
err = r − p_u · q_i
p_u += lr · ( err · q_i − λ · p_u )
q_i += lr · ( err · p_u − λ · q_i )
Eso es todo — dos actualizaciones de vectores por valoración observada. Crucialmente, las celdas faltantes no contribuyen nada a la pérdida, así que no hay sesgo de imputación.
Los modelos reales añaden sesgos: r_ui ≈ μ + b_u + b_i + p_u·q_i. Aquí μ es la media global, b_u captura 'Ana valora todo alto', y b_i captura 'esta película es querida por todos'. Los factores solo tienen que modelar la interacción — la parte personalizada que queda tras los promedios fáciles.
Un optimizador alternativo es ALS (Mínimos Cuadrados Alternados): fija Q, resuelve cada fila de P en forma cerrada (una regresión ridge), luego fija P y resuelve Q, y repite. ALS paraleliza de maravilla y es el predeterminado para feedback implícito a escala (la MLlib de Spark lo usa). SGD es más simple y brilla para valoraciones explícitas.
Abajo implementamos Funk SVD desde cero y vemos caer el RMSE de entrenamiento época a época.
Python (in browser)
Funk SVD desde cero: SGD solo sobre valoraciones observadas, con regularización L2 y sesgos de usuario/ítem. Observa caer el RMSE de entrenamiento cada época.
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
El mismo modelo vía la clase SVD de la librería Surprise (explícito) y una nota sobre ALS implícito (lectura guiada; no se ejecuta aquí).
¿Cuál es la ventaja clave de Funk SVD (SGD sobre entradas observadas) frente a ejecutar SVD plano sobre una matriz imputada con la media?
- Funk SVD minimiza el error cuadrático regularizado solo sobre entradas OBSERVADAS — sin imputación, así que sin sesgo del valor de relleno.
- Cada paso de SGD: err = r − p·q, luego empuja p y q (y los sesgos) por lr·(err·otro − λ·self). Dos actualizaciones de vector baratas por valoración.
- Añade media global + sesgos de usuario/ítem (μ + b_u + b_i + p·q); ALS es la alternativa alternada en forma cerrada, predeterminada para feedback implícito a escala.
Funk SVD con sesgos es el recomendador canónico de feedback explícito (la clase SVD de la librería Surprise); ALS impulsa Spark MLlib y la librería implicit usada en la industria.
Si lo quitas: Sin aprender factores sobre entradas observadas, te quedas con SVD sesgado por imputación o CF basado en memoria que no escala — ninguno competitivo sobre datos dispersos reales.