📘 第10章 图像和语音的模式识别
📄 一句话概括

25 个像素里,真正管用的可能只有两三个方向。降维要干的事,就是把这几个方向找出来, 其余的扔掉。 最经典的两个办法:主成分分析只看数据散得开不开,线性判别分析还要看标签。 一个不看答案,一个看答案——差别有多大,这一节用判别比量给你看。

📝 本节和前面几节的关系

这一节接 10.1 的最后一句话。上一节说「维数一高距离就失灵」,这里给出解法。 它也是 7.1 无监督学习的第二次出现:主成分分析是最典型的无监督方法,因为它一个标签都不用。

一、25 个像素里,有几个是废的

造一批小图:三个 5 乘 5 的模板(十字、斜叉、方框),每类 40 张训练、40 张测试, 每张按 12% 的概率翻转像素。 把 25 个像素原样当特征,最近邻分类的测试准确率是 0.9917。 看着挺好,可这 25 个数里有大量冗余:四个角基本恒为 0, 中间那个像素每张都不一样——它们对分类毫无贡献,却各自占着一个维度。 这正是 10.1 讲的那个陷阱的温和版。25 维还不至于让距离失灵, 但每多一个废维度,距离里就多一份纯噪声。

二、主成分分析:顺着散得最开的方向看

主成分分析的想法一句话说完:找数据散得最开的那个方向,把数据投影上去; 再找第二个方向(必须跟第一个垂直),继续投影。 为什么「散得最开」等于「信息最多」? 因为如果一个方向上所有点都挤在一起,那这个方向就分不出谁是谁;散得开,才有区分度。 对训练集算 25 乘 25 的协方差矩阵,做特征分解。前三个主成分画回 5 乘 5,是这样:

主成分 占方差 形状
第 1 个 31.0% 一个十字(横竖各一道)
第 2 个 27.4% 一个斜叉(两条对角线)
第 3 个 3.7% 边角的零碎
这两个方向正好对应「十字」和「斜叉」两个模板。
没人告诉算法哪个模板长什么样,它自己从协方差里看出来的。
累计解释方差:
主成分个数 1 2 3 5 8 12 25
累计方差 0.3095 0.5839 0.6206 0.6870 0.7652 0.8469 1.0000
前 2 个主成分只留下 58.4% 的方差,可分类准确率是 1.0000——比用全部 25 维的 0.9917 还高一点。
这件事值得停下来想一秒:丢掉 41.6% 的方差,结果反而更好。
因为丢掉的方差里,绝大部分是噪声。
降维不是压缩,是筛掉噪声——这是它跟「文件压缩」最大的区别。

三、一个必须提的坑:不标准化

主成分有个软肋:它只看方差大小,不问这个方差有没有意义。 做个实验:在 25 个像素后面再挂一个特征——「墨水总量」乘 40 再加噪声。 这个数纯粹是噪声,可它数值大,方差也就大。

做法 第 1 主成分占方差 第 26 维吃掉的载荷 前 2 维准确率
不标准化 0.9997 100.0% 0.9167
先标准化 0.3167 8.1% 0.9917
不标准化的时候,第 1 主成分 100.0% 的载荷都压在那一维噪声上,25 个像素一点没进。
换句话说,算法把整张图忽略了,只盯着那个数。
准确率立刻从 0.9917 掉到 0.9167。这是「降维降错了方向,信息真丢了」。
标准化之后,第 26 维只占 8.1%,主成分才回到图形结构上。
所以「先标准化再降维」不是讲究,是这一步的前提条件。

四、线性判别分析:让标签说话

主成分从头到尾不看标签。可如果标签本来就有,为什么不利用? 线性判别分析的想法:找一个方向,让「类间散得开、类内挤得紧」。 这个目标跟主成分完全不一样——主成分只问「整体散不散」,它问「类跟类分不分得开」。 量这个的指标叫判别比:类间方差除以类内方差,越大越分得开。同样把 25 维压到 1 维:

