🧮 第3章 无监督学习
📄 一句话概括

LDA 是有监督的降维:它知道每个点属于哪一类,于是专门找一个「投影后两类离得最远」的方向。 Demo 里同一份数据, PCA 选的方向分离度只有 0.6866(两团糊在一起),LDA 选的方向 6.7165,差了 9.8 倍—— 因为 PCA 只看方差,根本不知道有「类」这回事。

📝 本节和前面几节的关系

前面三个算法都是无监督的:没有 y,只能找「数据自己散成什么样」。这一节把 y 请回来。 有了标签,降维的目标就变了——不是「保住最多信息」,而是「保住最能分开类的信息」。 这两个目标经常冲突,Demo 会把它量出来。 注意本节的 LDA 是 Linear Discriminant Analysis,中文叫线性判别分析。 另一个常被叫 LDA 的是 Latent Dirichlet Allocation,中文叫潜在狄利克雷分配。 两者除了缩写一样,没有任何关系,最后一节专门辨析。

一、目标换了:不是保信息,是保可分性

PCA 的问题在这里暴露得很清楚:方差大的方向,不一定分得开类。 想象两类点, 在 x1 方向上都拉得很长(方差大),但两类的中心几乎重合; 在 x2 方向上都挤得很紧(方差小),但中心差得很远。 PCA 会毫不犹豫地选 x1——方差大嘛。可投到 x1 上,两类彻底糊在一起,什么都分不出来。 它优化的是「投影后散得开」,不是「投影后分得开」。 散得开和分得开是两件事。 LDA 换了个目标:找一个方向,让投影之后「类与类之间的距离」尽量大,同时「类内部的散布」尽量小。 这两个要求合起来就是所谓的Fisher 判据:类间散度除以类内散度,越大越好。 这个比值的妙处在于它是尺度无关的:方向向量乘 10 倍,分子分母同时乘 100,比值不变。 所以 LDA 只需要找方向,不用管长度。

二、两个散度矩阵

类内散度 Sw:把每一类的点减去自己的中心,平方求和,再把各类加起来。 它衡量「同一类内部有多散」。这个数越小越好。 类间散度 Sb:把每一类的中心减去全体中心,按类的大小加权,平方求和。 它衡量「各类中心彼此隔得多远」。这个数越大越好。 投影到方向 w 上之后,类内散度变成 wᵀSw w,类间散度变成 wᵀSb w,Fisher 判据就是这两个二次型之比。 求它的最大值,归结成解一个广义特征值问题:Sw 的逆乘 Sb 的最大特征向量,就是最佳方向。 这个解有个很直观的名字叫白化再找方向:先乘 Sw 的逆,等于把数据「压扁」到各类内部一样散, 然后在压扁后的空间里找类间散度最大的方向。先消掉「谁本来就散」,再比「谁分得开」。 工程上有个细节:Sw 可能是奇异的(特征数多于样本数时必然如此),直接求逆会炸。 所以代码里用的是伪逆 pinv。 这也是 LDA 在小样本高维数据上常要先做一次 PCA 的原因——先把维度降到 Sw 可逆,再做 LDA。

三、同一份数据,两个方向

Demo 造了两类点,每类 150 个。 关键设计:x1 方向上两类的标准差都是 2.5(散得厉害),中心只错开 1; x2 方向上标准差只有 0.45(挤得紧),中心差 3。 一句话:x1 「散得厉害但分不开」, x2 「挤得紧但分得开」。 结果很清楚:PCA 的方向是 (−0.974, −0.226),基本落在 x1 上; LDA 的方向是 (+0.016, −1.000),几乎是垂直的。 同一个数据集,两种「好方向」的定义,选出两个完全不同的方向。 投影到一维之后差距更明显:PCA 的分离度 0.6866(小于 1,两团重叠),LDA 的 6.7165。 9.8 倍。 这就是「知道有类」和「不知道有类」的区别。 但要注意:LDA 并不是「更好的 PCA」。 它俩优化的东西不一样。 如果下游任务是「压缩存储」而不是「分类」, PCA 才是对的——LDA 会为了分开类而丢掉大量与分类无关的变化,那些变化在重建时是必需的。

四、它的硬上限:类数减一

