Phase 01 - Lesson 10

Redução de Dimensionalidade

This lesson includes a graded coding exercise that runs in your browser, unlocked with lifetime access.

Dados de alta dimensão tem estrutura. Você a encontra olhando do ângulo certo.

Tipo: Build Linguagem: Python Pré-requisitos: Fase 1, Lições 01 (Intuição de Álgebra Linear), 02 (Vetores, Matrizes e Operações), 03 (Autovalores e Autovetores), 06 (Probabilidade e Distribuições) Tempo: ~90 minutos

Objetivos de Aprendizagem

  • Implementar PCA do zero: centralizar os dados, calcular a matriz de covariância, fazer a autodecomposição e projetar
  • Usar a razão de variância explicada é o método do cotovelo para escolher o número de componentes principais
  • Comparar PCA, t-SNE e UMAP para visualizar dígitos do MNIST em 2D e explicar seus tradeoffs
  • Aplicar kernel PCA com um kernel RBF para separar estruturas de dados não-lineares que o PCA padrão não consegue lidar

O Problema

Você tem um conjunto de dados com 784 features por amostra. Talvez sejam valores de pixels de dígitos escritos à mão. Talvez sejam níveis de expressao genica. Talvez sejam sinais de comportamento de usuário. Você não consegue visualizar 784 dimensões. Você não consegue plotá-las. Você nem consegue pensar sobre elas.

Mas a maioria dessas 784 features e redundante. A informação real vive em uma superfície muito menor. Um "7" escrito à mão não precisa de 784 números independentes para ser descrito. Ele precisa de alguns: o ângulo do traco, o comprimento dá barra, o quanto ele se inclina. O resto e ruido.

A redução de dimensionalidade encontra essa superfície menor. Ela pega seus dados de 784 dimensões e os comprime para 2, 10 ou 50 dimensões mantendo a estrutura que importa.

O Conceito

A maldicao dá dimensionalidade

Espaços de alta dimensão são contraintuitivos. Três coisas quebram conforme as dimensões crescem.

A distância perde o sentido. Em altas dimensões, a distância entre dois pontos aleatórios quaisquer converge para o mesmo valor. Se cada ponto está aproximadamente a mesma distância de todos os outros, a busca por vizinhos mais próximos para de funcionar.

Dimension    Avg distance ratio (max/min between random points)
2            ~5.0
10           ~1.8
100          ~1.2
1000         ~1.02

O volume se concentra nos cantos. Um hipercubo unitário em d dimensões tem 2^d cantos. Em 100 dimensões, quase todo o volume está nos cantos, longe do centro. Os pontos de dados se espalham para as bordas e seus modelos passam fome por dados no interior.

Você precisa de exponencialmente mais dados. Para manter a mesma densidade de amostras em um espaço, ir de 2D para 20D significa que você precisa de 10^18 vezes mais dados. Você nunca tem o suficiente. Reduzir dimensões traz a densidade dos dados de volta a algo viável.

PCA: encontre as direções que importam

A Analise de Componentes Principais (PCA) encontra os eixos ao longo dos quais seus dados mais variam. Ela rotaciona seu sistema de coordenadas para que o primeiro eixo capture a maior variância, o segundo capture a próxima maior, e assim por diante.

O algoritmo:

1. Center the data        (subtract the mean from each feature)
2. Compute covariance     (how features move together)
3. Eigendecomposition     (find the principal directions)
4. Sort by eigenvalue     (biggest variance first)
5. Project               (keep top k eigenvectors, drop the rest)

Por que autodecomposição? A matriz de covariância é simétrica e positiva semi-definida. Seus autovetores são direções ortogonais no espaço de features. Os autovalores te dizem quanta variância cada direção captura. O autovetor com o maior autovalor aponta ao longo dá direção de variância máxima.

graph LR
    A["Original data (2D)\nData spread in both\nx and y directions"] -->|"PCA rotation"| B["After PCA\nPC1 captures the elongated spread\nPC2 captures the narrow spread\nDrop PC2 and you lose little info"]
  • Antes do PCA: A nuvem de dados se espalha diagonalmente pelos eixos x e y
  • Depois do PCA: O sistema de coordenadas e rotacionado para que PC1 se alinhe com a direção de variância máxima (espalhamento alongado) e PC2 se alinhe com a direção de variância minima (espalhamento estreito)
  • Redução de dimensionalidade: Descartar PC2 projeta os dados em PC1, perdendo muito pouca informação

Razão de variância explicada

Cada componente principal captura uma fração dá variância total. A razão de variância explicada te diz quanto.

Component    Eigenvalue    Explained ratio    Cumulative
PC1          4.73          0.473              0.473
PC2          2.51          0.251              0.724
PC3          1.12          0.112              0.836
PC4          0.89          0.089              0.925
...