取 1 个方向 判别比 测试准确率
主成分(不看标签) 14.1566 0.9417
判别方向(看标签) 22.8599 0.9667
判别方向赢了,而且这是它该赢的——它优化的正是这个指标。
这张表要看的不是谁赢,是「有标签」这件事值多少钱:
同样的数据、同样降到 1 维,只换个方向,判别比从 14.1566 变成 22.8599。
但判别分析有个死穴。把训练样本一路减下去:
每类训练张数 判别比 测试准确率
9 712.8476 0.7300
15 73.5109 0.8100
30 28.1910 0.8500
40 23.0695 0.8717
每类只有 9 张的时候,判别比高得离谱(712 对 23),测试准确率却最低(0.7300 对 0.8717)。
原因很具体:25 维里凑 27 个样本,类内散度矩阵几乎是奇异的。
它求逆的时候会把「噪声的方向」也算成「可分的方向」,判别比就被刷上去了。
这是小样本下判别分析的典型翻车——指标漂亮,效果拉胯。

五、降维不是压缩,是取舍

把三种做法摆在一起:主成分、标准化后再主成分、线性判别分析。

做法 要不要标签 擅长什么 怕什么
主成分 不要 去冗余、可视化 被量纲拐跑
标准化+主成分 不要 同上,且公平对待每一维 把有用的小方差也压平
线性判别分析 要 分类前降维,判别比直接优化 样本少时矩阵奇异
三者没有谁更好,只有用在哪。
没有标签(或标签不可信)就主成分;要分类、标签可靠、样本够多就判别分析。
而两个方法都在做同一件事:把「维度」换成「方向」。
方向的好处是它可以挑——挑那些真带信息的,扔掉剩下的。
这一节的所有算法都有一个共同的软肋:方向和目标都是人设计的。
主成分挑「散得开」,判别分析挑「分得开」,可到底哪个方向真的有用,最终还是得靠人猜。
9.4 讲的卷积核,就是把这个「猜」交给数据去做。

六、动手 Demo:从 25 个像素里,抽出两三个真正有用的数

五段:原始像素的冗余、主成分长什么样、标准化的坑、判别分析、三者怎么选。 第 2 段和第 4 段是主角,一个是无监督的极限,一个是有监督的起点。 协方差分解、广义特征值、判别比,全部自己写。 主成分的图是「把方向画回 5 乘 5」,这一步特别值得盯着看:数学算出来的东西,居然长得像笔画。

def pca(X, k):
    M = X.mean(0)
    Z = X - M                           # 先减均值,再做分解
    C = Z.T @ Z / max(1, len(X) - 1)     # 协方差矩阵
    v, V = np.linalg.eigh(C)             # 对称矩阵用 eigh,一定出实数
    o = np.argsort(v)[::-1]              # 方差从大到小排
    v, V = v[o], V[:, o]
    return M, V[:, :k].T, v / v.sum()    # 方向、解释方差比

def lda(X, y, k=1):
    Sw = np.zeros((dim, dim))            # 类内散度:同一类离自己中心多远
    Sb = np.zeros((dim, dim))            # 类间散度:各类中心离总中心多远
    for c in range(NC):
        Xc = X[y == c]
        mc = Xc.mean(0)
        Sw += (Xc - mc).T @ (Xc - mc)
        d = (mc - M)[:, None]
        Sb += len(Xc) * (d @ d.T)
    W, V = np.linalg.eig(np.linalg.pinv(Sw) @ Sb)   # 广义特征值问题
    return V.real[:, np.argsort(W.real)[::-1]][:, :k].T

# 标准化的作用:不除以标准差,主成分就被数值最大的那一维拐跑
Z = (X - X.mean(0)) / X.std(0)

实跑输出:

--------------------------------------------------------------------
从 25 个像素里,抽出两三个真正有用的数
--------------------------------------------------------------------
==============================================================

[1] 直接用像素当特征,问题出在哪

  三个 5 乘 5 的模板,每类加 12% 的像素翻转噪声,各造 40 张训练、40 张测试:

    十字  ..#..  ..#..  #####  ..#..  ..#..
    斜叉  #...#  .#.#.  ..#..  .#.#.  #...#
    方框  #####  #...#  #...#  #...#  #####

  每张图都是 25 个数,取值为 0 或 1。
  拿 25 个像素原样当特征,最近邻分类:

    25 维原始像素      测试准确率 0.9917

  看着还行——因为噪声只有 12%,形状还在。
  可这 25 个数里有大量冗余:中间那个像素几乎每张图都不一样,
  而四个角基本恒为 0,它们对分类一点贡献都没有,却一样占着一个维度。

