me.api / 数学

主成分分析 · PCA

如果只能留下一个数,应该留下什么?

一个问题,五站路。每站只回答一件事,配一个实验;推导和细节挂在支线上,想看再点开。

原稿 2024-08-24编排 2026.10.28看原稿 →

第 1 站 · 留一个数

平面上的点要两个数才能记下,能不能只记一个?

假设你记下了一个物体在桌面上的 8 个位置。每个位置要两个数:横着多远,竖着多远。

可这 8 个点大致排成了一条斜线。它们主要只沿一个方向变化,第二个数好像有点浪费。

试试这样:拿一把尺子,穿过这些点的中心放好,再把每个点垂直地压到尺子上。点落在尺子的哪个刻度,就记下哪个数。8 个点,8 个数,一个点只要一个。

8 个模拟点,平均位置 (100, 100) mm。尺子水平时,垂线平均长 7.5 mm;顺着点的走向(45°)时,平均只有 2.83 mm。启用 JavaScript 后可以转动尺子。

压上去的位置和原来不一样,中间那段垂线,就是只记一个数要付出的代价。转一转尺子:横着放,垂线很长;顺着点的走向放,垂线短得多。

一个数能记下大部分位置,丢掉的是那些垂线。尺子怎么放,决定了丢多少。

下一站 · 怎样算好:丢掉的部分,怎样才算丢得最少? →

第 2 站 · 怎样算好

丢掉的部分,怎样才算丢得最少?

上一站,每个点压到尺子上,都留下一段垂线。尺子换个角度,有的垂线变短,有的变长。要比较两种放法,得把 8 段垂线合成一个分数。

我们把每段垂线的长度平方,再求平均,叫它「丢掉的量」EE。为什么用平方?因为它和勾股定理配得上,下面马上就用到。

看任意一个点。它到中心的距离 rr 是一个直角三角形的斜边,两条直角边分别是它在尺子上的刻度 zz 和那段垂线 dd:

z2+d2=r2.z^2 + d^2 = r^2.

尺子怎么转,rr 都不变。所以留下的 z2z^2 和丢掉的 d2d^2 此消彼长:一边多一点,另一边就少一点。

尺子水平(0°)时,留下的平均 V = 66.5,丢掉的平均 E = 66.5;尺子 45° 时,V = 125,E = 8。两种情况都是 V + E = 133。启用 JavaScript 后可以逐点查看并拖动曲线。

对 8 个点都这样分账,再求平均:留下的平均 VV 加上丢掉的平均 EE,永远等于同一个总量 TT。这组数据里 T=133T=133,尺子放在 45° 时丢得最少,只丢 8。

还有一点:刻度 zz 的平均是 0(尺子穿过中心),所以 VV,也就是 z2z^2 的平均,正是这些刻度的方差。

推导 · 任意维数

上面用的是平面上的 8 个点。换成 nn 条记录、每条 dd 个数,道理一样,下面一步步写出来。

  1. 记第 ii 条记录为 xix_i,平均为 μ\mu,偏移 x~i=xi−μ\tilde x_i=x_i-\mu。尺子方向是单位向量 ww(wTw=1w^Tw=1)。刻度和垂线分别是

    zi=x~iTw,di=x~i−ziw.z_i=\tilde x_i^{T}w,\qquad d_i=\tilde x_i-z_iw.
  2. 垂线和尺子垂直:

    wTdi=wTx~i−zi wTw=zi−zi=0.w^{T}d_i=w^{T}\tilde x_i-z_i\,w^{T}w=z_i-z_i=0.
  3. 所以勾股定理成立,交叉项消失:

    ∥x~i∥2=∥ziw+di∥2=zi2+2zi wTdi+∥di∥2=zi2+∥di∥2.\begin{aligned}\|\tilde x_i\|^2&=\|z_iw+d_i\|^2\\&=z_i^2+2z_i\,w^{T}d_i+\|d_i\|^2\\&=z_i^2+\|d_i\|^2.\end{aligned}
  4. 对 nn 条记录求平均,得到留下的 VV、丢掉的 EE 和总量 TT:

    1n∑i∥x~i∥2⏟T=1n∑izi2⏟V(w)+1n∑i∥di∥2⏟E(w).\underbrace{\tfrac1n\textstyle\sum_i\|\tilde x_i\|^2}_{T}=\underbrace{\tfrac1n\textstyle\sum_i z_i^2}_{V(w)}+\underbrace{\tfrac1n\textstyle\sum_i\|d_i\|^2}_{E(w)}.

    TT 里没有 ww,尺子怎么转都不变。于是

    min⁡wE(w)  ⟺  max⁡wV(w).\min_{w}E(w)\iff\max_{w}V(w).
  5. 刻度的平均是 0,因为偏移的平均是 0:1n∑izi=(1n∑ix~i)Tw=0\tfrac1n\sum_i z_i=\big(\tfrac1n\sum_i\tilde x_i\big)^{T}w=0。所以 V(w)V(w) 正是刻度 ziz_i 的方差。

