13 - 降维与流形学习¶
🎯 为什么需要降维?¶
维度灾难 (Curse of Dimensionality)¶
Text Only
高维空间的问题:
1. 数据稀疏性
- 1000个样本在3维空间:密集
- 1000个样本在1000维空间:极度稀疏
2. 距离失效
- 高维空间中,所有点对的距离趋于相近
- 欧氏距离失去区分能力
3. 计算复杂度
- 存储:O(d²) 的协方差矩阵
- 计算:矩阵分解复杂度为 O(d³),高维时计算量巨大
4. 可视化困难
- 人类只能直观理解3维及以下
降维的好处¶
📐 线性降维方法¶
PCA (主成分分析)¶
已在 03-无监督学习.md 中详细介绍,这里补充进阶内容。
PCA的数学本质¶
Text Only
目标:找到投影方向,使得投影后的方差最大
数学推导:
给定中心化数据 X (n×d)
寻找投影向量 w,使得 Var(Xw) 最大
Var(Xw) = w^T X^T X w / n = w^T C w
约束:||w|| = 1
拉格朗日函数:
L = w^T C w - λ(w^T w - 1)
求导得:Cw = λw
→ w 是协方差矩阵 C 的特征向量
→ λ 是对应的特征值
→ 最大方差 = 最大特征值
核PCA (Kernel PCA)¶
Text Only
问题:PCA只能找到线性投影
解决:通过核函数映射到高维空间,再PCA
步骤:
1. 选择核函数 K(x,y)
2. 计算核矩阵 K (n×n)
3. 对K进行特征分解
4. 投影:新坐标 = K · 特征向量
常用核函数:
- 多项式核:K(x,y) = (x·y + c)^d
- RBF核:K(x,y) = exp(-γ||x-y||²)
Python
from sklearn.decomposition import KernelPCA
# 核PCA
kpca = KernelPCA(
n_components=2,
kernel='rbf', # 或 'poly', 'sigmoid', 'linear'
gamma=0.1
)
X_kpca = kpca.fit_transform(X)
LDA (线性判别分析)¶
核心思想¶
有监督降维:最大化类间距离,最小化类内距离
Text Only
目标:找到投影方向,使得:
- 不同类的中心尽可能远(类间散度大)
- 同类样本尽可能聚集(类内散度小)
优化目标:
J(w) = (类间散度) / (类内散度) = (w^T S_b w) / (w^T S_w w)
其中:
S_b = 类间散度矩阵
S_w = 类内散度矩阵
与PCA的区别¶
代码实现¶
Python
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
# LDA降维
lda = LinearDiscriminantAnalysis(n_components=2)
X_lda = lda.fit_transform(X, y)
# 注意:n_components <= min(n_classes - 1, n_features)
print(f"LDA降维后形状: {X_lda.shape}")
# 可视化
plt.scatter(X_lda[:, 0], X_lda[:, 1], c=y, cmap='viridis')
plt.title('LDA降维可视化')
plt.show()
其他线性方法¶
NMF (非负矩阵分解)¶
Python
from sklearn.decomposition import NMF
nmf = NMF(n_components=10, random_state=42)
W = nmf.fit_transform(X) # 基矩阵
H = nmf.components_ # 系数矩阵
SVD (奇异值分解)¶
已在 00-数学基础.md 中介绍。
Python
from sklearn.decomposition import TruncatedSVD
svd = TruncatedSVD(n_components=50)
X_svd = svd.fit_transform(X)
🌊 非线性降维 - 流形学习¶
什么是流形?¶
Text Only
直观理解:
- 高维空间中的低维结构
- 局部是欧几里得的,整体是非线性的
例子:
- 地球表面:3维空间中的2维流形
- 瑞士卷:3维空间中的2维流形
- 人脸图像:高维像素空间中的低维流形
t-SNE (t-Distributed Stochastic Neighbor Embedding)¶
已在 03-无监督学习.md 中介绍,这里补充原理。
数学原理¶
Text Only
核心思想:保持局部邻域关系
步骤1:在高维空间计算相似度
p(j|i) = exp(-||x_i - x_j||² / 2σ_i²) / Σ_{k≠i} exp(-||x_i - x_k||² / 2σ_i²)
步骤2:在低维空间计算相似度(用t分布)
q(j|i) = (1 + ||y_i - y_j||²)^(-1) / Σ_{k≠i} (1 + ||y_i - y_k||²)^(-1)
步骤3:最小化KL散度
KL(P||Q) = Σ_i Σ_j p(j|i) log(p(j|i) / q(j|i))
为什么用t分布?
- 重尾特性:防止拥挤问题 (crowding problem)
- 允许低维空间中保持更多的局部结构
使用技巧¶
Python
from sklearn.manifold import TSNE
# 推荐流程
# 1. 先用PCA降到中等维度(如50维)
from sklearn.decomposition import PCA
pca = PCA(n_components=50)
X_pca = pca.fit_transform(X_scaled)
# 2. 再用t-SNE降到2维
tsne = TSNE(
n_components=2,
perplexity=30, # 通常5-50
learning_rate=200, # 通常10-1000
n_iter=1000,
random_state=42
)
X_tsne = tsne.fit_transform(X_pca)
# 注意:t-SNE计算复杂度高,不适合大数据
UMAP (Uniform Manifold Approximation and Projection)¶
核心优势¶
数学思想¶
Text Only
基于两个假设:
1. 数据均匀分布在黎曼流形上
2. 黎曼度量是局部欧几里得的(常数)
步骤:
1. 构建高维空间的模糊拓扑表示
2. 构建低维空间的模糊拓扑表示
3. 最小化两者的交叉熵
代码实现¶
Python
import umap
# UMAP降维
reducer = umap.UMAP(
n_components=2,
n_neighbors=15, # 控制局部/全局权衡
min_dist=0.1, # 控制点之间的最小距离
metric='euclidean', # 距离度量
random_state=42
)
X_umap = reducer.fit_transform(X_scaled)
# 可视化
plt.scatter(X_umap[:, 0], X_umap[:, 1], c=y, cmap='viridis', s=5)
plt.title('UMAP降维可视化')
plt.show()
# UMAP支持transform新数据
# reducer.transform(X_new)
参数调优¶
Text Only
n_neighbors:
- 小值(2-5):关注局部结构,可能发现细粒度聚类
- 大值(50-100):关注全局结构,保持整体形状
min_dist:
- 小值(0.001-0.1):点更聚集,显示局部结构
- 大值(0.5-1.0):点更分散,显示全局结构
其他流形学习方法¶
ISOMAP¶
Python
from sklearn.manifold import Isomap
isomap = Isomap(n_components=2, n_neighbors=10)
X_isomap = isomap.fit_transform(X)
LLE (局部线性嵌入)¶
Python
from sklearn.manifold import LocallyLinearEmbedding
lle = LocallyLinearEmbedding(n_components=2, n_neighbors=10)
X_lle = lle.fit_transform(X)
MDS (多维缩放)¶
Python
from sklearn.manifold import MDS
mds = MDS(n_components=2, random_state=42)
X_mds = mds.fit_transform(X)
📊 方法对比¶
线性方法对比¶
| 方法 | 类型 | 特点 | 适用场景 |
|---|---|---|---|
| PCA | 无监督 | 最大化方差,全局结构 | 通用降维,去噪 |
| LDA | 有监督 | 最大化类间可分性 | 分类预处理 |
| NMF | 无监督 | 非负约束,可解释性强 | 图像、文本 |
| SVD | 无监督 | 矩阵分解,数值稳定 | 推荐系统,文本 |
非线性方法对比¶
| 方法 | 速度 | 全局结构 | 新数据变换 | 适用场景 |
|---|---|---|---|---|
| t-SNE | 慢 | 差 | 不支持 | 可视化,小数据 |
| UMAP | 快 | 好 | 支持 | 可视化,大数据 |
| ISOMAP | 中等 | 中等 | 不支持 | 测地距离重要 |
| LLE | 中等 | 差 | 不支持 | 局部线性结构 |
| MDS | 慢 | 好 | 不支持 | 距离保持重要 |
🎯 实践建议¶
选择指南¶
Text Only
第一步:是否需要非线性?
├── 是 → 使用流形学习方法
│ ├── 数据量大 (>10K) → UMAP
│ └── 数据量小 → t-SNE
│
└── 否 → 使用线性方法
├── 有标签 → LDA
└── 无标签 → PCA
完整流程示例¶
Python
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
import umap
# 1. 数据标准化(必须!)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# 2. 先用PCA去除噪声,降到中等维度
pca = PCA(n_components=50)
X_pca = pca.fit_transform(X_scaled)
print(f"PCA保留方差比例: {pca.explained_variance_ratio_.sum():.2%}")
# 3. 再用UMAP降到2维可视化
reducer = umap.UMAP(n_components=2, random_state=42)
X_umap = reducer.fit_transform(X_pca)
# 4. 可视化
plt.figure(figsize=(10, 8))
scatter = plt.scatter(X_umap[:, 0], X_umap[:, 1], c=y, cmap='tab10', s=5)
plt.colorbar(scatter)
plt.title('UMAP Visualization')
plt.show()
常见陷阱¶
Text Only
❌ 不降维直接可视化高维数据
→ 必须使用降维方法
❌ 不降维直接用t-SNE/UMAP
→ 先PCA预处理,加速且去噪
❌ 忽略数据标准化
→ 不同尺度的特征会主导结果
❌ 过度解读t-SNE/UMAP的距离
→ 只保持局部结构,全局距离不可靠
❌ 用降维后的数据直接做分类
→ 信息损失,性能可能下降
💡 总结¶
Text Only
降维的本质:
在保留重要信息的前提下,减少数据维度
线性方法:
- 简单、快速、可解释
- 适合线性结构的数据
非线性方法(流形学习):
- 能发现复杂的非线性结构
- 计算成本高,主要用于可视化
选择原则:
1. 先尝试线性方法
2. 可视化用UMAP或t-SNE
3. 始终标准化数据
4. 注意信息损失与任务需求的平衡
下一步:学习 14-贝叶斯方法.md,掌握概率化建模方法!