LDA 最多能把数据降到「类别数 − 1」维。 两类最多降到 1 维,三类最多 2 维, 十类最多 9 维——再往上找不出方向了。 为什么?因为类间散度矩阵 Sb 的秩最多是「类数 − 1」。 直观理解:k 个类的中心,减去全体中心之后,这些向量加起来等于 0(加权平均), 所以它们最多只有 k − 1 个是独立的。Sb 的秩就是这个独立方向数。 这个上限在小类别数时很要命。做一个 3 类的可视化,LDA 最多给你 2 维,正好能画; 做一个 100 类的细粒度分类,LDA 最多 99 维, 而原始特征可能有几万维——中间那一大段压缩,LDA 帮不上忙。 这也是实践中常见的组合拳: 先用 PCA 把几万维降到几百维(无监督,保信息),再用 LDA 降到几十维(有监督,保可分性)。 两步各干各擅长的事。

五、两个 LDA 不是一回事

这一节的 LDA 是 Linear Discriminant Analysis,中文叫线性判别分析,有监督的降维方法。 它的亲戚还有 QDA(二次判别分析),区别是假设各类协方差不同。 另一个常被叫 LDA 的是 Latent Dirichlet Allocation,中文叫潜在狄利克雷分配, 无监督的主题模型,用来从文档里抽主题。 它和上一节 LSA 是同一个赛道的东西,只是用了概率生成模型那套语言。 两个东西除了三个字母一样,没有任何关系。 一个是降维,一个是主题模型; 一个要标签,一个不要标签。读论文、看文档时看到 LDA,先看上下文判断是哪个。 顺便说,这一类同名不同义的坑还有几个: PCA 和「主成分回归」不是一回事;「逻辑回归」是分类不是回归; 「朴素贝叶斯」的「朴素」是「假设特征独立」的意思,不是「简单」。 术语这东西, 撞车比想象中常见。

六、动手 Demo:PCA 选散得最开的方向,LDA 选分得最开的方向

两类点各 150 个。x1 方向上标准差 2.5、中心只错开 1;x2 方向上标准差 0.45、中心差 3。 换句话说:x1 上散得厉害但分不开,x2 上挤得紧但分得开。 这个设计就是为了让两个方法分道扬镳。 代码做两件事:算类内散度 Sw 和类间散度 Sb,取伪逆乘 Sb 的最大特征向量当 LDA 方向; 再对协方差矩阵做特征值分解,取最大特征向量当 PCA 方向。两条路都不需要迭代。

import numpy as np

m0, m1 = X[y == 0].mean(axis=0), X[y == 1].mean(axis=0)
Sw = np.zeros((2, 2))                         # 类内散度:每类自己散得多开
for c in (0, 1):
    Xc = X[y == c] - X[y == c].mean(axis=0)
    Sw += Xc.T @ Xc
Sb = n * np.outer(m0 - X.mean(axis=0), m0 - X.mean(axis=0)) \
    + n * np.outer(m1 - X.mean(axis=0), m1 - X.mean(axis=0))

# 让「类间散度 / 类内散度」最大的方向,就是 Sw 逆乘 Sb 的最大特征向量
ev, evec = np.linalg.eig(np.linalg.pinv(Sw) @ Sb)
w_lda = np.real(evec[:, np.argmax(np.real(ev))])

# 对照:PCA 只找方差最大的方向,完全不看 y
C = np.cov((X - X.mean(axis=0)).T)
ev2, evec2 = np.linalg.eigh(C)
w_pca = evec2[:, np.argmax(ev2)]

实跑输出:

