Dimensionality Reduction

Otoño 2025

Dimensionality Reduction

¿Qué es Dimensionality Reduction?

Dimensionality Reduction

¿Qué es Dimensionality Reduction?

Dimensionality Reduction es el proceso de reducir el número de variables aleatorias bajo consideración, obteniendo un conjunto de variables principales.

Dimensionality Reduction

¿Qué es Dimensionality Reduction?

Dimensionality Reduction es el proceso de reducir el número de variables aleatorias bajo consideración, obteniendo un conjunto de variables principales. Ayuda en:

  • Visualization de datos de alta dimensionalidad
  • Reducir el costo computacional
  • Eliminar ruido y redundancia

Métodos

  • Principal Component Analysis (PCA)
  • Multidimensional Scaling (MDS)
  • Sparse Random Projection (SRP)
  • Locally Linear Embedding (LLE)
  • t-Distributed Stochastic Neighbor Embedding (t-SNE)
  • Uniform Manifold Approximation and Projection (UMAP)

PCA

PCA

Idea central:

Se basa en la maximización de la varianza y la eigen decomposition.

PCA

Idea central:

Se basa en la maximización de la varianza y la eigen decomposition.

Propósito:

Encontrar un conjunto de ejes ortogonales (componentes principales) que capturen la máxima varianza en los datos.

PCA

Pasos:

  • Centrar los datos.
  • Calcular la matriz de covarianza: \(\sum = \frac{1}{n} X^T X\)
  • Calcular los eigenvectors y eigenvalues: \(\sum v_i = \lambda_i v_i\)
  • Proyectar los datos sobre el nuevo espacio: \(Z = X W_k\) donde \(W_k\) son los top-k eigenvectors.

Ejemplo en MNIST

MNIST Dataset

  • Dataset de imágenes de 28x28 píxeles de dígitos escritos a mano

MNIST Dataset

  • Cada imagen de MNIST puede pensarse como un arreglo de 28x28 números que describen qué tan oscuro es cada píxel:
  • Podemos aplanar cada arreglo en un vector de 28 * 28 = 784 dimensiones, donde cada componente del vector es un valor entre cero y uno que describe la intensidad del píxel
  • Podemos pensar en MNIST como una colección de vectores de 784 dimensiones

MNIST Dataset

  • ¡Pero no todos los vectores en este espacio de 784 dimensiones son dígitos de MNIST! Los puntos típicos en este espacio son muy diferentes
  • Para tener una idea de cómo luce un punto típico, podemos elegir al azar algunas imágenes de 28x28 – cada píxel es aleatoriamente blanco, negro o algún tono de gris. Estos puntos aleatorios se ven como ruido:

MNIST Dataset

  • Las imágenes de 28x28 que se parecen a dígitos de MNIST son muy raras - conforman un subespacio muy pequeño del espacio de 784 dimensiones
    • Con algunos argumentos un poco más complejos, podemos ver que ocupan un subespacio de dimensión menor (que 748)
  • Muchas teorías sobre la estructura de menor dimensión de MNIST (y datos similares)
    • Hipótesis del manifold (popular entre investigadores de ML): MNIST es un manifold de baja dimensión que se curva a través de su embedding space de alta dimensión
    • Otra hipótesis (con raíces en topological data analysis) es que datos como MNIST consisten en blobs con protuberancias tipo tentáculo que sobresalen hacia el espacio circundante
    • ¡Pero nadie lo sabe con certeza!

MNIST Cube

  • Imagina los puntos de datos de MNIST como puntos suspendidos en un cubo de 784 dimensiones
    • Cada dimensión del cubo corresponde a un píxel en particular
    • Los puntos de datos van de cero a uno según la intensidad del píxel
    • De un lado de la dimensión hay imágenes donde ese píxel es blanco. Del otro lado de la dimensión hay imágenes donde es negro. En el medio, hay imágenes donde es gris.
  • ¿Cómo se ve este cubo si miramos una cara bidimensional en particular? Veamos las visualizations de Olah.

