第11篇:降维算法:PCA、t-SNE、UMAP
摘要:
本文系统讲解三大主流降维算法:PCA(主成分分析)、t-SNE(t-分布随机邻域嵌入)和UMAP(统一流形近似与投影)。从协方差矩阵、特征值分解到流形学习思想,结合Scikit-learn与Python代码,实现高维数据的可视化与特征压缩,并对比其在MNIST手写数字数据集上的表现,帮助学习者掌握“让数据变简单”的艺术。
一、为什么需要降维?
高维数据带来“维度灾难”(Curse of Dimensionality):
- ✅ 计算成本高:距离计算、模型训练变慢
- ✅ 可视化困难:人类无法理解>3维空间
- ✅ 过拟合风险:特征过多,样本相对不足
- ✅ 噪声干扰:无关特征影响模型性能
降维(Dimensionality Reduction)目标:
- 在保留关键信息的前提下,将数据从高维映射到低维(通常2D/3D)。
二、PCA:主成分分析(线性降维)
2.1 核心思想
寻找数据方差最大的方向(主成分),将数据投影到这些方向上。
- 第1主成分:方差最大的方向
- 第2主成分:与第1正交且方差次大的方向
- ...
2.2 数学原理
- 中心化:
X ← X - mean(X)
- 计算协方差矩阵:
Σ = (1/(n-1)) XᵀX
- 特征值分解:
Σ = VΛVᵀ
V
:特征向量矩阵(主成分方向)Λ
:特征值对角阵(对应方差大小)
- 投影:
Z = X V_k
,V_k
为前k个最大特征值对应的特征向量
2.3 从零实现PCA
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_irisclass PCA:def __init__(self, n_components=2):self.n_components = n_componentsself.components_ = Noneself.mean_ = Noneself.explained_variance_ratio_ = Nonedef fit(self, X):# 1. 中心化self.mean_ = np.mean(X, axis=0)X_centered = X - self.mean_# 2. 协方差矩阵cov_matrix = np.cov(X_centered, rowvar=False)# 3. 特征值分解eigen_vals, eigen_vecs = np.linalg.eigh(cov_matrix)# 4. 按特征值降序排列idx = np.argsort(eigen_vals)[::-1]eigen_vals = eigen_vals[idx]eigen_vecs = eigen_vecs[:, idx]# 5. 选择前k个主成分self.components_ = eigen_vecs[:, :self.n_components]# 6. 解释方差比total_var = np.sum(eigen_vals)self.explained_variance_ratio_ = eigen_vals[:self.n_components] / total_varreturn selfdef transform(self, X):X_centered = X - self.mean_return np.dot(X_centered, self.components_)def fit_transform(self, X):return self.fit(X).transform(X)# 使用鸢尾花数据集(4维 → 2维)
iris = load_iris()
X, y = iris.data, iris.targetpca = PCA(n_components=2)
X_pca = pca.fit_transform(X)# 可视化
plt.figure(figsize=(10, 7))
scatter = plt.scatter(X_pca[:,0], X_pca[:,1], c=y, cmap='viridis', alpha=0.7)
plt.xlabel(f'第一主成分 (解释方差: {pca.explained_variance_ratio_[0]:.2%})')
plt.ylabel(f'第二主成分 (解释方差: {pca.explained_variance_ratio_[1]:.2%})')
plt.title('PCA 降维结果')
plt.colorbar(scatter, ticks=[0,1,2], label='类别')
plt.show()
✅ PCA保留了约95%的方差,类别清晰可分。
三、t-SNE:t-分布随机邻域嵌入(非线性降维)
3.1 核心思想
t-SNE专注于保留局部结构,即“相似的样本在低维空间中仍相近”。
3.2 算法步骤
-
高维空间:计算样本
i
与j
的相似度(用高斯分布):p_{j|i} = exp(-||x_i - x_j||² / (2σ_i²)) / Σ_{k≠i} exp(-||x_i - x_k||² / (2σ_i²))
σ_i
由困惑度(Perplexity)决定。 -
低维空间:定义样本
i
与j
的相似度(用t分布,自由度=1):q_{ij} = (1 + ||y_i - y_j||²)^{-1} / Σ_{k≠l} (1 + ||y_k - y_l||²)^{-1}
-
优化:最小化
P
和Q
的KL散度:KL(P||Q) = Σ p_{ij} log(p_{ij}/q_{ij})
使用梯度下降优化
Y
。
3.3 关键参数
- 困惑度(Perplexity):约等于“有效邻居数”,通常5-50。
- 学习率(Learning Rate):梯度下降步长。
- 迭代次数:通常1000+
3.4 Scikit-learn实战t-SNE
from sklearn.manifold import TSNE# 对鸢尾花数据降维
tsne = TSNE(n_components=2, perplexity=30, n_iter=1000, random_state=42)
X_tsne = tsne.fit_transform(X)plt.figure(figsize=(10, 7))
plt.scatter(X_tsne[:,0], X_tsne[:,1], c=y, cmap='plasma', alpha=0.7)
plt.title('t-SNE 降维结果')
plt.show()
✅ t-SNE能更好分离类别,但结果随机性强,每次运行略有不同。
四、UMAP:统一流形近似与投影
4.1 UMAP vs t-SNE
UMAP是t-SNE的现代替代品,具有:
- ✅ 更快的速度:适合大数据集
- ✅ 更好的全局结构保持
- ✅ 可逆性(部分实现)
- ✅ 支持新样本投影
4.2 核心思想
- 将数据视为拓扑空间中的点。
- 构建高维空间的模糊拓扑表示(基于最近邻)。
- 在低维空间寻找最相似的拓扑结构。
4.3 Scikit-learn实战UMAP
# 需先安装: pip install umap-learn
import umapumap_reducer = umap.UMAP(n_components=2, n_neighbors=15, min_dist=0.1, random_state=42)
X_umap = umap_reducer.fit_transform(X)plt.figure(figsize=(10, 7))
plt.scatter(X_umap[:,0], X_umap[:,1], c=y, cmap='Set1', alpha=0.7)
plt.title('UMAP 降维结果')
plt.show()
✅ UMAP在保持局部结构的同时,全局布局更清晰。
五、实战:MNIST手写数字可视化(784维 → 2维)
from sklearn.datasets import fetch_openml
import time# 加载MNIST(仅取前1000个样本加速)
mnist = fetch_openml('mnist_784', version=1, as_frame=False)
X_mnist, y_mnist = mnist.data[:1000], mnist.target[:1000].astype(int)# 标准化
X_mnist = X_mnist / 255.0# 比较三种算法
methods = {'PCA': PCA(n_components=2),'t-SNE': TSNE(n_components=2, perplexity=30, n_iter=500, random_state=42),'UMAP': umap.UMAP(n_components=2, n_neighbors=15, min_dist=0.1, random_state=42)
}results = {}
for name, method in methods.items():start = time.time()X_reduced = method.fit_transform(X_mnist)end = time.time()results[name] = X_reducedprint(f"{name} 用时: {end-start:.2f}秒")# 绘制对比图
fig, axes = plt.subplots(1, 3, figsize=(18, 6))
for ax, (name, X_reduced) in zip(axes, results.items()):scatter = ax.scatter(X_reduced[:,0], X_reduced[:,1], c=y_mnist, cmap='tab10', s=5)ax.set_title(f'{name} (MNIST)')ax.axis('off')
plt.tight_layout()
plt.show()
结果分析:
- PCA:速度快,但类别重叠严重,仅保留线性结构。
- t-SNE:类别分离好,但运行慢,局部结构强。
- UMAP:速度接近PCA,效果接近t-SNE,综合性能最佳。
六、降维的应用场景
6.1 数据可视化
- 将高维数据(如词向量、图像特征)投影到2D/3D便于观察。
6.2 特征工程
- 作为预处理步骤,降低后续模型(如SVM、K-Means)的输入维度。
6.3 去噪
- PCA可过滤掉方差小的“噪声”主成分。
6.4 加速计算
- 减少特征数量,提升训练和推理速度。
七、如何选择降维算法?
场景 | 推荐算法 |
---|---|
快速线性降维 | PCA |
小数据集,追求最佳可视化 | t-SNE |
大数据集,兼顾速度与效果 | UMAP |
需要可逆变换 | PCA |
探索数据流形结构 | UMAP |
📌 一般建议:先用PCA快速探索,再用UMAP或t-SNE精细可视化。
八、总结与学习建议
本文我们:
- 掌握了PCA的数学原理(协方差、特征分解);
- 理解了t-SNE的KL散度优化与困惑度;
- 学习了UMAP的拓扑流形思想;
- 在MNIST上对比了三种算法;
- 了解了降维的多种应用场景。
📌 学习建议:
- 理解“保留什么”:PCA保全局方差,t-SNE/UMAP保局部邻近。
- 注意参数调优:如t-SNE的
perplexity
,UMAP的n_neighbors
。- 结果非绝对:降维是近似,不同运行可能有差异。
- 结合后续任务:降维后需评估对下游任务的影响。
九、下一篇文章预告
第12篇:神经网络基础:从感知机到多层感知机(MLP)
我们将进入深度学习领域,讲解:
- 感知机(Perceptron)的数学模型与局限
- 激活函数(Sigmoid, ReLU, Tanh)的作用
- 前向传播与反向传播(Backpropagation)算法
- 梯度下降与损失函数
- 使用NumPy从零实现MLP
- Scikit-learn的MLPClassifier实战
揭开深度学习的“基石”——神经网络的神秘面纱!
参考文献
- 周志华. 《机器学习》(“西瓜书”). 第10章(降维与度量学习)。
- Maaten, L. v. d. & Hinton, G. (2008). Visualizing Data using t-SNE. JMLR.
- McInnes, L. et al. (2018). UMAP: Uniform Manifold Approximation and Projection. JOSS.
- Scikit-learn降维文档: 2.5. Decomposing signals in components (matrix factorization problems) — scikit-learn 1.7.1 documentation
- UMAP官方文档: UMAP: Uniform Manifold Approximation and Projection for Dimension Reduction — umap 0.5.8 documentation