核对:这 8 个点 T=133T=133。尺子在 45° 时 V=125V=125、E=8E=8,在 0° 时 V=E=66.5V=E=66.5,两次相加都是 133。

让丢掉的最少,就是让留下的方差最大。PCA 讲「方差最大」,不是先规定方差重要,而是从「丢得最少」推出来的。

下一站 · 方向在哪:最好的方向,能不能直接算出来,而不是一个个试? →

第 3 站 · 方向在哪

最好的方向,能不能直接算出来,而不是一个个试?

上一站知道了要找让 VV 最大的方向。二维时把尺子转一圈,一个个角度试,总能找到。可一条记录有 8 个、100 个数的时候,方向多到试不完。我们需要一个能直接算的办法。

先把 VV 写成能算的样子。它是每个点刻度平方的平均,展开以后,只用到这批数据的三个数:横向各自的方差 66.5、纵向各自的方差 66.5,以及横纵一起变化的程度 58.5(协方差)。把它们摆成一张 2×2 的表,就是协方差矩阵 CC,任何方向的 VV 都能直接算:

C=[66.558.558.566.5],V(w)=wTCw.C=\begin{bmatrix}66.5&58.5\\58.5&66.5\end{bmatrix},\qquad V(w)=w^{T}Cw.

换个角度看 CC:它是一台机器,喂进去一个方向箭头 ww,吐出来一个新箭头 CwCw。而 VV 正是 CwCw 落在 ww 上的影子长度。

w 在 0° 时,Cw = (66.5, 58.5),往逆时针偏了约 41°,影子 V = 66.5。w 在 45° 时,Cw = 125w,不拐弯,V = 125;在 135° 时,Cw = 8w,也不拐弯,V = 8。启用 JavaScript 后可以拖动箭头。

大多数方向,CwCw 都会拐个弯,偏向点云拉长的那一边。这时只要把 ww 顺着它偏的方向转一点,影子就会变长,说明还没到最好。

所以最好的方向一定不拐弯:CwCw 和 ww 同向,只是长度变了。这样的方向叫特征向量,长度变成的倍数叫特征值:

Cw=λw.Cw=\lambda w.

这组数据里有两个不拐弯的方向:45°,特征值 125;135°,特征值 8。影子长度 V=wTCw=λV=w^{T}Cw=\lambda,所以特征值就是这个方向上留下的方差。

推导 · 任意维数

  1. 把 V(w)V(w) 写成矩阵。zi2=(x~iTw)2=wTx~ix~iTwz_i^2=(\tilde x_i^{T}w)^2=w^{T}\tilde x_i\tilde x_i^{T}w,平均以后 ww 可以提到外面:

    V(w)=wTCw,C=1n∑i=1nx~ix~iT=1nXTX.\begin{aligned}V(w)&=w^{T}Cw,\\C&=\frac1n\sum_{i=1}^{n}\tilde x_i\tilde x_i^{T}=\frac1n X^{T}X.\end{aligned}

    XX 是把偏移按行排成的 n×dn\times d 矩阵。CC 是 d×dd\times d 的对称矩阵:第 jj 行第 kk 列是第 jj、kk 个特征乘积的平均,对角线是各自的方差。总量 TT 就是对角线之和 tr⁡C\operatorname{tr}C。

  2. 要解的问题:

    max⁡w  wTCws.t.wTw=1.\max_{w}\;w^{T}Cw\quad\text{s.t.}\quad w^{T}w=1.
  3. 用拉格朗日乘子把约束并进去,对 ww 求导并令其为 0(CC 对称,所以 ∇w wTCw=2Cw\nabla_w\,w^{T}Cw=2Cw):

    L(w,λ)=wTCw−λ(wTw−1),∇wL=2Cw−2λw=0⟹Cw=λw.\begin{aligned}L(w,\lambda)&=w^{T}Cw-\lambda(w^{T}w-1),\\\nabla_wL&=2Cw-2\lambda w=0\\\Longrightarrow\quad Cw&=\lambda w.\end{aligned}

    最大值点一定是 CC 的特征向量。这就是实验里「不拐弯」的意思。

  4. 在这样的点上,留下的方差就是特征值:

    V(w)=wTCw=λ wTw=λ.V(w)=w^{T}Cw=\lambda\,w^{T}w=\lambda.

    所以在所有特征向量里挑特征值最大的那个。

  5. 它确实是全局最大,不只是几个候选里最好的。对称矩阵有一组互相垂直的单位特征向量 v1,…,vdv_1,\dots,v_d,特征值 λ1≥⋯≥λd≥0\lambda_1\ge\dots\ge\lambda_d\ge0(非负,因为 wTCww^{T}Cw 是平方的平均)。任何单位方向都能写成 w=∑jajvjw=\sum_j a_jv_j,∑jaj2=1\sum_j a_j^2=1,于是

    wTCw=∑jλjaj2≤λ1∑jaj2=λ1,w^{T}Cw=\sum_{j}\lambda_ja_j^2\le\lambda_1\sum_{j}a_j^2=\lambda_1,

    等号在 w=v1w=v_1 时取到。此时丢掉的最少,是 E=T−λ1E=T-\lambda_1。

