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全流程

下面这张流程图画了从原始数据到降维结果的完整路径,每个方块对应一个步骤,箭头表示数据流动方向。

PCA 算法四步流程 原始数据 X n×p 矩阵 数据标准化 z = (x-μ)/σ 协方差矩阵 Σ p×p 对称矩阵 特征分解 λ, v 排序取前k 投影 Y = X·W n×k 降维结果 核心原则 保留方差最大的方向

4.6 几个常被问的细节

问题回答
为什么用协方差矩阵而不是相关矩阵?标准化后等价。如果没标准化,直接算协方差矩阵会被量纲带偏。
特征向量为什么是正交的?协方差矩阵是对称矩阵,对称矩阵的特征向量必然正交。这样主成分之间不相关。
k怎么选?看累计方差贡献率。一般选到85%~95%。或者用碎石图找拐点。
特征值有虚数怎么办?理论上协方差矩阵是半正定的,特征值全为实非负。如果出现复数,检查数据是否有NaN或计算精度问题。

经验之谈:我常用np.linalg.svd代替特征分解——SVD数值更稳定,而且可以直接得到主成分得分。但原理是一样的。你掌握了这里的四步,后面用sklearn的PCA类就只是调参问题了。

4.7 小结

PCA的四步走:标准化让数据对齐→协方差矩阵量化关联→特征分解找出主方向→投影完成降维。每一步背后都有矩阵运算撑着,但用代码实现其实就几行。嗯……别忘了标准化,这是我踩过最大的坑。