Quando a variância explicada cumulativa atinge 0.95, você sabe que aquele número de componentes captura 95% dá informação. Tudo depois disso e majoritariamente ruido.

Escolhendo o número de componentes

Três estrategias:

  1. Limiar. Mantenha componentes suficientes para explicar 90-95% dá variância.
  2. Método do cotovelo. Plote a variância explicada por componente. Procure por uma queda acentuada.
  3. Desempenho downstream. Use PCA como pre-processamento. Varie k e meca a acurácia do seu modelo. O melhor k e onde a acurácia estabiliza.

t-SNE: preserve vizinhancas

O t-Distributed Stochastic Neighbor Embedding (t-SNE) foi projetado para visualizacao. Ele mapeia dados de alta dimensão para 2D (ou 3D) preservando quais pontos estão próximos uns dos outros.

A intuição: no espaço original, calcule uma distribuição de probabilidade sobre pares de pontos com base em suas distâncias. Pontos próximos recebem alta probabilidade. Pontos distantes recebem baixa probabilidade. Depois encontre um arranjo 2D onde a mesma distribuição de probabilidade se mantém. Pontos que eram vizinhos em 784 dimensões continuam vizinhos em 2D.

Propriedades-chave do t-SNE:

  • Não-linear. Ele consegue desdobrar variedades complexas que o PCA não consegue.
  • Estocástico. Execucoes diferentes produzem layouts diferentes.
  • O parâmetro de perplexidade controla quantos vizinhos considerar (faixa tipica: 5-50).
  • As distâncias entre clusters na saída não têm significado. Apenas os próprios clusters tem.
  • Lento em grandes conjuntos de dados. O(n^2) por padrão.

UMAP: mais rápido, melhor estrutura global

O Uniform Manifold Approximation and Projection (UMAP) funciona de forma similar ao t-SNE mas com duas vantagens:

  • Mais rápido. Ele usa grafos aproximados de vizinhos mais próximos em vez de calcular todas as distâncias pareadas.
  • Melhor estrutura global. As posições relativas dos clusters na saída tendem a ser mais significativas do que no t-SNE.

O UMAP constrói um grafo ponderado no espaço de alta dimensão (a "representacao topológica difusa") e depois encontra um layout de baixa dimensão que preserve esse grafo o melhor possível.

Parâmetros-chave:

  • n_neighbors: quantos vizinhos definem a estrutura local (similar a perplexidade). Valores maiores preservam mais estrutura global.
  • min_dist: quao apertados os pontos se agrupam na saída. Valores menores criam clusters mais densos.

Quando usar qual

Method Use case Preserves Speed
PCA Pre-processamento antes do treinamento Variância global Rápido (exato), funciona em milhões de amostras
PCA Visualizacao exploratoria rápida Estrutura linear Rápido
t-SNE Graficos 2D de qualidade de publicacao Vizinhancas locais Lento (< 10k amostras ideal)
UMAP Visualizacao 2D em escala Estrutura local + alguma global Médio (lida com milhões)
PCA Redução de features para modelos Features ranqueadas por variância Rápido
t-SNE / UMAP Entender a estrutura de clusters Separacao de clusters Médio a lento

Regra prática: use PCA para pre-processamento e compressão de dados. Use t-SNE ou UMAP quando precisar visualizar estrutura em 2D.

Kernel PCA

O PCA padrão encontra subespacos lineares. Ele rotaciona seu sistema de coordenadas e descarta eixos. Mas e se os dados estiverem em uma variedade não-linear? Um circulo em 2D não pode ser separado por nenhuma linha. O PCA padrão não vai ajudar.

O kernel PCA aplica PCA em um espaço de features de alta dimensão induzido por uma função de kernel, sem calcular explicitamente as coordenadas nesse espaço. Esse é o truque do kernel -- a mesma ideia por tras das SVMs.

O algoritmo:

  1. Calcule a matriz de kernel K onde K_ij = k(x_i, x_j)
  2. Centralize a matriz de kernel no espaço de features
  3. Faça a autodecomposição dá matriz de kernel centralizada
  4. Os autovetores principais (escalonados por 1/sqrt(autovalor)) são as projecoes

Funções de kernel comuns:

Kernel Formula Good for
RBF (Gaussian) exp(-gamma * ||x - y||^2) Maioria dos dados não-lineares, variedades suaves
Polynomial (x . y + c)^d Relações polinomiais
Sigmoid tanh(alpha * x . y + c) Mapeamentos do tipo rede neural

Quando usar kernel PCA vs PCA padrão:

Criterion Standard PCA Kernel PCA
Estrutura dos dados Subespaco linear Variedade não-linear
Velocidade O(min(n^2 d, d^2 n)) O(n^2 d + n^3)
Interpretabilidade Componentes são combinacoes lineares de features Componentes carecem de interpretação direta de features
Escalabilidade Funciona em milhões de amostras A matriz de kernel e n x n, limitada por memória
Reconstrução Transformacao inversa direta Requer aproximação de pre-imagem

O exemplo clássico: circulos concentricos em 2D. Dois aneis de pontos, um dentro do outro. O PCA padrão projeta ambos na mesma linha -- inutil para classificacao. O kernel PCA com um kernel RBF mapeia o circulo interno é o externo para regiões diferentes, tornando-os linearmente separaveis.

Erro de Reconstrução

Quao boa é a sua redução de dimensionalidade? Você comprimiu 784 dimensões para 50. O que você perdeu?

Meca o erro de reconstrução:

  1. Projete os dados para k dimensões: X_reduced = X @ W_k
  2. Reconstrua: X_hat = X_reduced @ W_k^T
  3. Calcule o MSE: mean((X - X_hat)^2)

Para o PCA, o erro de reconstrução tem uma relação limpa com a variância explicada:

Reconstruction error = sum of eigenvalues NOT included
Total variance = sum of ALL eigenvalues
Fraction lost = (sum of dropped eigenvalues) / (sum of all eigenvalues)

A razão de variância explicada para cada componente e:

explained_ratio_k = eigenvalue_k / sum(all eigenvalues)

Plotar a variância explicada cumulativa contra o número de componentes te dá a curva do "cotovelo". O número certo de componentes e onde:

  • A curva se achata (retornos decrescentes)
  • A variância cumulativa cruza o seu limiar (geralmente 0.90 ou 0.95)
  • O desempenho dá tarefa downstream estabiliza

O erro de reconstrução é útil além de escolher k. Você pode usa-lo para detecção de anomalias: amostras com alto erro de reconstrução são outliers que não se encaixam no subespaco aprendido. Essa é a base dá detecção de anomalias baseada em PCA em sistemas de producao.

Construa

Passo 1: PCA do zero

import numpy as np

class PCA:
    def __init__(self, n_components):
        self.n_components = n_components
        self.components = None
        self.mean = None
        self.eigenvalues = None
        self.explained_variance_ratio_ = None

    def fit(self, X):
        self.mean = np.mean(X, axis=0)
        X_centered = X - self.mean

        cov_matrix = np.cov(X_centered, rowvar=False)

        eigenvalues, eigenvectors = np.linalg.eigh(cov_matrix)

        sorted_idx = np.argsort(eigenvalues)[::-1]
        eigenvalues = eigenvalues[sorted_idx]
        eigenvectors = eigenvectors[:, sorted_idx]

        self.components = eigenvectors[:, :self.n_components].T
        self.eigenvalues = eigenvalues[:self.n_components]
        total_var = np.sum(eigenvalues)
        self.explained_variance_ratio_ = self.eigenvalues / total_var

        return self

    def transform(self, X):
        X_centered = X - self.mean
        return X_centered @ self.components.T

    def fit_transform(self, X):
        self.fit(X)
        return self.transform(X)

Passo 2: Teste em dados sintéticos

np.random.seed(42)
n_samples = 500

t = np.random.uniform(0, 2 * np.pi, n_samples)
x1 = 3 * np.cos(t) + np.random.normal(0, 0.2, n_samples)
x2 = 3 * np.sin(t) + np.random.normal(0, 0.2, n_samples)
x3 = 0.5 * x1 + 0.3 * x2 + np.random.normal(0, 0.1, n_samples)

X_synthetic = np.column_stack([x1, x2, x3])

pca = PCA(n_components=2)
X_reduced = pca.fit_transform(X_synthetic)

print(f"Original shape: {X_synthetic.shape}")
print(f"Reduced shape:  {X_reduced.shape}")
print(f"Explained variance ratios: {pca.explained_variance_ratio_}")
print(f"Total variance captured: {sum(pca.explained_variance_ratio_):.4f}")

Passo 3: Dígitos do MNIST em 2D

from sklearn.datasets import fetch_openml

mnist = fetch_openml("mnist_784", version=1, as_frame=False, parser="auto")
X_mnist = mnist.data[:5000].astype(float)
y_mnist = mnist.target[:5000].astype(int)

pca_mnist = PCA(n_components=50)
X_pca50 = pca_mnist.fit_transform(X_mnist)
print(f"50 components capture {sum(pca_mnist.explained_variance_ratio_):.2%} of variance")

pca_2d = PCA(n_components=2)
X_pca2d = pca_2d.fit_transform(X_mnist)
print(f"2 components capture {sum(pca_2d.explained_variance_ratio_):.2%} of variance")

Passo 4: Compare com o sklearn

from sklearn.decomposition import PCA as SklearnPCA
from sklearn.manifold import TSNE