核对:v1=(1,1)/2v_1=(1,1)/\sqrt2 时 Cv1=(125,125)/2=125 v1Cv_1=(125,125)/\sqrt2=125\,v_1;v2=(1,−1)/2v_2=(1,-1)/\sqrt2 时 Cv2=8 v2Cv_2=8\,v_2。tr⁡C=66.5+66.5=133=125+8\operatorname{tr}C=66.5+66.5=133=125+8,丢掉的最少是 133−125=8133-125=8。

最好的方向,就是协方差矩阵特征值最大的那个特征向量,它留下的方差就等于这个特征值。不用再一个个试:维数再高,也只是解一个特征值问题。

下一站 · 留几个:特征多了以后,留几个新坐标才够? →

第 4 站 · 留几个

特征多了以后,留几个新坐标才够?

二维的点只有两条主轴:45° 那条留下 125,和它垂直的 135° 那条留下 8。第二条为什么要垂直?第一条已经记下的变化,不该再记一遍。在垂直于第一条的方向里再找方差最大的,就是第二条,它恰好是另一个特征向量。

一条记录有 dd 个数,就有 dd 条互相垂直的主轴,按特征值从大到小排好。留下前 kk 条,每条记录就从 dd 个数变成 kk 个数。

丢了多少,还是第 2 站那本账:总量 TT 等于所有特征值之和,留下前 kk 条以后,丢掉的 EE 就是后面那些特征值之和。二维例子里 T=125+8=133T=125+8=133,只留一条就丢掉 8。

到这里,原稿开头的三个词都有了着落:特征值是这条轴上留下的方差;贡献率是它占总量的比例;累积贡献率是前 kk 条加起来的比例。

贡献率j=λj∑ℓλℓ,\text{贡献率}_j=\frac{\lambda_j}{\sum_\ell\lambda_\ell},累积贡献率k=λ1+⋯+λk∑ℓλℓ.\text{累积贡献率}_k=\frac{\lambda_1+\dots+\lambda_k}{\sum_\ell\lambda_\ell}.

二维例子里,第一条轴的贡献率是 125/133,约 94%。原稿给了一条经验线:

通常,最好选择累积贡献超过70-80%的主要成分

这条线到底好不好用?拿三组八维数据试一试。

同一条 80% 的线:一个主导方向的数据留 1 条,缓慢衰减的留 5 条,接近平坦的留 7 条。启用 JavaScript 后可以切换数据、移动阈值。

同样是 80%,有的数据留 1 条就够,有的要留 7 条。阈值管的只是「最多丢掉多大比例」,管不了要留几条;留几条取决于特征值怎么分布,也取决于你要拿这些新坐标去做什么。

