Machine Learning: Redução de Dimensionalidade
Última atualização: 2026-08-26
No espaço de alta dimensionalidade, cada ponto está longe de todos os outros — a redução de dimensionalidade não é apenas compressão, é uma forma de revelar a verdadeira estrutura dos seus dados.
1. O que você vai aprender
- A maldição da dimensionalidade: colapso de distâncias e dispersão dos dados em espaços de alta dimensionalidade
- PCA (Análise de Componentes Principais): matrizes de covariância, decomposição em autovalores e razão de variância explicada
- Visualização por manifold com t-SNE: o parâmetro perplexity, divergência KL e mapeamento de alta dimensionalidade para 2D/3D
- Uma rápida visão do UMAP: uma alternativa mais rápida ao t-SNE que preserva a estrutura global
- Estudo de caso de comportamento de usuário em e-commerce: features de comportamento de 50 dimensões → visualização 2D que revela segmentos naturais de usuários
2. A história real de um cientista de dados
(1) O problema: dados de comportamento de usuário de 50 dimensões que você não consegue ver ou entender
Bob coletou 50 features de comportamento de usuário (duração da sessão, contagem de cliques, taxas de engajamento por categoria, e assim por diante), mas não tinha como entender intuitivamente como os usuários estavam distribuídos — tabelas eram ilegíveis, gráficos de dispersão se limitam a duas dimensões, e os resultados de agrupamento eram difíceis de validar. Dados de alta dimensionalidade são uma caixa-preta para a intuição humana.
(2) A solução: PCA + t-SNE
PCA comprime os dados para duas dimensões preservando a variância máxima, e t-SNE mapeia a estrutura local em um plano 2D — tornando os dados de alta dimensionalidade algo que você pode realmente ver.
from sklearn.decomposition import PCA
from sklearn.manifold import TSNE
from sklearn.preprocessing import StandardScaler
X_scaled = StandardScaler().fit_transform(X_50d)
# PCA: rápido, preserva variância global
X_pca = PCA(n_components=2).fit_transform(X_scaled)
# t-SNE: lento, preserva estrutura local
X_tsne = TSNE(n_components=2, perplexity=30).fit_transform(X_scaled)
(3) O resultado: um gráfico 2D revela instantaneamente três grupos naturais de usuários
Após a redução de dimensionalidade, Bob conseguiu ver três grupos distintos de usuários de relance — alto valor, ativos e dormentes — que se alinhavam bem com seus resultados de agrupamento e permitiram à equipe de negócios entender a distribuição dos dados em segundos.
3. A maldição da dimensionalidade
(1) Fenômenos contraintuitivos em espaços de alta dimensionalidade
- As distâncias colapsam: no espaço de alta dimensionalidade, a distância até o ponto mais próximo e a distância até o ponto mais distante convergem para o mesmo valor
- Dispersão dos dados: o volume cresce exponencialmente com a dimensionalidade, então um número fixo de amostras fica cada vez mais esparso
- Risco de sobreajuste: quando features >> amostras, os modelos tendem a "memorizar o ruído"
▶ Exemplo: Um experimento da maldição da dimensionalidade
import numpy as np
rng = np.random.default_rng(42)
for dim in [2, 10, 50, 100, 500]:
n = 1000
X = rng.uniform(0, 1, (n, dim))
# Calcular distâncias par a par
from sklearn.metrics import pairwise_distances
dists = pairwise_distances(X)
np.fill_diagonal(dists, np.inf)
min_dist = dists.min()
max_dist = dists.max()
ratio = (max_dist - min_dist) / max_dist
print(f"Dim={dim:4d}: min={min_dist:.3f}, max={max_dist:.3f}, "
f"intervalo_relativo={ratio:.4f}")
Saída:
# Executa com sucesso
| Dimensões | Distância Mín | Distância Máx | Intervalo Relativo | Significado |
|---|---|---|---|---|
| 2 | 0,02 | 1,41 | 0,99 | Distâncias são significativas |
| 10 | 0,78 | 1,87 | 0,58 | Distâncias começam a falhar |
| 50 | 2,42 | 3,28 | 0,26 | Distâncias são quase inúteis |
| 100 | 3,54 | 4,05 | 0,13 | Todas as distâncias convergem |
| 500 | 8,11 | 8,47 | 0,04 | Completamente quebradas |
4. PCA (Análise de Componentes Principais)
(1) Como o PCA funciona
O PCA usa a decomposição em autovalores para encontrar as direções de variância máxima (os componentes principais) e projeta os dados em um espaço de menor dimensionalidade.
graph TB
INPUT[Dados Padronizados] --> COV[Calcular Matriz de Covariância]
COV --> EIG[Decomposição em Autovalores]
EIG --> SELECT[Selecionar Top K Autovetores]
SELECT --> PROJECT[Projetar Dados em K Dimensões]
PROJECT --> OUTPUT[Representação de Baixa Dimensão]
▶ Exemplo: Redução por PCA e razão de variância explicada
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
import numpy as np
X, y = load_iris(return_X_y=True)
# Padronizar primeiro (PCA é sensível à escala)
X_scaled = StandardScaler().fit_transform(X)
# PCA com todos os componentes para ver a variância explicada
pca_full = PCA()
pca_full.fit(X_scaled)
print("Razão de variância explicada:")
for i, (evr, cum) in enumerate(zip(pca_full.explained_variance_ratio_,
pca_full.explained_variance_ratio_.cumsum())):
print(f" PC{i+1}: {evr:.3f} (cumulativa: {cum:.3f})")
# Reduzir para 2D
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)
print(f"\n2D PCA preserva {pca.explained_variance_ratio_.sum():.1%} da variância")
# Visualizar
fig, ax = plt.subplots(figsize=(8, 6))
for target in np.unique(y):
mask = y == target
ax.scatter(X_pca[mask, 0], X_pca[mask, 1], label=load_iris().target_names[target], s=40)
ax.set_xlabel(f"PC1 ({pca.explained_variance_ratio_[0]:.1%})")
ax.set_ylabel(f"PC2 ({pca.explained_variance_ratio_[1]:.1%})")
ax.set_title("PCA do Conjunto de Dados Iris")
ax.legend()
plt.tight_layout()
plt.savefig("pca_iris.png", dpi=150)
Saída:
Razão de variância explicada:
(2) Escolhendo quantos componentes manter
▶ Exemplo: Gráfico de variância explicada cumulativa
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(42)
n, p = 500, 50
X = rng.standard_normal((n, p))
# Tornar as primeiras 5 dimensões com mais sinal
X[:, :5] *= 3
X_scaled = StandardScaler().fit_transform(X)
pca = PCA().fit(X_scaled)
fig, ax = plt.subplots(figsize=(10, 5))
cumvar = pca.explained_variance_ratio_.cumsum()
ax.plot(range(1, len(cumvar)+1), cumvar, "b-o", markersize=3)
ax.axhline(0.95, color="red", linestyle="--", label="95% da variância")
ax.axhline(0.90, color="orange", linestyle="--", label="90% da variância")
ax.set_xlabel("Número de Componentes")
ax.set_ylabel("Variância Explicada Cumulativa")
ax.set_title("Variância Explicada pelo PCA")
ax.legend()
n_95 = (cumvar < 0.95).sum() + 1
print(f"Componentes para 95% da variância: {n_95}")
plt.tight_layout()
plt.savefig("pca_variance.png", dpi=150)
Saída:
# Executa com sucesso
| Limiar | Significado | Resultado típico |
|---|---|---|
| 90% | Mantém o sinal principal | 5–15 componentes (para 50 dimensões) |
| 95% | Mantém mais detalhes | 10–25 componentes (para 50 dimensões) |
| 99% | Quase sem perdas | 20–40 componentes (para 50 dimensões) |
5. Visualização por Manifold com t-SNE
(1) Como o t-SNE funciona
O t-SNE preserva as relações de vizinhança local, minimizando a divergência KL entre similaridades par a par nos espaços de alta e baixa dimensionalidade.
▶ Exemplo: Visualização com t-SNE
from sklearn.manifold import TSNE
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import load_digits
import matplotlib.pyplot as plt
import numpy as np
X, y = load_digits(return_X_y=True)
X_scaled = StandardScaler().fit_transform(X)
# t-SNE com diferentes perplexity
fig, axes = plt.subplots(1, 3, figsize=(18, 5))
for ax, perplexity in zip(axes, [5, 30, 50]):
tsne = TSNE(n_components=2, perplexity=perplexity, random_state=42, init="pca")
X_tsne = tsne.fit_transform(X_scaled)
scatter = ax.scatter(X_tsne[:, 0], X_tsne[:, 1], c=y, cmap="tab10", s=5, alpha=0.7)
ax.set_title(f"t-SNE (perplexity={perplexity})")
ax.set_xlabel("t-SNE 1")
ax.set_ylabel("t-SNE 2")
plt.tight_layout()
plt.savefig("tsne_perplexity.png", dpi=150)
Saída:
# Executa com sucesso
(2) Pontos de atenção com o t-SNE
| Consideração | Notas |
|---|---|
| perplexity | Tipicamente 5–50. Menor → foca em vizinhos próximos; maior → foca na estrutura global |
| Semente aleatória | Sementes diferentes podem dar resultados diferentes — fixe random_state |
| Não usar downstream | O t-SNE não preserva distâncias, então é inadequado como entrada para um modelo |
| Lento para calcular | Para grandes conjuntos de dados (>10k), reduza com PCA primeiro, depois execute t-SNE |
| Tamanho do cluster não tem significado | O t-SNE pode exagerar ou diminuir os tamanhos relativos dos clusters |
▶ Exemplo: Acelerando o t-SNE com pré-processamento PCA
from sklearn.manifold import TSNE
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
import numpy as np
rng = np.random.default_rng(42)
X = rng.standard_normal((5000, 50))
# t-SNE direto (lento)
# tsne = TSNE(n_components=2, random_state=42)
# X_tsne = tsne.fit_transform(X) # Muito lento em 5000x50!
# Melhor prática: PCA primeiro, depois t-SNE
X_scaled = StandardScaler().fit_transform(X)
X_pca_30 = PCA(n_components=30).fit_transform(X_scaled) # PCA rápido para 30D
X_tsne = TSNE(n_components=2, perplexity=30, random_state=42,
init="pca", learning_rate="auto").fit_transform(X_pca_30)
print(f"Shape PCA→t-SNE: {X_tsne.shape}")
Saída:
# Executa com sucesso
6. Prática: Redução de Dimensionalidade para Comportamento de Usuário em E-commerce
▶ Exemplo: Features de usuário de 50 dimensões → visualização 2D
from sklearn.decomposition import PCA
from sklearn.manifold import TSNE
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(42)
n = 3000
# Gerar dados de comportamento de usuário de 50 dimensões com 3 grupos naturais
group1 = rng.normal(loc=2, scale=1, size=(800, 50)) # Usuários de alto valor
group2 = rng.normal(loc=0, scale=1.5, size=(1200, 50)) # Usuários regulares
group3 = rng.normal(loc=-2, scale=0.8, size=(1000, 50)) # Usuários dormentes
X = np.vstack([group1, group2, group3])
# Adicionar dimensões de ruído
X[:, 20:] += rng.normal(0, 3, (n, 30))
# Padronizar
X_scaled = StandardScaler().fit_transform(X)
# PCA para 2D
X_pca = PCA(n_components=2).fit_transform(X_scaled)
# t-SNE para 2D (com pré-processamento PCA)
X_pca30 = PCA(n_components=30).fit_transform(X_scaled)
X_tsne = TSNE(n_components=2, perplexity=30, random_state=42,
init="pca", learning_rate="auto").fit_transform(X_pca30)
# Rótulos K-Means para referência
labels = KMeans(n_clusters=3, random_state=42, n_init=10).fit_predict(X_scaled)
# Visualizar
fig, axes = plt.subplots(1, 2, figsize=(16, 6))
for ax, X_2d, title in [(axes[0], X_pca, "PCA"), (axes[1], X_tsne, "t-SNE")]:
scatter = ax.scatter(X_2d[:, 0], X_2d[:, 1], c=labels, cmap="Set1", s=8, alpha=0.6)
ax.set_title(title)
ax.set_xlabel(f"{title} 1")
ax.set_ylabel(f"{title} 2")
plt.tight_layout()
plt.savefig("user_behavior_dim_reduction.png", dpi=150)
Saída:
# Executa com sucesso
(1) PCA vs t-SNE vs UMAP
| Aspecto | PCA | t-SNE | UMAP |
|---|---|---|---|
| O que preserva | Variância global | Vizinhanças locais | Global + local |
| Velocidade | Rápido (<1s) | Lento (minutos) | Médio (segundos) |
| Reprodutibilidade | Determinístico | Dependente de seed | Dependente de seed |
| Interpretabilidade | Alta (variância explicada) | Baixa | Média |
| Uso downstream | ✅ Usável como features | ❌ Apenas visualização | ⚠️ Precisa de validação |
| Mapeamento de novos dados | ✅ transform | ❌ Precisa rerodar | ✅ transform |
❓ Perguntas Frequentes
P: Posso usar dados reduzidos por PCA para treinar modelos? R: Sim. Os componentes principais do PCA podem ser usados como entrada para modelos, e são especialmente úteis em cenários de alta dimensionalidade com poucas amostras. Lembre-se: ajuste o PCA no conjunto de treino e apenas transforme o conjunto de teste.
P: Por que o t-SNE não pode ser usado como features de modelo? R: O t-SNE não preserva relações de distância global, e não consegue transformar novos dados (você precisa recomputar do zero a cada vez). É adequado apenas para visualização, não como entrada de modelo.
P: Como escolher o parâmetro perplexity? R: O intervalo típico é 5–50. Para conjuntos de dados pequenos (~1k), use 5–20; para dados grandes (10k+), use 30–50. Teste alguns valores e escolha o que produzir os clusters mais nítidos.
P: PCA exige padronização? R: Com certeza. PCA maximiza a variância, então se suas features estão em escalas diferentes (por exemplo, idade vs. renda), as features de alta variância dominarão os componentes principais. Sempre execute StandardScaler antes do PCA.
P: O que torna o UMAP melhor que o t-SNE? R: Três vantagens — 1) é 10–100x mais rápido; 2) preserva mais estrutura global; 3) suporta transformação de novos dados. A desvantagem é que você precisa instalá-lo separadamente com
pip install umap-learn.
P: Quanta informação é perdida na redução de dimensionalidade? R: Depende de quantos componentes você mantém. Reter 95% da variância geralmente significa perder apenas cerca de 5% da informação — e o que é descartado pode ser apenas ruído. Use
cumsum(explained_variance_ratio_)para decidir.
📖 Resumo
- A maldição da dimensionalidade: distâncias colapsam, dados ficam esparsos e o risco de sobreajuste aumenta — a redução de dimensionalidade é essencial
- PCA: preserva as direções de máxima variância; ótimo para redução rápida e entrada de modelo, mas é preciso padronizar primeiro
- t-SNE: preserva relações de vizinhança local; ideal para visualização, mas inutilizável como features de modelo e lento para calcular
- UMAP: uma alternativa ao t-SNE que é mais rápida, preserva a estrutura global e suporta mapeamento de novos dados
- Um pipeline típico: StandardScaler → PCA (aceleração + remoção de ruído) → t-SNE/UMAP (visualização)
- Escolhendo o número de componentes do PCA: olhe a razão de variância explicada cumulativa; 90–95% é um limite comum
📝 Exercícios
- Básico (Dificuldade ⭐): Execute o PCA no conjunto de dados Iris para reduzi-lo a 2 dimensões, plote um gráfico de dispersão e anote a razão de variância explicada. Dica:
PCA(n_components=2)+ scatter. - Intermediário (Dificuldade ⭐⭐): Nos dados
load_digits(64 dimensões), primeiro reduza para 30 dimensões com PCA, depois para 2 dimensões com t-SNE. Compare o tempo de execução e a qualidade da visualização do t-SNE direto versus PCA + t-SNE. Dica: meça o tempo comtime.time(). - Desafio (Dificuldade ⭐⭐⭐): Gere dados de 50 dimensões com 3 grupos de usuários, reduza para 2D usando PCA, t-SNE e UMAP respectivamente, e avalie com agrupamento K-Means (ARI/NMI) para determinar qual método melhor preserva a estrutura original dos grupos. Dica:
sklearn.metrics.adjusted_rand_score.
← Anterior: Aprendizado por Ensemble | Próximo: Fundamentos de NLP →