MNIST Cube

  • ¿Qué cualidades tendría la visualization ‘perfecta’ de MNIST? ¿Cuál debería ser nuestro objetivo?
  • ¿Cuál es la mejor forma de hacer clustering de los datos de MNIST?
  • Podemos intentar lo que aprendimos la semana pasada…

PCA on MNIST

Importar librerías:

import numpy as np
import matplotlib.pyplot as plt
import sklearn
from sklearn.datasets import load_digits
from sklearn.decomposition import TruncatedSVD
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.ensemble import RandomTreesEmbedding
from sklearn.manifold import (Isomap, LocallyLinearEmbedding, MDS, SpectralEmbedding, TSNE)
from sklearn.neighbors import NeighborhoodComponentsAnalysis
from sklearn.pipeline import make_pipeline
from sklearn.random_projection import SparseRandomProjection
from sklearn.decomposition import PCA
import plotly.graph_objects as go
from umap import UMAP

PCA on MNIST

Definir funciones auxiliares para visualizar clusters en 2D y 3D:

# Authors: Gael Varoquaux
# License: BSD 3 clause (C) INRIA 2014
def plot_clustering(X_red, labels, title=None):
    x_min, x_max = np.min(X_red, axis=0), np.max(X_red, axis=0)
    X_red = (X_red - x_min) / (x_max - x_min)

    plt.figure(figsize=(6, 6))
    for digit in digits.target_names:
        plt.scatter(
            *X_red[y == digit].T,
            marker=f"${digit}$",
            s=50,
            # c=plt.cm.nipy_spectral(labels[y == digit] / 10),
            alpha=0.5,
        )

    plt.xticks([])
    plt.yticks([])
    if title is not None:
        plt.title(title, size=17)
    plt.axis("off")
    plt.tight_layout(rect=[0, 0.03, 1, 0.95])

PCA on MNIST

Definir funciones auxiliares para visualizar clusters en 2D y 3D:

def plot_3d_clustering(X_red, labels, title=None):
    component0 = X_red[:, 0]
    component1 = X_red[:, 1]
    component2 = X_red[:, 2]
    indices =  list(range(X_red.shape[0]))
    labels = list (map(lambda x: "label:" + str(y[x]) + ", id:" + str(x),indices))

    fig = go.Figure(data=[go.Scatter3d(
        x=component0,
        y=component1,
        z=component2,
        mode='markers',
        marker=dict(
            size=10,
            color=y,                # set color to an array/list of desired values
            colorscale='Rainbow',   # choose a colorscale
            opacity=1,
            line_width=1,
        ),
        text = labels

    )])
# tight layout
    fig.update_layout(margin=dict(l=50,r=50,b=50,t=50),width=520,height=520)
    fig.layout.template = 'plotly_dark'

    fig.show()

PCA on MNIST

Cargar el dataset de MNIST:

digits = load_digits(n_class=6)
X, y = digits.data, digits.target
n_samples, n_features = X.shape
n_neighbors = 30

print("Muestras: " + str(n_samples))
print("Características: " + str(n_features))
Muestras: 1083
Características: 64

PCA on MNIST

Realizar PCA:

pca = PCA(n_components=2)
pca.fit(X)
X_trans = pca.transform(X)
print("Valores singulares: " + str(pca.singular_values_))
Valores singulares: [503.75922595 466.04224081]

PCA on MNIST

    Vemos que podemos agrupar razonablemente los datos por dígito usando PCA:
plt.scatter(X_trans[:,0],X_trans[:,1])

PCA on MNIST

    Vemos que podemos agrupar razonablemente los datos por dígito usando PCA:
plot_clustering(X_trans, "", "PCA")

PCA on MNIST

    También podemos visualizar esto en 3D:
pca_3d = PCA(n_components=3)
X_trans_3d = pca_3d.fit_transform(X)
plot_3d_clustering(X_trans_3d, "", "PCA")

MDS

MDS

También se conoce como Principal Coordinates Analysis (PCoA).

MDS

También se conoce como Principal Coordinates Analysis (PCoA).

Idea central:

Se enfoca en preservar distancias usando eigenvalue decomposition de una matriz de similitud.