[2] 主成分分析:把 25 维压成 2 维

  对训练集算协方差矩阵(25 乘 25),做特征分解,
  取方差最大的几个方向。前三个主成分长这样:

    第 1 主成分(占方差 31.0%)
        +  
       .#..
      +###:
       .#. 
        +  
    第 2 主成分(占方差 27.4%)
      :.  +
       #.# 
       .+: 
       #.# 
      +.  +
    第 3 主成分(占方差 3.7%)
      :.:  
      :..  
       ++ .
      . :++
      :#::+

  累计解释方差:

    前 1  个主成分   累计 0.3095
    前 2  个主成分   累计 0.5839
    前 3  个主成分   累计 0.6206
    前 5  个主成分   累计 0.6870
    前 8  个主成分   累计 0.7652
    前 12 个主成分   累计 0.8469
    前 25 个主成分   累计 1.0000

  只用前 2 个主成分(25 维降到 2 维,只留下 58.4% 的方差),再分类:

    25 维原始像素       测试准确率 0.9917
    2 维主成分          测试准确率 1.0000

  **维度砍掉九成,准确率不但没掉,还高了一点——说明那 23 个维度里本来就没信息,**
  **这就是特征提取的意思:不是压缩,是「把没用的先扔掉」。**

[3] 一个坑:不标准化,主成分会被「量纲大」的那一维拐跑

  在 25 个像素后面再挂一个特征:一张图的「墨水总量」乘 40,
  这个数纯粹是噪声,但它数值大。看看主成分往哪边倒:

    不标准化:第 1 主成分占方差 0.9997,其中第 26 维吃掉载荷能量的 100.0%
    不标准化:前 2 维分类准确率 0.9167

    标准化后:第 1 主成分占方差 0.3167,其中第 26 维只占载荷能量的 8.1%
    标准化后:前 2 维分类准确率 0.9917

  **两个后果都出来了:**
  **第一,不标准化时主成分几乎整条都趴在那个噪声维上,25 个像素一点没进;**
  **第二,分类准确率从 0.9917 掉到 0.9167——降维降错了方向,信息真丢了。**
  **标准化之后第 26 维只占一小撮载荷,主成分才回到图形结构上。**
  **所以「先标准化再降维」不是讲究,是这一步的前提。**

[4] 线性判别分析:让标签告诉你「哪个方向分得开」

  主成分只问「哪个方向数据散得开」,从不看标签。
  线性判别分析反过来:找「类间散得开、类内挤得紧」的方向。

    只取 1 个方向         判别比    测试准确率
    主成分(不看标签)    14.1566   0.9417
    判别方向(看标签)    22.8599   0.9667

  判别比是「类间方差除以类内方差」,越大说明三类在那个方向上越分得开。

  **判别方向是按定义就该赢的——它优化的正是这个指标,**
  **主成分完全没理由赢。这张表要看的不是谁赢,**
  **而是「有标签」这件事本身值多少钱:同样的 25 维数据、同样降到 1 维,**
  **换个方向,判别比就从 14.1566 变成 22.8599。**

  但判别分析有个死穴,演示一下它的代价:

    每类 9   张训练:判别比 712.8476,测试准确率 0.7300
    每类 15  张训练:判别比 73.5109,测试准确率 0.8100
    每类 30  张训练:判别比 28.1910,测试准确率 0.8500
    每类 40  张训练:判别比 23.0695,测试准确率 0.8717

  **每类只有 9 张的时候判别比高得离谱,测试准确率却不是最高——**
  **因为 25 维里凑 27 个样本,类内散度矩阵几乎是奇异的,**
  **它会把「噪声的方向」也算成可分方向。这就是小样本下判别分析的典型翻车。**

