【老计带你懂线性代数】10:特征分解与PCA,用线代做降维
开头:把降维讲到根上
在《老计带你懂AI算法》和《老计带你懂概率统计》里,我们都讲过PCA降维,但都是从"效果"层面讲的,找方差最大的方向、留住信息、扔掉冗余。这一篇,我们从线性代数的根上把PCA彻底讲透,你会看到它其实就是上一篇特征值特征向量的一次漂亮应用。
读完这篇,PCA对你就再无黑箱。你会明白它为什么能找到主方向、那些主成分到底是什么、为什么它用的是协方差矩阵的特征向量。这是三个系列在降维这个点上的一次彻底会师。
特征分解:把矩阵拆成它的本质方向
先讲特征分解。上一篇我们找到了一个矩阵的特征值和特征向量,特征分解就是把矩阵用它的特征向量和特征值重新表示出来。
直觉上,特征分解在说:一个矩阵所做的复杂变换,可以拆解成三步,先转到特征向量的坐标系、在各个方向上按特征值纯缩放、再转回来。换句话说,任何一个(可分解的)变换,骨子里都是"换个角度看,就是几个方向上的简单拉伸"。特征分解就是把这个本质显式地拆出来。
打个比方:一个复杂的舞步(变换),特征分解告诉你,其实它等于先转个身面向几个特定方向、在每个方向上迈固定步长、再转回来。再花哨的动作,拆开都是几个主方向上的简单移动。特征分解就是这套拆解说明书。
特征分解的意义在于,它让我们能从最本质的方向去理解和操作矩阵。而它最重要的应用之一,就是PCA。
PCA的第一步:用协方差矩阵描述数据怎么散布
现在讲PCA。要理解它,先得有一个东西,协方差矩阵,它描述一堆数据是怎么散布的。
回忆一下:一堆数据点散在空间里,形成某种形状(比如一个拉长的椭圆)。协方差矩阵就是用一个矩阵,精确地刻画这堆数据在各个方向上散得开不开、以及不同特征之间怎么一起变化(比如身高和体重是不是一起增长)。它抓住了数据分布的形状。
关键点来了:这个协方差矩阵是对称的(上一篇末尾专门铺垫过),所以它的特征向量互相垂直、特征值都是实数,天然适合当一组新坐标轴。
PCA的核心:主成分就是协方差矩阵的特征向量
这是全篇的核心,一句话说透:对数据的协方差矩阵做特征分解,得到的特征向量就是主成分(数据的主方向),对应的特征值就是数据在那个方向上的方差(散布程度、信息量)。
把这句话拆开体会:
- 上一篇说过,特征值最大的特征向量,是变换拉伸最狠的方向。放到协方差矩阵上,特征值最大的特征向量,就是数据方差最大、散得最开、信息最多的方向,这正是第一主成分。
- 第二大特征值对应的特征向量,是与第一主成分垂直的、次要的方向,就是第二主成分。以此类推。
- 降维,就是只保留特征值最大的前几个特征向量(主成分),把数据投影到它们张成的低维空间上。大特征值方向信息多、留下,小特征值方向信息少、舍弃。
所以PCA的全流程,用线代语言说就是:算协方差矩阵、对它做特征分解、按特征值从大到小排特征向量、取前k个作为新坐标轴、把数据投影上去。前两个系列讲的"找方差最大的方向、留住信息",在这里落到了实处,方差最大的方向,就是协方差矩阵最大特征值对应的特征向量。降维这件事,被特征值特征向量从根上解释清楚了。
打个比方:PCA像给一群站成椭圆形的人重新安排坐标。它先看清人群散布的形状(协方差矩阵),找出人群拉得最开的那条主轴(最大特征向量)和次轴,然后说,与其用东南西北记录每个人,不如就用沿主轴的位置来记录,因为人群主要就是沿这条轴散开的。只记主轴位置,就用一个数概括了每个人的大致位置,这就是降到一维。
为什么留方差大的方向,是保留信息
有个问题值得停下来想清楚:PCA为什么留方差大的方向、扔方差小的方向,就等于保留了信息、丢掉的是无关紧要的?
关键在于,方差大意味着数据在这个方向上散得开、不同样本区分得明显;方差小意味着大家在这个方向上都差不多、挤成一团。一个方向如果所有样本取值都几乎一样(方差接近0),那它对区分样本毫无帮助,扔掉它几乎不损失任何区分能力。反过来,方向上散得越开,越能把不同样本拉开距离,这个方向就越有信息价值。
打个比方:你要用尽量少的信息描述一个班的学生。如果全班身高差不多(方差小)、但成绩差异巨大(方差大),那描述"成绩"远比描述"身高"有用,因为成绩能把学生区分开,身高几乎区分不了。PCA就是自动挑出那些最能把样本区分开的方向(方差大的),扔掉那些大家都雷同、区分不了的方向(方差小的)。这就是为什么保留大方差方向等于保留信息,这也和《老计带你懂概率统计》里讲方差衡量数据分散程度完全一脉相承。理解了这层,你就懂了PCA取舍的判断标准到底在判断什么。
输入和输出长什么样
- 输入:一份数据矩阵(样本乘特征),通常先做中心化(每个特征减去均值)。
- 输出:主成分(几个特征向量,即新坐标轴方向)、每个主成分的方差占比(特征值占比,表示留住了多少信息)、以及降维后的数据。
- NumPy可以手写全流程,sklearn有现成的PCA。
上代码:手写一遍PCA
# 依赖:pip install numpy scikit-learnimportnumpyasnpfromsklearn.datasetsimportload_iris# 加载鸢尾花数据:150朵花,每朵4个特征(4维)X,y=load_iris(return_X_y=True)print("原始数据形状:",X.shape)# (150, 4)# 第1步:中心化,每个特征减去它的均值X_centered=X-X.mean(axis=0)# 第2步:算协方差矩阵(描述数据怎么散布,是对称矩阵)cov=np.cov(X_centered,rowvar=False)# 4x4的协方差矩阵print("协方差矩阵形状:",cov.shape)# 第3步:对协方差矩阵做特征分解eigenvalues,eigenvectors=np.linalg.eigh(cov)# eigh用于对称矩阵# 第4步:按特征值从大到小排序(特征值大=方差大=信息多)idx=np.argsort(eigenvalues)[::-1]eigenvalues=eigenvalues[idx]eigenvectors=eigenvectors[:,idx]print("\n特征值(每个主成分的方差,从大到小):",np.round(eigenvalues,3))ratio=eigenvalues/eigenvalues.sum()print("各主成分信息占比:",np.round(ratio,3))print("前2个主成分累计留住信息:",round(ratio[:2].sum(),3))# 第5步:取前2个主成分(特征向量),把4维数据投影降到2维top2=eigenvectors[:,:2]X_pca=X_centered @ top2print("\n降维后形状:",X_pca.shape,"(从4维压到2维)")print("前3个样本降维后:\n",np.round(X_pca[:3],3))# 对比:用sklearn的PCA,结果应一致(验证我们手写的对)fromsklearn.decompositionimportPCA X_sklearn=PCA(n_components=2).fit_transform(X_centered)print("\nsklearn的PCA前3个样本:\n",np.round(X_sklearn[:3],3))print("和我们手写的一致(可能正负号相反,不影响,方向而已)")运行输出(示例):
原始数据形状: (150, 4) 协方差矩阵形状: (4, 4) 特征值(每个主成分的方差,从大到小): [4.228 0.243 0.078 0.024] 各主成分信息占比: [0.925 0.053 0.017 0.005] 前2个主成分累计留住信息: 0.978 降维后形状: (150, 2) (从4维压到2维) 前3个样本降维后: [[-2.684 0.319] [-2.714 -0.177] [-2.889 -0.145]] sklearn的PCA前3个样本: [[-2.684 0.319] [-2.714 -0.177] [-2.889 -0.145]] 和我们手写的一致(可能正负号相反,不影响,方向而已)运行你会看到:4维鸢尾花数据算出4乘4的协方差矩阵,特征分解后第一个特征值4.228占了92.5%的信息,前两个主成分累计留住97.8%的信息,于是把4维压到2维几乎不丢东西;我们手写的PCA结果和sklearn完全一致。整个流程就是上一篇特征分解的直接应用,协方差矩阵的特征向量就是主成分,这就是PCA的线代真身。
在AI里怎么用
- 降维与可视化:把高维数据压到2到3维画出来看,或降维后再喂给模型提速防过拟合,呼应《老计带你懂AI算法》PCA篇。
- 去噪与压缩:舍弃小特征值方向(往往是噪声),保留主成分,起到去噪和压缩作用。
- 人脸识别的特征脸:早期人脸识别把人脸做PCA,得到的主成分叫特征脸,是经典应用。
这里把特征脸多讲一句,因为它特别能体现PCA的威力和美感。一张人脸照片有成千上万个像素(高维向量),但把大量人脸做PCA后会发现,用几十到上百个主成分(特征脸),就能相当好地重建出任意一张人脸。每个主成分本身是一张模糊的、抽象的脸型模板(有的主成分反映胖瘦、有的反映光照、有的反映五官间距),任意一张真实人脸,都约等于这些模板脸按不同比例的加权组合。于是识别一个人,不用比对几万个像素,只用比对这几十个组合系数就行,又快又准。这就是降维的实际价值,把一张脸从几万维压缩成几十维,抓住了脸的本质、扔掉了冗余,还顺带去了噪。虽然如今人脸识别早已被深度学习取代,但特征脸这个思路,把抽象的PCA变成了看得见摸得着的东西,至今是理解降维的经典范例。
- 数据预处理:很多流程先PCA降维去相关,再做后续建模。
常见误区
- PCA前要先中心化(减均值),否则算出的方向会被数据的整体位置带偏。
- 主成分是新的组合特征、没有直观现实含义(第一主成分可能是"0.5身高加0.3体重加…"),这是降维换来的代价,呼应算法系列讲的。
- 特征值占比决定留几维:看累计信息占比(比如留住95%),来决定保留几个主成分。
- PCA只抓线性结构:数据是复杂非线性时,PCA力不从心,要用非线性降维(如t-SNE),这也和算法系列呼应。
小结与承上启下
- 特征分解:把矩阵拆成它的特征向量和特征值,本质是"换个角度看就是几个方向上的纯缩放"。
- 协方差矩阵:描述数据怎么散布,是对称矩阵,特征向量正交、特征值实数。
- PCA的线代真身:主成分就是协方差矩阵的特征向量,特征值就是那个方向的方差(信息量),降维就是留大特征值方向、投影上去。
- 打通三系列:前面讲的"找方差最大方向、留住信息",根子就是协方差矩阵的特征分解。
特征分解很强,但它只对方阵有效。现实里大量矩阵不是方阵(比如用户乘物品的评分表)。有没有一个更通用、任何矩阵都能拆的分解?有,它是线性代数皇冠上的明珠。下一篇讲SVD奇异值分解,看它怎么拆解任意矩阵,以及它在推荐、压缩、降维里的巨大威力。
我们下一篇见。
延伸阅读
- 3Blue1Brown《线性代数的本质》:特征分解与基变换:https://www.3blue1brown.com/topics/linear-algebra
- scikit-learn 官方文档:PCA:https://scikit-learn.org/stable/modules/generated/sklearn.decomposition.PCA.html
- NumPy 官方文档:对称矩阵特征分解np.linalg.eigh:https://numpy.org/doc/stable/reference/generated/numpy.linalg.eigh.html
(说明:以上为官方与经典公开资料地址,可能随版本调整,如打不开可用标题搜索。)