MDS

También se conoce como Principal Coordinates Analysis (PCoA).

Idea central:

Se enfoca en preservar distancias usando eigenvalue decomposition de una matriz de similitud.

Propósito:

Preservar las distancias por pares entre los puntos de datos en el embedding de baja dimensión.

MDS

Pasos:

  • Calcular la matriz de proximidad al cuadrado: \(D^2 = [d^2_{ij}]\)
  • Doble-centrar la matriz de distancias para obtener una matriz de similitud: \(B = - \frac{1}{2} C D^2 C\) Donde \(C=I-\frac{1}{n} J_n\) es la matriz de centrado y \(J_n = 11^T\) es la matriz de todos unos.
  • Realizar eigen decomposition: \(B = V \Sigma V^T\)
  • Usar los top eigenvectors como coordenadas de baja dimensión: \(X = V_k \Sigma_k ^{1/2}\)

MDS on MNIST

mds = MDS(n_components=2, n_init=1, max_iter=120, n_jobs=2)
X_mds = mds.fit_transform(X)
plot_clustering(X_mds, "", "MDS")

MDS on MNIST

mds_3d = MDS(n_components=3, n_init=1, max_iter=120, n_jobs=2)
mds_3d.fit(X)
X_mds_3d = mds_3d.fit_transform(X)
plot_3d_clustering(X_mds_3d, "", "MDS")

SRP

SRP

Idea central:

Proyecta usando matrices sparse random, preservando distancias.

SRP

Idea central:

Proyecta usando matrices sparse random, preservando distancias.

Propósito:

Reducir la dimensionalidad mientras se preservan aproximadamente las distancias por pares, usando matrices sparse random.

SRP

Pasos:

  • Generar una matriz sparse random: \(\mathcal{R}_{ij} = \sqrt{s} \times \begin{cases} +1, & \text{con probabilidad } \frac{1}{2s} \\ -1, & \text{con probabilidad } \frac{1}{2s} \\ 0, & \text{con probabilidad } 1 - \frac{1}{s} \\ \end{cases}\) Donde \(s\) es el parámetro de sparsity.
  • Proyecta \(Z = X\mathcal{R}\)

SRP on MNIST

srp=SparseRandomProjection(n_components=2, random_state=42)
X_srp=srp.fit_transform(X)
plot_clustering(X_srp, "", "Sparse Random Projection")

SRP on MNIST

srp_3d=SparseRandomProjection(n_components=3, random_state=42)
X_srp_3d=srp_3d.fit(X)
X_srp_3d=srp_3d.fit_transform(X)
plot_3d_clustering(X_srp_3d, "", "Sparse Random Projection")

LLE

LLE

Idea central:

Usa pesos de reconstrucción lineal local.

LLE

Idea central:

Usa pesos de reconstrucción lineal local.

Propósito:

Preservar las relaciones de vecindad local mediante reconstrucción lineal.

LLE

Pasos:

  • Identificar los k-nearest neighbors de cada punto: Minimizar \(\sum_i || x_i - \sum _{j \in N(i) w_{ij} x_j } ||^2\)
  • Calcular los pesos que reconstruyen cada punto a partir de sus vecinos.
  • Encontrar embeddings de baja dimensión que preserven estos pesos: \(\Phi(Y) = \sum_i ||y_i - \sum _{j \in N(i) w_{ij} y_j } ||^2\)

LLE on MNIST

lle=LocallyLinearEmbedding(n_neighbors=5, n_components=2, method="standard")
X_lle=lle.fit(X)
X_lle=lle.fit_transform(X)
plot_clustering(X_lle, "", "LLE")

LLE on MNIST

lle_3d=LocallyLinearEmbedding(n_neighbors=n_neighbors, n_components=3, method="standard")
X_lle_3d=lle_3d.fit(X)
X_lle_3d=lle_3d.fit_transform(X)
plot_3d_clustering(X_lle_3d, "", "LLE")

t-SNE

t-SNE

Idea central:

Optimiza la divergencia KL entre similitudes de alta y baja dimensión.