--------------------------------------------------------------------
LDA:让两类投影之后离得最远
--------------------------------------------------------------------

  造两类点,每类 150 个。
  **关键设计:两类在 x1 方向上拉得很长(标准差 2.5)、中心只错开 1,**
  **在 x2 方向上分得很开(中心差 3、标准差只有 0.45)。**
   也就是说,x1 方向「散得厉害但分不开」,x2 方向「挤得紧但分得开」。

  【一】两个方向各是什么
  方法    方向向量            它优化什么
  ----------------------------------------------------
  PCA     (-0.974, -0.226)    投影后方差最大
  LDA     (+0.016, -1.000)    类间散度除以类内散度最大
  PCA 的方向基本落在 x1 上——因为 x1 方向上两类的方差大得多,**方差大的方向,PCA 就选它。**
   LDA 的方向几乎是垂直的——因为 x2 方向上两类分得开。
  **同一个数据集,两种「好方向」的定义,选出两个方向。**

  【二】投影到一维之后,两类分得开吗
  方法    类 0 均值   类 1 均值   分离度
  ----------------------------------------
  PCA     -0.2458     -1.8805     0.6866
  LDA     0.0280      -2.9631     6.7165
  分离度是「两类中心的距离」除以「两类标准差」——越大越好,小于 1 基本就是两团糊在一起。
  **PCA 只有 0.69,糊成一团;LDA 有 6.72,隔得干干净净。倍数上差了 9.8 倍。**
   原因很直白:PCA 完全不知道有「类别」这回事,它只找方差大的方向。

  【三】所以它和 LSA 那个 LDA 不是一回事
  这一节的 LDA 是 **Linear Discriminant Analysis(线性判别分析)**,**有监督**
  的降维方法——它用类别标签算类间散度和类内散度。
  另一个常被叫 LDA 的是 **Latent Dirichlet Allocation(潜在狄利克雷分配)**,**无监督**
  的主题模型,用来从文档里抽主题。**两个东西除了缩写一样,没有任何关系。**
   读论文时看到 LDA 要先看上下文。

  【四】什么时候用 LDA,什么时候用 PCA
  场景                      选哪个                  理由
  --------------------------------------------------------------------------
  有标签,想降维后好分类    LDA                     它专门优化类间可分性
  没标签,想压缩或可视化    PCA                     不需要标签
  想找回数据的主要变化方向  PCA                     它保的是方差,不是可分性
  类别数多于特征数时        LDA 最多降到类数减一维  这是它的硬限制
  **LDA 的降维上限是「类别数 − 1」。**
   因为类间散度矩阵的秩最多就是这么多,再多找不出方向了。
  三类数据最多降到两维——这也是为什么做可视化时,LDA 画出来常常正好是二维。

方向表看一眼就懂。PCA 是 (−0.974, −0.226),几乎躺在 x1 上;LDA 是 (+0.016, −1.000),几乎垂直。 同一个数据集,两个方向几乎垂直。 分离度表才是判分的地方:0.6866 对 6.7165。小于 1 意味着两团重叠得很厉害, 6.7 意味着两团中心隔了六七个标准差。差了 9.8 倍。 注意 LDA 的分离度是负数方向算出来的。方向向量整体反号不影响分离度(因为取了绝对值), 所以「类 0 均值 0.028、类 1 均值 −2.963」和反过来写是一回事。符号不用纠结,看间距。 最后那张表是选型清单。有标签想分类就用 LDA,没标签想压缩就用 PCA,想找回主要变化方向还是 PCA。 没有「哪个更好」,只有「哪个目标对得上」。

30 秒实验

  1. 把两类的 x2 中心差从 3 改成 1.5,PCA 和 LDA 的分离度会变成多少?差距还在吗?
  2. 把 x1 的中心错开从 1 改成 3(两类在 x1 上也能分开),LDA 的方向会转到哪?
  3. 做三类的数据,用这份代码最多能降到几维?降到 2 维之后还能画图看吗?

常见误区

  1. 以为 LDA 是「更好的 PCA」。它俩优化的目标不同:PCA 保方差,LDA 保可分性。 下游是压缩或重建时用 LDA 会丢关键信息。先想清楚下游任务是什么。
  2. 在高维小样本上直接用 LDA。特征数超过样本数时 Sw 必然奇异,求逆会炸或给出垃圾方向。 先做 PCA 降维,或者用收缩估计。
  3. 把两个 LDA 搞混。线性判别分析(降维,要标签)和潜在狄利克雷分配(主题模型, 不要标签)是两回事。看到 LDA 先看上下文。
自测三问(点开对答案) 1. Fisher 判据优化的是什么比值?为什么它和尺度无关? —— 类间散度除以类内散度(投影之后)。 方向向量乘任意倍数,分子分母同时乘这个倍数的平方,比值不变,所以只需要找方向不用管长度。 2. 为什么 LDA 最多只能降到「类数 − 1」维? —— 因为类间散度矩阵的秩最多是类数减一:k 个类的中心减去全体中心后,这些向量加权和为 0, 最多 k − 1 个线性无关。秩就是能找出的方向数上限。 3. 什么情况下该用 PCA 而不是 LDA? —— 没有标签的时候(LDA 用不了),或者下游任务是压缩、重建、可视化而不是分类的时候。 PCA 保住的是总方差,这对重建更有利。

3.1 到 3.4 四个算法都在「换坐标」。 从 3.5 节开始换一类问题:数据是几团拼起来的,怎么把团找出来。 先看最出名的 k-means。

相关笔记