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)从低维空间映射回原始空间做重构误差分析时用
我个人习惯在 fit 之后立刻打印 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),那它就会主导主成分方向。我在项目中处理金融数据时吃过这个亏——交易量动辄几百万,而收益率只有百分之几,不标准化的话第一主成分几乎就是交易量本身。

关键结论:标准化是 PCA 的前提操作,但不是死的。如果所有特征量纲相同(如图像像素值),也可以不标准化。不过大多数情况下,标准化不会有坏处。

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 实现串起来,就是下面这张图。嗯,照着走基本不会出错。

PCA 降维实现流程 原始数据 X (n×m) StandardScaler pca.fit(X_scaled) 选择主成分个数 X_pca = pca.transform() explained_variance_ratio_ 可视化 / 机器学习模型 注意:标准化和主成分数选择是两个最容易出错的点,务必在验证集上独立 做 fit 和 transform。

4.6 常见问题与避坑

  • 数据泄露:千万不要对整个数据集先降维再划分。一定要在训练集上 fit,再 transform 训练集和测试集。
  • 稀疏数据:PCA 对稀疏矩阵不友好,它会破坏稀疏结构。我建议先用 TruncatedSVD(专门处理稀疏矩阵)代替。
  • 特征数量远大于样本数:这种情况下协方差矩阵的秩有限,主成分最多只能有 min(n_samples, n_features) 个。可以先做特征筛选。
  • 可视化误判:PCA 降维到 2 维后,看到的分布可能完全失真(比如环形结构被拉直)。所以可视化结果只能当参考,不要过度解读。
如果数据有缺失值,PCA 会直接报错。我习惯先填充(如用均值或中位数),或者用 IterativeImputer 做更精细的处理。不要跳过这步。

好了,PCA 的实现部分就到这里。代码不算复杂,但每一步都有它的道理。你在实战中遇到问题时,回头看看这张流程表和表格,应该能定位到大部分坑位。