推导 · 任意维数

  1. 留 kk 条轴,就是选 kk 个互相垂直的单位向量,排成 d×kd\times k 的矩阵 WW,WTW=IW^{T}W=I。每条记录存 kk 个新坐标,还原时用它们把轴加回去,剩下的是垂线:

    zi=WTx~i,x^i=Wzi,di=x~i−Wzi.\begin{aligned}z_i&=W^{T}\tilde x_i,\\\hat x_i&=Wz_i,\\d_i&=\tilde x_i-Wz_i.\end{aligned}
  2. 和第 2 站一样,垂线垂直于每一条轴:WTdi=zi−WTWzi=0W^{T}d_i=z_i-W^{T}Wz_i=0。勾股定理照样成立,平均以后还是那本账:

    T=∑l=1kwlTCwl⏟V(W)+E(W).T=\underbrace{\textstyle\sum_{l=1}^{k}w_l^{T}Cw_l}_{V(W)}+E(W).
  3. 把 CC 按特征向量展开,C=∑jλjvjvjTC=\sum_j\lambda_jv_jv_j^{T},于是 wlTCwl=∑jλj(vjTwl)2w_l^{T}Cw_l=\sum_j\lambda_j(v_j^{T}w_l)^2。把同一个 jj 的项收在一起:

    V(W)=∑j=1dλj pj,pj=∑l=1k(vjTwl)2.V(W)=\sum_{j=1}^{d}\lambda_j\,p_j,\qquad p_j=\sum_{l=1}^{k}(v_j^{T}w_l)^2.
  4. 这些权重有两条限制。pjp_j 是 vjv_j 投影到这 kk 条轴上的长度平方,不超过 vjv_j 本身的长度 1;而对 jj 求和,等于把每条 wlw_l 在一组垂直基上的分量平方加起来:

    0≤pj≤1,∑jpj=∑l∥wl∥2=k.0\le p_j\le1,\qquad \sum_{j}p_j=\sum_{l}\|w_l\|^2=k.

    总共 kk 份权重,每个特征值最多分到 1 份。要让 ∑jλjpj\sum_j\lambda_jp_j 最大,就把权重全给最大的 kk 个特征值:

    V(W)≤λ1+⋯+λk,E(W)≥λk+1+⋯+λd.\begin{aligned}V(W)&\le\lambda_1+\dots+\lambda_k,\\E(W)&\ge\lambda_{k+1}+\dots+\lambda_d.\end{aligned}

    取 W=[v1,…,vk]W=[v_1,\dots,v_k] 时等号成立。k=2k=2 时就说明了正文那句话:第二条轴是第二大特征值的特征向量。

  5. 总量 T=tr⁡C=λ1+⋯+λdT=\operatorname{tr}C=\lambda_1+\dots+\lambda_d(第 3 站第 1 步)。所以累积贡献率就是留下的比例:

    λ1+⋯+λkλ1+⋯+λd=VT=1−ET.\frac{\lambda_1+\dots+\lambda_k}{\lambda_1+\dots+\lambda_d}=\frac{V}{T}=1-\frac{E}{T}.

    「累积贡献超过 80%」,意思正是「丢掉的平方误差不超过总量的 20%」。

  6. 顺带一个性质:新坐标的协方差矩阵是

    1n∑iziziT=WTCW=diag⁡(λ1,…,λk).\begin{aligned}\frac1n\sum_i z_iz_i^{T}&=W^{T}CW\\&=\operatorname{diag}(\lambda_1,\dots,\lambda_k).\end{aligned}

    对角线外都是 0,新坐标两两不相关。第 5 站的支线会说明,不相关还不等于独立。

核对:一个主导方向的数据,总量 10.510.5,留 1 条是 8.5/10.5≈80.95%8.5/10.5\approx80.95\%,丢掉 22。缓慢衰减的数据,留 4 条 75.96%,留 5 条 84.75%,所以 80% 的线要留 5 条。接近平坦的数据,留 6 条 79.63%,留 7 条 90.12%,要留 7 条。

留几个没有通用的线。先看特征值一条条掉下来的样子:前面陡、后面平,常在拐弯处停下;一路平缓,说明数据本来就没有低维结构,PCA 省不了多少。

下一站 · 它看不见什么:方差最大,就一定是最重要的吗? →

第 5 站 · 它看不见什么

方差最大,就一定是最重要的吗?

前四站,PCA 一直只做一件事:在平方误差的意义下,丢得最少。它只看数字散得有多开,不知道这些数字代表什么。这一站专门去找它的盲点。

纵坐标乘 0.1,主轴从 45° 转到 5°;乘 10,转到 85°;按列标准化后回到 45°。在 (25, −25) 加一个离群点,主轴翻到 135°。类别只取决于小方差方向时,PC1 留下 99.25% 的方差,却分不开两类。弯曲的点上,第一主轴只留下 53.5%。启用 JavaScript 后可以逐个试。

尺度。纵坐标换个单位,数字变小,PCA 就觉得纵向「不重要」,主轴倒向横轴。数据什么都没变。常见做法是先把每一列除以自己的标准差(标准化),可这等于换了一种量「丢多少」的尺子,问题本身也变了。

离群点。平方让远处的点代价特别大,一个记错的点就能把主轴拉走。

意义。两类点的区别全在纵向,纵向的变化却很小。第一主轴留下了 99% 以上的方差,偏偏把能区分两类的那一点丢了。PCA 不知道你关心什么。

形状。点沿一条弯线排开,本来一个数(沿线走了多远)就能描述,可直尺顺不了弯。

推导 · 它只看得见 μ 和 C

