降维这个主题,我在学的时候绕了不少弯路。课本上堆了一堆公式,但没人告诉我这些公式到底在干什么。后来自己动手推了一遍,又把主成分分析(PCA)、奇异值分解(SVD)和线性判别分析(LDA)放在一起对照,才慢慢摸到门道。
这篇文章就把我自己推导降维的完整过程整理出来,重点讲清楚每个公式背后的几何直觉、每一步推导的理由,以及实际用的时候那些没人提醒你的坑。适合已经学过线性代数和概率论基础、但被“降维”这个概念卡住的人,也适合想系统复习PCA之后再看核方法和流形学习的同学。
1. 降维问题的本质:数据冗余与信息保持的平衡
1.1 为什么高维数据让人头疼
我在实际项目中第一次被高维数据坑到,是在处理一个图像分类任务。每张图片是28乘28像素,展平之后是784个特征。对于几千条样本来说,这个维度并不算特别高,但训练出来的模型非常不稳定:稍微调整一下训练集,模型的表现就剧烈波动。后来分析特征矩阵的协方差结构,发现大量特征之间相关性极高,真正独立的信息维度远小于784。
这就是高维数据最典型的问题:特征之间高度冗余,真实的自由度远小于表面维度。除了冗余,还有另一个常被提到的问题——维度灾难。当维度增加时,数据在空间中的分布会变得越来越稀疏,样本之间的距离趋于相等,这对基于距离的算法(如K近邻)是灾难性的。
降维的目标,就是找到一个低维表示,让这个低维表示尽可能保留原始数据中的重要结构,同时去掉冗余信息和噪声。但“重要结构”怎么定义?这就引出了不同降维方法的分歧:PCA认为重要结构是方差最大的方向,LDA认为是最有利于分类的方向,流形学习认为是数据所在低维流形的内在坐标。
1.2 从几何视角看降维的三个基本问题
任何降维方法都要回答三个问题:
第一,投影方向怎么选。PCA选了最大化方差的轴;LDA选了类间散度与类内散度比值最大的轴。方向的选择直接决定了降维后保留下来的信息是什么。
第二,降到什么维度。这通常靠累计解释方差比来定,但也需要结合具体任务调。我见过一个项目把维度从1000压到3做三维可视化,信息保留率不到40%,但聚类结构反而更清晰了——这说明有些场景下游任务比方差更重要。
第三,降维之后的数据怎么解释。PCA的主成分是原始特征的线性组合,如果原始特征有明确物理含义(比如流量、温度、压力),那主成分往往难以直接解释。这点在工业场景经常被质疑,后面我会详细说。
1.3 线性假设与非线性数据的矛盾
经典的PCA、SVD、LDA都是线性降维方法,它们的共同假设是:数据大致分布在一个线性子空间附近。但现实中大量数据并非如此。
举一个直观的例子:一张人脸在不同光照下的照片,分布在像素空间里其实是一个近似圆锥形的流形,而不是一个平面。用线性PCA去压这个数据,效果会很差,因为它找不到一个线性平面来近似这个弯曲的流形。
这个矛盾推动了一系列非线性降维方法的发展。但学习非线性方法之前,必须先把线性方法吃透,因为核PCA的推导就是在线性PCA的框架里加了一个映射函数,流形方法里的局部线性嵌入(LLE)也大量借用了PCA的局部化思想。这篇笔记的核心是线性方法,但我会专门说明非线性扩展的入口在哪。
2. PCA主成分分析:从最大投影方差到特征值分解的完整推导
2.1 中心化与协方差矩阵的来源
PCA的第一步是中心化。给定数据矩阵
[ X = \begin{bmatrix} x_1^T \ x_2^T \ \vdots \ x_N^T \end{bmatrix} \in \mathbb{R}^{N \times p} ]
每一行是一个样本,每个样本有p个特征。计算均值向量
[ \bar{x} = \frac{1}{N} \sum_{i=1}^{N} x_i ]
然后构造中心化矩阵
[ X_c = X - \mathbf{1}_N \bar{x}^T ]
中心化看起来是个不起眼的操作,但它非常关键。协方差矩阵的定义是
[ \Sigma = \frac{1}{N} \sum_{i=1}^{N} (x_i - \bar{x})(x_i - \bar{x})^T = \frac{1}{N} X_c^T X_c ]
如果不做中心化,那相当于假设数据均值在原点,或者把原点本身当成参考点。这在数据均值偏离原点很远的场景下,会严重扭曲方差结构。我在一个传感器数据分析任务中做过对比:没有中心化直接做PCA,第一主成分几乎就是数据的均值方向,而不是变化最大的方向,这就是中心化缺失的典型症状。
2.2 投影后方差的展开式
现在假设我们要把数据投影到单位方向向量 ( u ) 上(要求 ( u^T u = 1 ) 是为了让投影长度有明确的几何意义,也避免方向向量伸缩导致方差无限大)。样本 ( x_i ) 在 ( u ) 方向上的投影坐标是标量 ( x_i^T u ),所有样本投影后的方差是
[ \frac{1}{N} \sum_{i=1}^{N} (x_i^T u - \bar{x}^T u)^2 ]
把均值提出来,上式等价于
[ u^T \left[ \frac{1}{N} \sum_{i=1}^{N} (x_i - \bar{x})(x_i - \bar{x})^T \right] u = u^T \Sigma u ]
所以PCA要解决的优化问题就是
[ \max_{u} ; u^T \Sigma u \quad \text{s.t.} \quad u^T u = 1 ]
这个过程证明了“最大化投影方差”和“最大化 ( u^T \Sigma u )”是同一件事。
2.3 拉格朗日乘子法推导特征方程
带约束的优化问题用拉格朗日乘子法。构造拉格朗日函数
[ L(u, \lambda) = u^T \Sigma u + \lambda (1 - u^T u) ]
对向量 ( u ) 求偏导并令其为零:
[ \frac{\partial L}{\partial u} = 2\Sigma u - 2\lambda u = 0 ]
约掉2,得到
[ \Sigma u = \lambda u ]
这正好是特征值方程。也就是说,协方差矩阵的特征向量就是我们要找的投影方向,对应的特征值就是该方向上的投影方差。
把 ( \Sigma u = \lambda u ) 两边同时左乘 ( u^T ),得到
[ u^T \Sigma u = \lambda ]
右边就是投影方差,所以特征值直接等于该主成分解释的方差大小。特征值越大,这个方向保留的信息越多。对所有特征值排序,取前k个最大的特征值对应的特征向量,组成投影矩阵 ( W = [u_1, u_2, \ldots, u_k] ),降维后的数据就是
[ Z = X_c W ]
这里有个容易绕晕的细节:( X_c ) 是 ( N \times p ),( W ) 是 ( p \times k ),乘完是 ( N \times k )。每一行是降维后的样本,每一列是一个主成分。
2.4 为什么特征向量彼此正交且能消除冗余
实对称矩阵的谱定理保证了协方差矩阵 ( \Sigma ) 可以被正交对角化,即不同特征值对应的特征向量是正交的。这意味着PCA找到的各个主成分方向互相垂直,投影后的各维度之间线性无关。
换句话说,原始数据里那些线性相关的冗余特征,经过PCA投影之后,在新的坐标系里被解耦了。这是PCA能够“去相关”的数学基础。在实际应用中,这带来的直接收益是:降维后的特征可以放心地喂给做距离计算的算法(比如高斯核的SVM),因为这些特征的各维已经不再互相干扰。
3. SVD视角:一种更稳定也更通用的求解路径
3.1 从特征分解到奇异值分解
PCA的经典推导用的是协方差矩阵的特征值分解,但在实际计算中,我几乎不用特征分解直接算PCA,而是用SVD。原因有两个:数值稳定性更好,而且能直接处理“样本数远小于特征数”的高维场景。
SVD说的是:任意矩阵 ( X_c ) 总能分解成
[ X_c = U S V^T ]
其中 ( U ) 是 ( N \times N ) 的正交矩阵,( V ) 是 ( p \times p ) 的正交矩阵,( S ) 是 ( N \times p ) 的对角矩阵(对角线上是奇异值,非负且降序排列)。
3.2 SVD和PCA的数学桥接
关键在下面这个推导。考虑
[ X_c^T X_c = (U S V^T)^T (U S V^T) = V S^T U^T U S V^T = V S^T S V^T ]
因为 ( U ) 是正交矩阵所以 ( U^T U = I )。这里 ( S^T S ) 是一个 ( p \times p ) 对角矩阵,对角元素是奇异值的平方。
而协方差矩阵(乘以N之后)恰好是
[ X_c^T X_c = V S^T S V^T ]
这正是 ( X_c^T X_c ) 的特征值分解形式——特征向量矩阵是 ( V ),特征值是 ( S^T S ) 的对角元素。所以结论是:
PCA 里的特征向量就是数据矩阵 ( X_c ) 的右奇异向量 ( V ),特征值就是奇异值的平方除以样本数。
实际计算时,直接对 ( X_c ) 做SVD,得到右奇异向量 ( V ),然后取前 ( k ) 列作为投影方向。这样就避免了先构造 ( p \times p ) 的协方差矩阵再算特征分解的步骤。当特征数 p 很大时(比如几万维的图像数据),构造协方差矩阵本身就需要巨大的内存,而SVD可以直接作用在原始数据矩阵上,节省不少开销。
3.3 低秩矩阵分解与降维的关系
SVD还给出了一个更深刻的视角:PCA本质上是在做低秩近似。
假设原始数据矩阵 ( X_c ) 的秩为 ( r ),SVD分解为 ( U S V^T ),我们只保留前k个最大的奇异值,把其余的置零,得到
[ \tilde{X} = U_{:,1:k} S_{1:k,1:k} V_{:,1:k}^T ]
这个 ( \tilde{X} ) 是所有秩不超过k的矩阵中,与 ( X_c ) 的弗罗贝尼乌斯范数误差最小的那个——这就是经典的低秩近似定理(Eckart-Young定理)说的内容。
这个视角之所以有用,是因为它把降维从“选方向投影”变成了“用低秩矩阵去近似原数据”。在推荐系统里,用户-物品评分矩阵往往是个很大的稀疏矩阵,直接做PCA不现实,但用SVD做低秩分解来填补缺失值,就是矩阵分解推荐算法的数学基础。理解了这个,后面学矩阵分解类方法会顺很多。
3.4 什么时候用特征分解,什么时候用SVD
特征分解和SVD不是互斥的,它们的适用场景有明显区别。
如果数据矩阵是方阵且维度不高(比如几百乘几百的协方差矩阵),特征分解很快;但如果你要的是原始数据的主成分方向,直接用SVD一步到位更稳妥。还有一个细节是协方差矩阵可能是半正定的,数值上会出现接近于零的负特征值——这是因为浮点运算的误差导致的。用SVD得到的奇异值永远是准确的非负值,可以天然规避这个问题。
还有个工程上的建议:如果你用的是Python的numpy,np.linalg.svd返回的Vt是按行排列的,取前k个主成分方向要取Vt[:k].T,别取反了,这个细节我很长一段时间都是靠试错记下来的。
4. 白板推导中的关键细节:标准化、特征值符号与累计方差比
4.1 标准化到底该不该做
这是实践中第一个该想清楚的问题。PCA对特征的尺度极其敏感——因为它在最大化方差,而方差本身受量纲影响。
举个例子:某个数据集包含两个特征,第一个特征取值范围在0到1之间,第二个特征取值范围在0到1000之间。PCA会认为第二个特征的方差“大”,从而把主成分方向几乎完全偏向它,但这不代表第二个特征真的“更重要”,只是单位不同。
所以要不要标准化,取决于你对数据的理解。如果所有特征单位一致、量纲可比(比如都是像素灰度值),可以不标准化;如果特征来源不同、单位混杂(比如一个数据集里同时有温度、压力、流量),强烈建议先做标准化(z-score),让每个特征变成零均值单位方差。否则PCA的结果会因为某个特征的数值范围被主导。
但标准化也有代价:标准化之后,PCA做的是“相关矩阵”的谱分解,而不是“协方差矩阵”的谱分解,两者得到的主成分方向可能差别很大。在基因表达数据这类特征同源但异方差的场景中,这一点格外明显。我个人的建议是先做EDA,看特征分布,再决定。
4.2 特征值的符号、排序与degeneracy问题
特征分解得到的特征值可能是正、负或零。对PCA来说,我们关心的是正特征值,因为负特征值通常意味着数值误差。排序默认降序,前k个特征值对应的特征向量就是主成分方向。
这里要小心一种情况:当数据存在对称性或者某些方向上的方差几乎相等时,对应的特征值会很接近,特征向量会变得不稳定。也就是说,特征向量的方向可能有较大的估计误差。这时候如果强行取前几个主成分,得到的结论可能不稳健。我处理的方法是:对特征值做个折线图,看有没有明显的层级结构;如果前几个特征值之间没有明显断崖,就要警惕这个结果是否可靠。
还有一个小坑:某些库返回的特征向量列方向是任意的(特征向量乘以-1仍然是特征向量)。在做PCA投影时,方向正负不影响重建误差,但如果要解释主成分的物理含义,就得注意符号。比如第一主成分在某个特征上的载荷是正还是负,会直接影响业务解释。
4.3 累计解释方差比怎么用才合理
累计解释方差比是指前k个特征值之和占总特征值之和的比例:
[ \text{ratio}(k) = \frac{\sum_{i=1}^{k} \lambda_i}{\sum_{i=1}^{p} \lambda_i} ]
这个指标衡量前k个主成分保留了多少“总方差”。常见的做法是取累计方差比达到80%或90%的k。但我想提醒的是:这个指标只是参考,不是硬性标准。
我在一个工业项目里把累计方差比从0.9一路降到0.6,但下游分类模型的F1反而提升了两个百分点。原因很简单:被丢掉的那部分“方差”里大部分是噪声,保留它们等于把噪声也喂给了模型。所以选k的时候,最好的办法是直接看下游任务指标的曲线变化,而不是只盯着方差比。
4.4 随机化SVD与大矩阵处理
当数据矩阵特别大(几万乘几万甚至更大)时,精确SVD的计算量是吃不消的。此时可以用随机化SVD:先对原矩阵做随机投影得到一个小得多的矩阵,再在低维空间里做SVD,最后变换回来。这个思路在sklearn的TruncatedSVD里已经实现了,处理稀疏矩阵时尤为高效。
这类随机化算法的误差通常可以控制得很小,在我处理过的几十万行、几千列的数据集上,随机化SVD得到的奇异值和精确SVD的前几十个奇异值几乎一致。这个技巧对实际项目非常重要,因为很多问题根本轮不到调算法,数据都装不进内存就已经卡死了。
5. 核PCA与流形方法:线性降维扩展的两条路径
5.1 核PCA的直觉与推导入口
线性PCA解决不了非线性结构的数据。核PCA的思路是:先把数据通过某个非线性映射 ( \phi(x) ) 送到高维(甚至无穷维)的特征空间,在这个特征空间里做线性PCA,再想办法用核函数隐式地计算内积。
听起来有点绕,但推导的核心在于:PCA其实只需要内积。看一下PCA的目标函数和求解过程——最终要靠 ( \Sigma u = \lambda u ) 解特征向量,而 ( \Sigma ) 的每个元素都是数据内积的形式;特征空间里样本的内积 ( \langle \phi(x_i), \phi(x_j) \rangle ) 可以直接用核函数 ( k(x_i, x_j) ) 代替,不需要显式知道 ( \phi ) 到底是什么函数。
具体推导时,核心是构造核矩阵
[ K_{ij} = k(x_i, x_j) ]
然后对K做中心化和特征分解,得到的特征向量再组合成原始空间中样本的“投影坐标”。这就是核PCA的基本框架。
5.2 核函数的选择与常见误区
核PCA的效果严重依赖核函数的选择。高斯径向基核(RBF)是默认选择,但它的带宽参数影响极大:带宽太小,每个样本都只“看见”自己,核矩阵退化到单位矩阵,降维结果没有意义;带宽太大,所有样本的相似度趋于一致,同样没有区分度。我在实际使用中会用交叉验证来选带宽,而不是随便设一个默认值。
还有一个常见误区:核PCA之后的数据不能直接用来解释“主成分”的物理含义。因为在特征空间里的方向向量是特征向量的线性组合,映射回原始空间通常不是简单的线性组合。换句话说,核PCA适合做可视化、做预处理,但别指望能像线性PCA那样解读每个主成分的语义。
5.3 流形方法对PCA的修正
流形方法(如局部线性嵌入LLE、t-SNE、UMAP)走的是另一条路:它们假设数据分布在一个低维流形上,降维的目标是找到这个流形的内在坐标。这些方法跟PCA的数学框架完全不同,但思想上有延续——它们都在跟“局部结构”打交道。
以LLE为例:它先为每个样本找到近邻,用近邻的线性组合重构该样本,然后在高维空间里求重构权重,最后在低维空间里保持这些权重不变,解一个特征值问题。这个过程中用到的特征值分解,和PCA的特征值分解是同一类数学工具,只是矩阵不同。
我自己对非线性方法的建议是:先做一次PCA看结果,如果数据本身的线性结构已经能解释大部分变异,就别急着上非线性方法。非线性方法参数多、可解释性差、计算量大,不是万金油。只有线性方法确实解释不了数据分布时,才值得尝试。
6. LDA与PCA的对比:监督降维的不同侧重点
6.1 LDA的目标函数推导
LDA(线性判别分析)和PCA最大的不同是:PCA无监督,只看方差;LDA有监督,要看类别。
假设数据分成c类,定义类内散度矩阵
[ S_w = \sum_{i=1}^{c} \sum_{x \in C_i} (x - \mu_i)(x - \mu_i)^T ]
和类间散度矩阵
[ S_b = \sum_{i=1}^{c} n_i (\mu_i - \mu)(\mu_i - \mu)^T ]
其中 ( \mu_i ) 是第i类的均值,( \mu ) 是全局均值,( n_i ) 是第i类样本数。LDA要找一个投影方向 ( w ),使得投影后类间散度尽量大、类内散度尽量小,目标函数是
[ J(w) = \frac{w^T S_b w}{w^T S_w w} ]
这是一个广义瑞利商问题,最优解可以通过求解广义特征值问题 ( S_b w = \lambda S_w w ) 得到。
6.2 二分类与多分类的维度限制
在二分类问题时,( S_b ) 的秩至多是1,所以LDA只会得到一个有效的投影方向——无论原始特征有多少维,最后都要压到一维。多分类问题时,( S_b ) 的秩至多是 ( c-1 ),所以LDA最多能降到 ( c-1 ) 维。
这个限制在应用时经常被忽略。有人拿LDA做1000维特征的三分类任务,想把数据降到10维,结果发现LDA只能给出2个有效方向,剩下8个方向其实是没有意义的(数值上接近0)。这类情况就需要先降维再做LDA,或者考虑其他方法。从实操上看,LDA更适合“先用PCA把维度压到合理范围,再用LDA做判别分析”这种组合路径。
6.3 PCA和LDA到底该选哪个
我的经验是看问题定义:如果你根本不知道类别标签,或者只想保留数据的全局结构,选PCA;如果类别明确,且目标是分类,LDA往往能给出更有判别力的低维表示。
但LDA对数据分布有较强的假设:各类近似服从高斯分布,且各类协方差矩阵相同。如果这个假设严重不成立,LDA的效果会明显劣化。还有一个常见问题:样本数远小于特征数时(比如基因数据),( S_w ) 不可逆,LDA基本跑不动,这时要么先PCA降维,要么用正则化的LDA变体。
实际项目里,我喜欢把PCA和LDA的结果放在一起对比。PCA画出来往往是一个整体分布结构,LDA画出来则是类别的可分离结构。这种直观对比能帮你快速判断问题适合哪种降维思路。
7. 实操经验:降维过程中那些没人提醒你的坑
7.1 先看数据形状,再决定降维路线
拿到一个高维数据集,第一件要做的事不是直接跑PCA,而是看数据矩阵的形态:样本数和特征数分别是多少、有没有缺失值、特征量纲是否统一、是否有大量重复或近似重复的列。
我遇到过一个项目,特征矩阵里有两列几乎完全线性相关(相关系数0.9999),但列名不同,业务方不让我们删。PCA做出来的结果里,这两个特征在主成分上的载荷几乎抵消了,毫无解释力。后来跟业务方反复确认,这两列确实来自同一个测量的不同导出方式,删掉一列后模型稳定性立刻大幅提升。
这说明一个残酷的事实:PCA帮你找到的是统计学上的主方向,它不会告诉你哪些原始特征是冗余的。特征筛选要靠领域知识,降维只是一种数学手段。
7.2 奇异值分布图:比累计方差比更直观的工具
我不知道你在实际项目里是不是也一样,一开始总是盯着累计解释方差比这个数字看,但后来我发现了更直观的指标:把奇异值(或特征值)按降序画一张折线图。
如果这条曲线下降得非常快,说明数据集中在前几个主成分上,降维空间是“低维的”;如果曲线缓慢下降,说明数据在很多方向上都有相当的方差,没有明显的低维结构。对后者,强行降维往往会丢掉真实信息。这个“肘部法则”虽然粗糙,但比单个数字更能反映数据本质。
还有一个实践细节:当样本数N远小于特征数p时,协方差矩阵的秩最多是N-1,意味着最多只有N-1个非零特征值。累计解释方差比的分母要正确地设为“非零特征值之和”,而不是p个特征值之和,否则比例会虚高。
7.3 降维结果如何往下游任务交接
降维不是终点,下游任务才是。我见过不少人在训练集上做完PCA,然后忘了把PCA的投影矩阵存下来,测试集来了只能重新拟合——结果投影方向变了,数据分布也变了,模型表现自然惨不忍睹。
正确做法是:在训练集上拟合PCA,保存投影矩阵(或直接用sklearn的PCA对象),然后对训练集和测试集应用同一个投影变换。任何涉及统计量的预处理步骤(标准化、PCA、LDA)都必须只从训练集估计参数,再应用到测试集。这是防止数据泄露的基本要求,但在降维这个环节特别容易被忽视。
7.4 关于降维结果的解释
降维模型建完,大多数人最头疼的问题是:老板问“这个主成分是什么意思?”。线性PCA可以尝试从载荷(特征向量分量)的绝对值大小来解读,绝对值大说明原始特征对该主成分贡献大,但要小心符号。核PCA和流形方法基本无法这样解释,需要换一种思路。
我的做法是:对降维后的数据做聚类或可视化,然后回到原始特征空间里,对比不同簇在各特征上的均值差异,用业务语言描述差异。这样既绕开了“主成分解释难”的尴尬,也让结果在业务侧更容易被接受。
7.5 降维与其他算法结合时的常见错误
最后提一个我在评审代码时经常发现的错误:把PCA当作万能预处理,不假思索地套在每一个算法前面。降维确实能减少计算量、去相关、降噪,但也会丢失信息,甚至破坏某些算法的判据。比如决策树和随机森林这类基于阈值的模型,对特征做了正交变换后,可解释性和分裂点含义都会被扭曲。
我的习惯是:先用PCA结果做可视化、做噪音过滤,再尝试直接用原始特征训练一个baseline模型,对比降维前后的效果。只有当降维带来的收益(速度、稳定性、精度)足够明显时,才把PCA固定到pipeline里。无脑降维,跟无脑不降维,本质上是一样的偷懒。
降维这个主题,从PCA推导到SVD,再到LDA和非线性扩展,每一个环节都贯穿着同样的提问方式:什么样的方向值得保留?这个方向由什么决定?把这个问题想清楚,PCA不再是一个“调用一下fit_transform”的黑盒,而是真正属于自己的工具。