4. PCA的算法步骤:从原始数据到降维结果
PCA的核心思想,说白了就是把高维数据映射到方差最大的几个方向上。你想想看,数据在某个方向上波动越大,保留的信息就越多。丢掉的都是那些“没啥动静”的方向——这就是降维的本质。
具体怎么做?我把它拆成四大步:标准化、协方差矩阵、特征分解、投影。咱们一步步来。
4.1 数据标准化——先给数据“对齐”一把尺
这一步千万别省。我在项目里见过有人直接把原始数据扔进PCA,结果第一主成分完全被量纲大的特征绑架了——比如有的特征取值0~1,有的几万,PCA直接跑去拟合那个大数值的维度。
标准化就是把每个特征变成均值为0、标准差为1。公式很简单:
z = (x - μ) / σ
其中μ是特征的均值,σ是标准差。搞完以后所有特征都在同一量级上说话。
避坑提醒:我曾经在客户的数据集上忘了标准化,协方差矩阵里最大特征值对应的特征向量全是单位不一致的变量……最后降维结果根本没法解释。从那以后,标准化成了我的肌肉记忆。
Python里做这件事一行搞定:
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
4.2 协方差矩阵计算——找变量间的关系
标准化之后,我们要算协方差矩阵。它告诉我们每两个特征之间是正相关、负相关还是压根不相关。矩阵的对角线是每个特征的方差,非对角线是协方差。
数学形式:
Σ = (1/(n-1)) · X_scaledᵀ · X_scaled
这是个对称矩阵,大小是p×p(p是特征数)。我习惯用NumPy来算:
import numpy as np
cov_matrix = np.cov(X_scaled, rowvar=False)
注意rowvar=False——这个参数容易被忽略。默认是行代表变量,而我们数据是每列一个特征,所以要关掉。
关键点:协方差矩阵里绝对值大的元素表示那两个特征强相关。PCA会把这些相关性“打包”到同一主成分里。这也是为什么降维后能保留大部分信息的原因。
4.3 特征分解——找到“主方向”
这一步是PCA的心脏。我们对协方差矩阵做特征分解,得到特征值和对应的特征向量。
特征值代表这个方向上方差的大小。特征向量就是那个方向本身——也就是主成分的轴线。
数学上:
Σ · v = λ · v
其中v是特征向量,λ是特征值。我们把特征值从大到小排序,取前k个最大的,就是我们要保留的k个主成分。
用NumPy实现:
eigenvalues, eigenvectors = np.linalg.eig(cov_matrix)
# 排序:按特征值降序
idx = np.argsort(eigenvalues)[::-1]
eigenvalues_sorted = eigenvalues[idx]
eigenvectors_sorted = eigenvectors[:, idx]
个人习惯:我一般先看一眼特征值的“肘部图”——横轴是主成分序号,纵轴是特征值。曲线突然变平的地方就是合适的k值。这叫“碎石图法”,简单实用。
4.4 降维投影——把数据“拍”到新坐标轴
取前k个特征向量组成投影矩阵W(p×k),然后用原始标准化数据乘以W,得到降维后的数据Y(n×k)。
W = eigenvectors_sorted[:, :k]
Y = X_scaled @ W
就这么简单。Y的每一列是一个主成分得分,每一行是一个样本在新坐标系下的坐标。
注意:投影一定要用标准化后的数据,千万别用原始数据。我见过一个同学直接拿原始数据和特征向量相乘,结果主成分完全被量纲干扰——他折腾了两个小时才发现问题。
完整的PCA代码(不依赖sklearn):
def pca_manual(X, k):
# 标准化
mean = np.mean(X, axis=0)
std = np.std(X, axis=0)
X_scaled = (X - mean) / std
# 协方差矩阵
cov = np.cov(X_scaled, rowvar=False)
# 特征分解
eig_vals, eig_vecs = np.linalg.eig(cov)
idx = np.argsort(eig_vals)[::-1]
eig_vals = eig_vals[idx]
eig_vecs = eig_vecs[:, idx]
# 投影
W = eig_vecs[:, :k]
Y = X_scaled @ W
return Y, eig_vals, W
4.5 一张图理清PCA全流程
下面这张流程图画了从原始数据到降维结果的完整路径,每个方块对应一个步骤,箭头表示数据流动方向。
4.6 几个常被问的细节
| 问题 | 回答 |
|---|---|
| 为什么用协方差矩阵而不是相关矩阵? | 标准化后等价。如果没标准化,直接算协方差矩阵会被量纲带偏。 |
| 特征向量为什么是正交的? | 协方差矩阵是对称矩阵,对称矩阵的特征向量必然正交。这样主成分之间不相关。 |
| k怎么选? | 看累计方差贡献率。一般选到85%~95%。或者用碎石图找拐点。 |
| 特征值有虚数怎么办? | 理论上协方差矩阵是半正定的,特征值全为实非负。如果出现复数,检查数据是否有NaN或计算精度问题。 |
经验之谈:我常用np.linalg.svd代替特征分解——SVD数值更稳定,而且可以直接得到主成分得分。但原理是一样的。你掌握了这里的四步,后面用sklearn的PCA类就只是调参问题了。
4.7 小结
PCA的四步走:标准化让数据对齐→协方差矩阵量化关联→特征分解找出主方向→投影完成降维。每一步背后都有矩阵运算撑着,但用代码实现其实就几行。嗯……别忘了标准化,这是我踩过最大的坑。