sklearn_pca = SklearnPCA(n_components=2)
X_sklearn_pca = sklearn_pca.fit_transform(X_mnist)

print(f"\nOur PCA explained variance:     {pca_2d.explained_variance_ratio_}")
print(f"Sklearn PCA explained variance: {sklearn_pca.explained_variance_ratio_}")

diff = np.abs(np.abs(X_pca2d) - np.abs(X_sklearn_pca))
print(f"Max absolute difference: {diff.max():.10f}")

tsne = TSNE(n_components=2, perplexity=30, random_state=42)
X_tsne = tsne.fit_transform(X_mnist)
print(f"\nt-SNE output shape: {X_tsne.shape}")

Passo 5: Comparação com UMAP

try:
    from umap import UMAP

    reducer = UMAP(n_components=2, n_neighbors=15, min_dist=0.1, random_state=42)
    X_umap = reducer.fit_transform(X_mnist)
    print(f"UMAP output shape: {X_umap.shape}")
except ImportError:
    print("Install umap-learn: pip install umap-learn")

Use

PCA como pre-processamento antes de um classificador:

from sklearn.decomposition import PCA as SklearnPCA
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

X_train, X_test, y_train, y_test = train_test_split(
    X_mnist, y_mnist, test_size=0.2, random_state=42
)

results = {}
for k in [10, 30, 50, 100, 200]:
    pca_k = SklearnPCA(n_components=k)
    X_tr = pca_k.fit_transform(X_train)
    X_te = pca_k.transform(X_test)

    clf = LogisticRegression(max_iter=1000, random_state=42)
    clf.fit(X_tr, y_train)
    acc = accuracy_score(y_test, clf.predict(X_te))
    var_captured = sum(pca_k.explained_variance_ratio_)
    results[k] = (acc, var_captured)
    print(f"k={k:>3d}  accuracy={acc:.4f}  variance={var_captured:.4f}")

O desempenho estabiliza bem antes de 784 dimensões. Esse plato é o seu ponto de operacao.

Entregue

Está lição produz:

  • outputs/skill-dimensionality-reduction.md - uma skill para escolher a tecnica de redução de dimensionalidade certa para uma dada tarefa

Exercícios

  1. Modifique a classe PCA para suportar inverse_transform. Reconstrua dígitos do MNIST a partir de 10, 50 e 200 componentes. Imprima o erro de reconstrução (diferença quadratica média em relação ao original) para cada um.

  2. Execute o t-SNE no mesmo subconjunto do MNIST com valores de perplexidade de 5, 30 e 100. Descreva como a saída muda. Por que a perplexidade afeta o aperto dos clusters?

  3. Pegue um conjunto de dados com 50 features onde apenas 5 são informativas (gere um com sklearn.datasets.make_classification). Aplique PCA e verifique se a curva de variância explicada identifica corretamente que os dados são efetivamente 5-dimensionais.

Termos-Chave

Term What people say What it actually means
Curse of dimensionality "Features demais" Distâncias, volumes e densidade de dados se comportam de forma contraintuitiva conforme as dimensões crescem. Modelos precisam de exponencialmente mais dados para compensar.
PCA "Reduzir dimensões" Rotaciona seu sistema de coordenadas para que os eixos se alinhem com as direções de variância máxima, depois descarta os eixos de baixa variância.
Principal component "Uma direção importante" Um autovetor dá matriz de covariância. A direção no espaço de features ao longo dá qual os dados mais variam.
Explained variance ratio "Quanta informação esse componente tem" A fração dá variância total capturada por um componente principal. Some as top k razoes para ver quanto k componentes preservam.
Covariance matrix "Como as features se correlacionam" Uma matriz simétrica onde a entrada (i,j) mede como a feature i é a feature j se movem juntas. As entradas diagonais são as variâncias individuais.
t-SNE "Aquele grafico de clusters" Um método não-linear que mapeia dados de alta dimensão para 2D preservando probabilidades de vizinhanca pareadas. Bom para visualizacao, não para pre-processamento.
UMAP "t-SNE mais rápido" Um método não-linear baseado em analise topológica de dados. Preserva tanto estrutura local quanto alguma global. Escala melhor que o t-SNE.
Perplexity "Um botao do t-SNE" Controla o número efetivo de vizinhos que cada ponto considera. Baixa perplexidade foca em estrutura muito local. Alta perplexidade captura padrões mais amplos.
Manifold "A superfície em que os dados vivem" Uma superfície de baixa dimensão embutida em um espaço de alta dimensão. Uma folha de papel amassada em 3D é uma variedade 2D.

Leitura Adicional

0 lifetime access. Curriculum based on AI Engineering from Scratch by Rohit Ghumare (MIT, used under attribution).