t-SNE

Idea central:

Optimiza la divergencia KL entre similitudes de alta y baja dimensión.

Propósito:

Preservar la estructura local de los datos con base en probabilidades de similitud.

t-SNE

Pasos:

  • Convertir las distancias de alta dimensión en probabilidades conjuntas: \(p_{ij} = \frac{p_{j|i} + p_{i|j}}{2n}\) Donde \(p_{j|i} = \frac{ exp( -|| x_i - x_j||^2 / 2 \sigma_i ^2 ) }{\sum_{k\neq i} exp( -|| x_i - x_k||^2 / 2 \sigma_i ^2 ) }\)
  • Definir las similitudes de baja dimensión: \(q_{ij} = \frac{(1+|| y_i - y_j ||^2)^{-1}}{ \sum_{k\neq l} (1+|| y_k - y_l ||^2)^{-1} }\)
  • Minimizar la divergencia de Kullback-Leibler entre \(p_{ij}\) y \(q_{ij}\): \(KL = \sum _{i \neq j} p_{ij} log(\frac{p_{ij}}{q_{ij}})\)

t-SNE on MNIST

tsne = sklearn.manifold.TSNE()
X_tsne = tsne.fit_transform(X)
plot_clustering(X_tsne, "", "T-SNE")

t-SNE on MNIST

tsne_3d = sklearn.manifold.TSNE(3)
X_tsne_3d = tsne_3d.fit_transform(X)
plot_3d_clustering(X_tsne_3d, "", "T-SNE")

Usando t-SNE Efectivamente

  • Los gráficos de t-SNE son populares y pueden ser útiles, pero solo si evitas malinterpretaciones comunes
    • Cuidado con los valores de los hyperparameters
    • Recuerda que el tamaño de los clusters en un gráfico de t-SNE no significa nada
    • Ten en cuenta que las distancias ENTRE clusters tampoco tienen por qué significar algo
    • El ruido aleatorio no siempre luce aleatorio
    • Puede que necesites múltiples gráficos para entender la topología
  • Ver Wattenberg, Viégas, y Johnson (2016) para más detalles

UMAP

UMAP

Idea central:

Usa manifold learning y minimiza una cross-entropy loss entre estructuras topológicas difusas.

UMAP

Idea central:

Usa manifold learning y minimiza una cross-entropy loss entre estructuras topológicas difusas.

Propósito:

Preservar tanto la estructura local como la global usando manifold learning.

UMAP

Pasos:

  • Construir un complejo simplicial difuso en el espacio de alta dimensión \(p_{ij}\): \(p_{ij} = exp(\frac{d(x_i, x_j) - \rho_i}{\sigma_i})\)
  • Construir un complejo similar en el espacio de baja dimensión \(q_{ij}\): \(q_{ij} = \frac{1}{ 1+\alpha || y_i - y_j ||^{2\beta} }\)
  • Minimizar la cross-entropy entre \(p_{ij}\) y \(q_{ij}\): \(CE = \sum _{(i, j)} p_{ij} log(\frac{p_{ij}}{q_{ij}}) + (1 - p_{ij}) log( \frac{1-p_{ij}}{1-q_{ij}} )\)

UMAP on MNIST

ump=UMAP()
ump.fit(X)
X_umap=ump.fit_transform(X)
plot_clustering(X_umap, "not used", "UMAP")

UMAP on MNIST

ump_3d=UMAP(n_components=3)
ump_3d.fit(X)
X_umap_3d = ump_3d.fit_transform(X)
plot_3d_clustering(X_umap_3d, "", "UMAP")

Resumen

Referencias (Recomendadas para Profundizar)

Olah, Christopher. 2014. «Visualizing Mnist: An exploration of dimensionality reduction». Visualizing MNIST: An Exploration of Dimensionality Reduction - colah’s blog. https://colah.github.io/posts/2014-10-Visualizing-MNIST/.
Wattenberg, Martin, Fernanda Viégas, y Ian Johnson. 2016. «How to use T-Sne effectively». Distill. https://distill.pub/2016/misread-tsne/.