回头看前四站的推导,数据只通过两样东西进来:平均 μ\mu 和协方差矩阵 C=1n∑ix~ix~iTC=\frac1n\sum_i\tilde x_i\tilde x_i^{T}。两批数据只要 μ\mu 和 CC 相同,PCA 给出的轴就完全一样。四个盲点都从这里来。

  1. 尺度。把第 jj 个特征的单位换掉,相当于乘上一个倍数 sjs_j。记 D=diag⁡(s1,…,sd)D=\operatorname{diag}(s_1,\dots,s_d),则 x~i→Dx~i\tilde x_i\to D\tilde x_i,

    C→DCD.C\to DCD.

    DCDDCD 的特征向量一般不是 CC 的特征向量乘上 DD,轴会转。二维时可以写出来:方向角为 θ\theta 时 V(θ)=c11cos⁡2θ+2c12sin⁡θcos⁡θ+c22sin⁡2θV(\theta)=c_{11}\cos^2\theta+2c_{12}\sin\theta\cos\theta+c_{22}\sin^2\theta,令导数为 0,

    tan⁡2θ=2c12c11−c22.\tan2\theta=\frac{2c_{12}}{c_{11}-c_{22}}.

    纵坐标乘 ss 后,c12→s c12c_{12}\to s\,c_{12},c22→s2c22c_{22}\to s^2c_{22}:

    tan⁡2θ=2⋅58.5 s66.5−66.5 s2.\tan2\theta=\frac{2\cdot58.5\,s}{66.5-66.5\,s^2}.

    标准化是取 sj=1/cjjs_j=1/\sqrt{c_{jj}},这时 DCDDCD 的对角线全是 1,也就是相关系数矩阵。它和单位无关,但量「丢多少」的尺子换了,答案回答的是另一个问题。

  2. 离群点。一个点在 CC 里的份额是 1nx~ox~oT\frac1n\tilde x_o\tilde x_o^{T}。它让方向 uu 上的方差多出

    (uTx~o)2n,\frac{(u^{T}\tilde x_o)^2}{n},

    和它离中心的距离成平方关系。距离翻倍,影响变成 4 倍。

  3. 意义。目标函数 V(w)=wTCwV(w)=w^{T}Cw 里只有 CC,没有类别标签。不管标签怎么分,PCA 都给出同样的轴。要用标签,就得换一个把标签写进去的目标,比如线性判别分析:让两类中心沿 ww 拉开,同时让每一类内部沿 ww 尽量紧。

  4. 形状。只留 kk 条轴时,还原出来的点 x^i=μ+WWTx~i\hat x_i=\mu+WW^{T}\tilde x_i 全落在一个过 μ\mu 的平直子空间里(k=1k=1 时是一条直线)。弯线上的点不在任何一条直线上,所以只留一条轴,误差不可能是 0。CC 只记录「一起线性变化」的程度,弯曲这件事它记不下来。

核对:s=0.1s=0.1 时 θ≈5.0∘\theta\approx5.0^\circ,s=10s=10 时 θ≈85.0∘\theta\approx85.0^\circ。离群点放在 (25, −25),9 个点的中心移到 (2.78, −2.78),它的偏移 (22.2, −22.2) 正好朝 135°,单独给这个方向加了 31.42/9≈109.731.4^2/9\approx109.7;135° 的方差变成 130.6,超过 45° 的 125×8/9≈111.1125\times8/9\approx111.1,主轴翻转。类别那组 C=diag⁡(132,1)C=\operatorname{diag}(132,1),第一主轴留下 132/133≈99.25%132/133\approx99.25\%,区分两类的纵向只占 1。弯线那组 xx 左右对称,c12=13n∑ixi3=0c_{12}=\frac{1}{3n}\sum_i x_i^3=0,C=diag⁡(13, 14.95)C=\operatorname{diag}(13,\,14.95),第一主轴只留下 14.95/27.95≈53.5%14.95/27.95\approx53.5\%。

PCA 回答的是一个很具体的问题:在给定的单位下,用一把直尺,让平方误差丢得最少。单位怎么选、离群点要不要、你在乎哪种差别、数据是不是弯的,这些它都不管,要你自己判断。

回到起点

如果只能留下一个数,应该留下什么?

  1. 留一个数放一把尺子,留下点在尺子上的刻度。
  2. 怎样算好丢得最少,等价于留下的方差最大。
  3. 方向在哪最好的方向是协方差矩阵最大特征值的特征向量。
  4. 留几个留几条看特征值怎么往下掉,没有通用的线。
  5. 它看不见什么单位、离群点、意义和形状,要自己把关。

看原稿 →回到所有文章 →