4. PCA的Python实现:使用sklearn进行PCA降维
理论讲完了,咱们直接上手。我个人习惯用 sklearn.decomposition.PCA,封装得很干净,几行代码就能跑通。不过要注意——调用方式不同,结果解释也不同。我见过不少新手上来就调 .fit_transform(),然后拿着结果一脸懵。咱们把每个环节拆开,你自然就明白了。
4.1 核心API速览
sklearn 的 PCA 主要就三个方法:fit、transform、fit_transform。说白了,fit 学数据、transform 做映射,fit_transform 一步到位。但什么时候拆开用?在交叉验证或上线推理时,必须先用训练集 fit,再用同样的变换去 transform 测试集。
fit_transform,然后做交叉验证。结果训练集和验证集的 PCA 映射混在一起,严重过拟合。后来才意识到,必须只在训练集上 fit,再分别 transform 训练集和验证集。
先看基本用法:
from sklearn.decomposition import PCA
import numpy as np
# 模拟数据:100个样本,每个样本5维
X = np.random.randn(100, 5)
# 降维到2维
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X)
print(X_pca.shape) # 输出 (100, 2)
你想想看,这里 n_components 可以传整数,也可以传浮点数(比如 0.95),表示保留 95% 的方差。这个参数在实际项目中特别有用——你不知道要保留多少维?那就定一个阈值,让算法自己算。
4.2 关键属性与方法
模型 fit 完之后,会生成几个重要属性。我把它整理成一个表格,方便你对照查。
| 属性/方法 | 含义 | 实践建议 |
|---|---|---|
explained_variance_ratio_ | 每个主成分解释的方差占比 | 画累计方差图时直接用这个 |
components_ | 主成分向量(载荷矩阵,形状 [n_components, n_features]) | 分析特征重要性时看这个 |
mean_ | 训练集各特征的均值 | 用于手动还原原始数据 |
singular_values_ | 奇异值(SVD分解得到) | 可用来判断矩阵的秩 |
n_features_in_ | 输入特征数 | 做 pipeline 时常用 |
inverse_transform(X_pca) | 从低维空间映射回原始空间 | 做重构误差分析时用 |
explained_variance_ratio_,看看前几个主成分能解释多少信息。如果前两个加起来不到 70%,我会考虑增加 n_components 或者换 t-SNE 试试。
4.3 实战案例:手写数字降维
咱们用经典的 MNIST 数据集跑一遍。代码不长,但内涵很丰富——包括数据标准化、降维、结果可视化。
from sklearn.datasets import load_digits
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt
digits = load_digits()
X, y = digits.data, digits.target
# 标准化很重要!PCA对尺度敏感
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)
# 可视化
plt.figure(figsize=(10, 8))
scatter = plt.scatter(X_pca[:,0], X_pca[:,1], c=y, cmap='tab10', alpha=0.7)
plt.colorbar(scatter)
plt.xlabel('PC1')
plt.ylabel('PC2')
plt.title('Digits - PCA 2D')
plt.show()
这里有个细节:为什么要标准化?因为 PCA 是基于协方差矩阵的,如果某个特征的数值范围比其他特征大很多(比如像素值0-255 vs 长宽比0-1),那它就会主导主成分方向。我在项目中处理金融数据时吃过这个亏——交易量动辄几百万,而收益率只有百分之几,不标准化的话第一主成分几乎就是交易量本身。
4.4 如何选择主成分数量
最常见的办法就是看累计方差解释率。我通常会画一个阶梯图,然后选拐点或者满足阈值的维度。
pca_full = PCA().fit(X_scaled)
cumulative = np.cumsum(pca_full.explained_variance_ratio_)
plt.plot(range(1, len(cumulative)+1), cumulative, 'bo-')
plt.axhline(y=0.95, color='r', linestyle='--')
plt.xlabel('Number of Components')
plt.ylabel('Cumulative Explained Variance')
plt.title('PCA Variance Ratio')
plt.show()
你看,红线是 95% 的阈值。一般实际项目中我会保留到 90%~95%,这样既压缩了维度又不损失太多信息。但也要具体问题具体分析——如果下游模型很复杂(比如深度神经网络),可以少保留一些,相当于做一次去噪。
4.5 本章核心流程
把整个 PCA 的 Python 实现串起来,就是下面这张图。嗯,照着走基本不会出错。
4.6 常见问题与避坑
- 数据泄露:千万不要对整个数据集先降维再划分。一定要在训练集上 fit,再 transform 训练集和测试集。
- 稀疏数据:PCA 对稀疏矩阵不友好,它会破坏稀疏结构。我建议先用 TruncatedSVD(专门处理稀疏矩阵)代替。
- 特征数量远大于样本数:这种情况下协方差矩阵的秩有限,主成分最多只能有 min(n_samples, n_features) 个。可以先做特征筛选。
- 可视化误判:PCA 降维到 2 维后,看到的分布可能完全失真(比如环形结构被拉直)。所以可视化结果只能当参考,不要过度解读。
IterativeImputer 做更精细的处理。不要跳过这步。
好了,PCA 的实现部分就到这里。代码不算复杂,但每一步都有它的道理。你在实战中遇到问题时,回头看看这张流程表和表格,应该能定位到大部分坑位。