Saltar al contenido

# aprendizaje-automatico-no-supervisado-con-python

Aprendizaje Automático No Supervisado con Python

Volver a la portada del curso
Lectura (Markdown)

SVD: Descomposición de Valores Singulares

Sección 3 · Reducción de Dimensionalidad

SVD: descomposición de valores singulares

PCA resumió las 8 columnas de las sesiones en dos ejes: «cuánto explora» y «cuánto compra». Por dentro, PCA calcula una descomposición de valores singulares (SVD). Conocer la SVD directamente sirve para dos cosas: entender de dónde salen los números de PCA y trabajar con matrices donde PCA no conviene, como una tabla de conteos llena de ceros. En esta lectura la aplicas a una matriz nueva de PyShop: cuántos productos vio cada sesión en cada una de 12 categorías.

La idea: tres matrices

Toda matriz $X$ de $n$ filas y $p$ columnas se puede escribir como el producto de tres matrices:

$$X = U \, \Sigma \, V^\top$$

  • $U$ ($n \times p$): una fila por sesión; dice cuánto tiene cada sesión de cada componente.
  • $\Sigma$: diagonal con los valores singulares $s_1 \ge s_2 \ge \dots \ge 0$; miden el peso de cada componente.
  • $V^\top$ ($p \times p$): una fila por componente; dice qué columnas originales forman cada componente.

Si te quedas con los $k$ valores singulares más grandes y tiras el resto, obtienes la mejor aproximación de $X$ con rango $k$ en el sentido de mínimos cuadrados. Eso es reducir dimensiones.

SVD y PCA son lo mismo sobre datos centrados

Con las 8 columnas escaladas de las sesiones (Xs, media 0 en cada columna) se comprueba la relación:

import numpy as np

U, S, Vt = np.linalg.svd(Xs, full_matrices=False)
print(U.shape, S.shape, Vt.shape)
print(S.round(1))
print((S**2 / (S**2).sum()).round(3))
print(np.allclose(U @ np.diag(S) @ Vt, Xs))
# (1500, 8) (8,) (8, 8)
# [76.3 52.4 40.  25.8 22.3 17.8 13.3 12.9]
# [0.485 0.229 0.134 0.055 0.042 0.026 0.015 0.014]
# True

Los valores singulares al cuadrado, divididos entre su suma, dan exactamente el explained_variance_ratio_ de PCA: 0.485, 0.229, 0.134… Las filas de Vt son las cargas de PCA (salvo el signo). Y el producto U @ diag(S) @ Vt reconstruye Xs: allclose da True. PCA es la SVD de los datos centrados.

TruncatedSVD: temas en una matriz de conteos

generar_vistas() devuelve una matriz de 1500 sesiones × 12 categorías (celulares, laptops, audio, videojuegos, cocina, muebles, decoración, jardín, ropa, calzado, deportes, belleza). El 45.8 % de las celdas son cero: casi nadie mira todo. Para tablas así, scikit-learn ofrece TruncatedSVD, que a diferencia de PCA no centra los datos. Por eso acepta matrices dispersas de SciPy sin convertirlas en densas, y por eso es la herramienta usual para conteos de palabras (en texto se llama análisis semántico latente, LSA).

import pandas as pd
from sklearn.decomposition import TruncatedSVD
V = generar_vistas()              # copiada del notebook
svd = TruncatedSVD(n_components=3, random_state=42)
T = svd.fit_transform(V)
temas = pd.DataFrame(svd.components_, columns=CATEGORIAS)
print(temas[["celulares", "muebles", "ropa", "calzado"]].round(2))
print(svd.singular_values_.round(1))
print(svd.explained_variance_ratio_.round(3))
#    celulares  muebles  ropa  calzado
# 0       0.50     0.25  0.28     0.28
# 1       0.41    -0.22 -0.39    -0.40
# 2      -0.07     0.53 -0.35    -0.34
# [335.4 243.7 211.7]
# [0.245 0.341 0.265]

Cada fila de components_ es un «tema» y se lee por sus signos:

  • Componente 0, volumen general: todas las cargas son positivas (celulares 0.50). Mide cuántos productos vio la sesión, sin importar dónde.
  • Componente 1, tecnología contra el resto: +0.41 en celulares y −0.40 en calzado.
  • Componente 2, hogar contra moda: +0.53 en muebles y −0.35 en ropa.

Aparecen los tres gustos de los visitantes (tecnología, hogar, moda) sin que nadie los haya etiquetado. La matriz T (1500 × 3) da la puntuación de cada sesión en cada tema; la segunda sesión, que vio 11 muebles y 5 artículos de cocina, tiene 9.99 en el componente 2.

Ojo: explained_variance_ratio_ no viene ordenado

La salida dice 0.245, 0.341, 0.265: el primer componente explica menos varianza que el segundo. No es un error. Como TruncatedSVD no centra, el primer componente captura sobre todo la media (el volumen general), que no cuenta como varianza. El orden lo dan singular_values_ (335.4, 243.7, 211.7), que sí bajan. Los tres suman 0.851; con 8 componentes, del cuarto en adelante cada uno explica alrededor de 0.02, así que tres bastan.

Compresión

La matriz original tiene 1500 × 12 = 18,000 números. Con tres componentes se guardan T (1500 × 3) y components_ (3 × 12): 4,536 números. svd.inverse_transform(T) reconstruye la tabla con un error cuadrático medio de 1.18 vistas por celda. Es la misma idea que usan los sistemas de recomendación basados en factorización de matrices: usuarios y productos se describen con unos pocos factores latentes.

Trampas comunes

  • Usar PCA sobre una matriz dispersa grande. Centrarla la vuelve densa; TruncatedSVD evita ese costo.
  • Ordenar los temas por explained_variance_ratio_. En TruncatedSVD el orden lo dan los valores singulares.
  • Interpretar el signo como bueno o malo. El signo de un componente es arbitrario; lo que importa es qué columnas van juntas y cuáles se oponen.
  • Olvidar la semilla. El algoritmo por omisión es aleatorizado; random_state fija el resultado.

El notebook svd-temas-de-interes repite estos pasos con la salida a la vista.

Recursos