[5] 结论

  四段连起来:**原始像素维数高但信息稀**;
  **主成分把散得最开的方向找出来,压到 2 维也够用**;
  **但它会被量纲拐跑,得先标准化**;
  **想用上标签就换判别分析,代价是小样本时容易过拟合。**

  这一节讲的是「提特征」的数学版:降维。
  下一节把它用到图上,看图像识别整条流水线长什么样。

第 2 段那三张主成分图是本节最值回票价的地方。 第 1 个画出来是十字,第 2 个画出来是斜叉——正好是两个模板的形状。 没人告诉它这件事,它只是找了协方差最大的方向。 58.4% 的方差换 1.0000 的准确率,这个组合要记住。 它说明「保留多少方差」和「保留多少信息」不是一回事,方差里混着大量跟类别无关的部分。 第 3 段那个 100.0% 对 8.1% 是全篇最该抄下来的对照。 不标准化的时候,主成分整条趴在噪声上——这不是精度问题,是方向完全错了。 第 4 段的两张表要连着读。第一张说判别方向赢(22.8599 对 14.1566), 第二张说样本一少它就崩(判别比 712 而准确率只有 0.7300)。 一个方法的长处和短处,往往来自同一个机制。

30 秒实验

  1. 把第 1 段的翻转概率从 12% 改成 30%,重跑。 主成分压缩到 2 维的准确率会掉,而 25 维的掉得更快——噪声越大,降维的价值越大。
  2. 把第 2 段的模板换成三个更像的形状(比如 0、8、6),重跑主成分图。 前两个主成分会变成「哪边有缺口」这类方向——它挑的永远是「变化最大的地方」。
  3. 把第 4 段的小样本实验改成每类 3 张,重跑。 判别比会飙到几千,准确率掉到 0.5 以下——这就是奇异矩阵在刷分。

常见误区

  1. 以为「保留的方差越多越好」。Demo 第 2 段:只留 58.4% 的方差, 准确率 1.0000,比留 100% 的 0.9917 还高。 方差里混着噪声,全留下等于把噪声也留下。
  2. 以为「主成分一定比原始特征好」。不一定。 主成分只保证「重建误差小」,不保证「分类好」——它压根不看标签。
  3. 以为「判别比高就是好模型」。Demo 第 4 段:每类 9 张时判别比 712.8476, 测试准确率却是最低的 0.7300。 指标和目标是两回事,小样本下这种背离是常态。
  4. 以为「标准化只是习惯」。Demo 第 3 段:不标准化,主成分 100.0% 的载荷压在噪声维上, 准确率 0.9167;标准化后 0.9917。这是一步之差十几个点的事。
自测三问(点开对答案) 1. 主成分分析凭什么认为「散得最开的方向」就是「信息最多的方向」? —— 因为**如果一个方向上所有点都挤在一起,那个方向就分不出谁是谁**。 Demo 第 2 段:第 1 主成分占 31.0% 的方差,画回 5 乘 5 是一个十字; **第 2 个占 27.4%,画出来是斜叉——正好对应两个模板**。 **散得开,才有区分度;这是它唯一的判据,也是它全部的局限。** 2. 为什么「先标准化」是主成分的前提? —— 因为**主成分只看方差大小,不看方差有没有意义**。 Demo 第 3 段:**在 25 个像素后面挂一个数值大的噪声维,** **不标准化时第 1 主成分 100.0% 的载荷全压在它上面,** **25 个像素一点没进**,准确率从 0.9917 掉到 0.9167。 **标准化后那一维只占 8.1%,主成分才回到图形结构上。** 3. 判别分析的判别比那么高,为什么还会翻车? —— 因为**判别比是「在训练集上算出来的」**,而小样本时类内散度矩阵接近奇异。 Demo 第 4 段: **每类 9 张(25 维里一共 27 个样本),判别比 712.8476,测试准确率只有 0.7300**; 每类 40 张时判别比降到 23.0695,**准确率反而升到 0.8717**。 **求逆放大了噪声方向,指标被刷上去,判别力却没跟上。**

下一节把降维这件事用到图上。 不过图像识别真正在工业界落地的做法不是主成分,而是几种更「土」的手工特征。 投影、梯度方向直方图,都算。 它们比主成分差吗?不一定。下一节用同一批图量给你看。

相关笔记