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

在深度学习之前,认图靠的是流水线:预处理、提特征、再分类。 四个零件,每个都得人来挑。 这一节把「挑特征」这件事量成一张表。 结果跟直觉相反:在干净的小图上,原始像素反而最强,越「高级」的特征越差。 可噪声一大,胜负就翻过来了。

📝 本节和前面几节的关系

这一节是 10.2 的落地版。上一节用主成分降维,这一节换几种更土的手工特征。 它也是 9.4 的反面: 9.4 说「卷积核让结构自带平移不变性」,这一节说「人也设计得出这种结构,而且设计得还不错」。 差别在于,人设计的那个只管一个任务,学出来的可以管很多任务。

一、一条图像流水线有四个零件

五类 8 乘 8 的点阵数字,每类 40 张训练、40 张测试。 每张图先按概率翻掉一批像素,再随机平移最多一格。 对机器来说,这不是图,就是一串 64 个数。流水线要做的第一件事, 是决定「把这 64 个数换成什么」再交给分类器。 四个零件分别是:预处理(去噪、二值化、裁剪)、特征提取(换一种记账方式)、 降维(挑出几个真有用的数)、分类器(最近邻、SVM、随机森林)。 这一节主要动第二个零件。因为在整个流水线里,它决定了后面所有环节的上限。 特征里没有的信息,再好的分类器也变不出来。

二、五种特征,噪声 10% 的时候谁赢

先看噪声 10% 这一档,只换特征,分类器一律用最近邻:

特征 维数 怎么算 测试准确率
原始像素 64 直接摊平 0.9650
行列投影 16 8 个行和 + 8 个列和 0.9050
梯度直方图 1 块 8 整图梯度方向加权计数 0.5700
梯度直方图 4 块 32 切 2 乘 2 四块各算一遍 0.7700
梯度直方图 16 块 128 切 4 乘 4 十六块 0.8800
原始像素赢了,16 维的行列投影紧跟在后头,梯度直方图垫底。
这跟「高级特征一定更强」的直觉相反,得说清楚为什么。
这几类数字的区别,恰恰就在「笔画落在哪几格」——位置本身就是身份。
而梯度方向直方图把位置信息扔掉了。
1 块版本只剩 8 个数,整张图只记「有横有竖有斜」,谁也认不出是 0 还是 3。
把块数从 1 加到 4 再加到 16,准确率从 0.5700 一路爬到 0.8800——它扔的全是干货,扔得越少越好。
这背后有一条更一般的规律:特征的设计,本质是在决定「记住什么、忘掉什么」。
忘掉的正好是没用的,就赚;忘掉的正好是有用的,就赔。

三、噪声一大,胜负就翻

同一套数据,把翻转概率一路加上去:

特征 5% 20% 30% 40%
原始像素 0.9750 0.7800 0.4500 0.2150
行列投影 0.9800 0.6500 0.3750 0.2600
梯度直方图 1 块 0.8100 0.3450 0.2150 0.2050
梯度直方图 4 块 0.9150 0.4650 0.3200 0.2350
梯度直方图 16 块 0.9550 0.5850 0.3800 0.1800
转折点在这里:翻到 40%,原始像素掉到 0.2150,跟瞎猜的 0.2000 只隔一张纸;
反而是 16 维的行列投影还有 0.2600,成了第一名。
规律不复杂:特征越粗,单个像素翻错对它的影响越小,噪声一大就显出抗噪;
代价是它一开始就丢了一截判别力,噪声小的时候白丢。
所以「用哪个特征」的正确答案永远跟着噪声水平走,没有通吃的。
这一点在工程上很要命:你在实验室里用干净数据挑出来的特征,到了现场(光照、雨雪、
压缩噪声)未必还是最好的。

四、抗噪和抗平移,是两件事

前面每张图既加噪声又平移,混在一起看不清是谁在拖后腿。 拆开量:同一张图,只加噪声,或者只平移,看特征变了多少。用余弦相似度,1.0000 表示一点没变:

扰动 原始像素 行列投影 梯度直方图 1 块
只翻 20% 像素 0.7095 0.9066 0.9421
只平移 1 格 0.2488 0.6729 0.8837
原始像素两头都最脆。平移一格,相似度就掉到 0.2488——因为笔画整体挪了位,64 个数全错位了。
行列投影对平移掉得慢得多(0.6729):它只记「每行有几个黑点」,不记在哪一行。
梯度直方图对两样都不太敏感(0.9421 和 0.8837),因为它压根不记像素值,只记方向。
但请把这张表和上一张表对起来看:梯度直方图最稳,准确率却最低。
稳定只是必要条件,不是充分条件。一个恒等于常数的特征最稳定——
它对噪声的不变性和对类别的无差别,是同一件事。
这就是「特征工程」最难受的地方:你没法单独优化稳定性,也没法单独优化判别力,
两者在同一个设计选择上互相拉扯。

五、预处理,和它的边界

最后一环是预处理。噪声 30% 时,加一步最土的 3 乘 3 均值滤波:

流水线 不用滤波 先滤波
原始像素 0.4200 0.4500
行列投影 0.3700 0.4000
涨 3 个点,不多,但方向是稳的。
注意这一步在真实系统里是全自动的——没人手工去滤测试图,所以这么比是公平的。
预处理的边界在哪儿?它只能对付「已知类型的噪声」。
均值滤波对高斯白噪声有效,对椒盐噪声一般,对运动模糊基本无效。
所以工业界的预处理往往是好几步串起来,每一步都针对一种已知的退化。
把这一节收一下。
这条流水线在 2012 年之前是绝对主流:人工设计特征(SIFT、HOG、Haar)加一个分类器(SVM、
随机森林)。
它的死结就在这一节的四张表里:
特征得靠人挑,挑法跟噪声水平、跟任务、跟数据量全挂钩,没有通吃的答案。
而 9.4 讲的卷积核,就是把这个「挑」的活儿交给数据自己去学——
同一个道理,只是决定「记住什么」的那个人,从工程师换成了梯度下降。

六、动手 Demo:一条图像识别流水线,特征换一个就全变了

五段:数据长什么样、五种特征在 10% 噪声下谁赢、噪声扫到 40%、抗噪和抗平移拆开量、 预处理值多少。 第 2 段和第 3 段是一对,一个说「原始像素最强」,一个说「噪声一大就翻盘」。 它们合起来构成这一节最有用的一句话:特征没有绝对的好坏,只有匹不匹配。

def f_proj(a):                    # 16 维:8 个行和加 8 个列和
    return np.concatenate([a.sum(1), a.sum(0)]) / 8.0

def f_hog(a, nb=8):               # 8 维:梯度方向的加权直方图
    gy, gx = np.gradient(a)       # 水平和垂直方向的差分
    mag = np.hypot(gx, gy)        # 这个点的梯度有多强
    ang = np.arctan2(gy, gx) % (2.0 * np.pi)
    b = np.minimum(nb - 1, (ang / (2.0 * np.pi / nb)).astype(int))
    h = np.bincount(b.ravel(), weights=mag.ravel(), minlength=nb)
    return h / (h.sum() + 1e-9)   # 归一化,去掉整体明暗的影响

def blur(a):                      # 3 乘 3 均值滤波,最土的去噪
    p = np.pad(a, 1, mode=u"constant")
    out = np.zeros_like(a)
    for i in range(a.shape[0]):
        for j in range(a.shape[1]):
            out[i, j] = p[i:i + 3, j:j + 3].mean()
    return out

实跑输出:

--------------------------------------------------------------------
一条图像识别流水线,特征换一个就全变了
--------------------------------------------------------------------
==============================================================

[1] 先看清楚要认的东西

  五类 8 乘 8 的点阵数字,每类造 40 张训练、40 张测试。
  每张图先按概率翻掉一批像素,再随机平移最多一格。原图长这样:

    0   ..####..   .#....#.   #......#   #......#
          #......#   #......#   .#....#.   ..####..
    1   ...##...   ..###...   ...##...   ...##...
          ...##...   ...##...   ...##...   .######.
    2   ..####..   .#....#.   ......#.   .....#..
          ....#...   ...#....   ..#.....   .######.
    3   ..####..   .#....#.   ......#.   ...####.
          ......#.   ......#.   .#....#.   ..####..
    4   ....##..   ...#.#..   ..#..#..   .#...#..
          #....#..   ######..   .....#..   .....#..

  噪声 20%、平移一格之后,其中一张「3」变成什么样:

    噪声 0%     
      ..####..
      .#....#.
      ......#.
      ...####.
      ......#.
      ......#.
      .#....#.
      ..####..
    噪声 20%    
      ...###..
      .......#
      ..#...#.
      .#.#####
      .##...#.
      ......##
      ....#.##
      ..###..#

  对机器来说,这从来不是图,就是一串 64 个数。
  流水线要决定的,是「把这 64 个数换成什么」再交给分类器。

[2] 五种特征,噪声 10% 的时候谁赢

  同 200 张训练、200 张测试,只换特征,最近邻分类:

    特征                    维数    测试准确率
    原始像素                64      0.9650
    行列投影                16      0.9050
    梯度直方图 1 块         8       0.5700
    梯度直方图 4 块         32      0.7700
    梯度直方图 16 块        128     0.8800

  **原始像素赢了,16 维投影紧跟在后头,梯度直方图垫底。**
  **这跟「高级特征一定更强」的直觉相反,得说清楚为什么:**
  **这几类数字的区别就在「笔画落在哪几格」,位置本身就是身份。**
  **梯度直方图恰恰把位置信息扔掉了——1 块版本只剩 8 个数,谁也认不出。**
  **把块数从 1 加到 4 再加到 16,准确率一路往上爬,正说明它扔的全是干货。**

[3] 但噪声一大,胜负就翻了

  同一套数据,把翻转概率一路加上去:

    特征                    5%       20%      30%      40%      
    原始像素                0.9750   0.7800   0.4500   0.2150   
    行列投影                0.9800   0.6500   0.3750   0.2600   
    梯度直方图 1 块         0.8100   0.3450   0.2150   0.2050   
    梯度直方图 4 块         0.9150   0.4650   0.3200   0.2350   
    梯度直方图 16 块        0.9550   0.5850   0.3800   0.1800   

  **转折点在这里:翻到 40%,原始像素掉到 0.2150,跟瞎猜的 0.2000 隔着一张纸;**
  **反而是 16 维的行列投影还有 0.2600。**

  **规律不复杂:**
  **特征越粗,单个像素翻错对它的影响越小,噪声一大就显出抗噪;**
  **代价是它一开始就丢了一截判别力,噪声小的时候白丢。**
  **所以「用哪个特征」的正确答案永远跟着噪声水平走,没有通吃的。**

[4] 抗噪和抗平移,是两件事

  前面每张图既加噪声又平移,混在一起看不清是谁在拖后腿。
  拆开量:同一张「3」,只加噪声,或者只平移,看特征变了多少。
  用余弦相似度,1.0000 表示一点没变:

    扰动                原始像素            行列投影            梯度直方图 1 块     
    只翻 20% 像素       0.7095              0.9066              0.9421              
    只平移 1 格         0.2488              0.6729              0.8837              

  **看得出来了:原始像素两头都最脆。**
  **平移一格就能让它相似度掉到 0.2488——因为笔画整体挪了位,64 个数全错位了。**
  **行列投影对平移掉得慢得多(0.6729):它只记「每行有几个黑点」,不记在哪一行。**
  **梯度直方图抗噪最强(0.9421),因为它压根不记像素值,只记方向。**

  **但请把这张表和上一张表对起来看:梯度直方图最稳,准确率却最低。**
  **稳定只是必要条件,不是充分条件——一个恒等于常数的特征最稳定,**
  **它对噪声的不变性和对类别的无差别,是同一件事。**

  最后补一枪:预处理。噪声 30% 时,加一步最土的 3 乘 3 均值滤波:

    流水线                    不用滤波   先滤波
    原始像素                  0.4200     0.4500
    行列投影                  0.3700     0.4000

  **涨得不多,但方向对了。这一步在流水线里是全自动的,**
  **所以「预处理」在工程上从来不算可选项。**

[5] 这条流水线在深度学习之前一直用

  五段连起来:**噪声小的时候像素和投影都在 0.9 以上,别急着上花活**;
  **噪声一上来,粗特征反而反超**;**抗噪和抗平移是两码事**;
  **而最稳的那个往往判别力最差**;**预处理是免费的几个点**。

  2012 年之前图像识别的标准做法就是这套:
  人工设计特征(SIFT、HOG、Haar)加一个分类器(SVM、随机森林)。
  它的死结也在这一节里摆着——特征得靠人挑,而挑法跟噪声水平、
  跟任务、跟数据量全挂钩,没有通吃的答案。
  9.4 那节讲的卷积核,就是把这个「挑」的活儿交给数据自己去学。

  下一节把同一套思路搬到声音上:像素换成波形采样点,
  梯度方向直方图换成频谱包络。

第 2 段那张表的排序值得记下来:64 维的原始像素赢过 128 维的梯度直方图。 这不是「深度学习没用」,是「在这个任务上位置就是身份」。 换个任务(比如从街景里找行人),排序就会反过来。 第 3 段那个 0.2150 对 0.2600 是转折点。 请注意 0.2000 是瞎猜的线——原始像素在 40% 噪声下已经贴着它走了。 第 4 段拆开量的做法本身值得学。 前面所有实验都是「噪声 + 平移」两个扰动一起上,得出的结论说不清是谁造成的。 把扰动控制成单因子,问题一下就清楚了。 第 4 段最后那几句是本节的骨头:稳定和有用不是一回事。 梯度直方图相似度 0.9421(最稳),准确率 0.5700(最差),这两个数放在一起才完整。

30 秒实验

  1. 把第 3 段的噪声加到 50%,重跑。 五种特征会全部塌到 0.2 附近——噪声超过信号的时候,什么特征都救不了,该做的是换传感器。
  2. 把第 2 段的梯度直方图块数从 16 加到 64(每块 1 个像素),重跑。 准确率会往原始像素靠——块小到极限,梯度直方图就退化成像素本身了。
  3. 把第 5 段的滤波换成 5 乘 5 均值,重跑。 30% 噪声下会再涨一点,但 10% 噪声下反而掉——滤波窗口太大,把笔画也抹平了。

常见误区

  1. 以为「特征越高级越好」。Demo 第 2 段:8 维梯度直方图只有 0.5700, 64 维原始像素 0.9650。位置是身份的时候,忘掉位置就是自杀。
  2. 以为「稳定的特征一定好用」。Demo 第 4 段:梯度直方图相似度 0.9421 最稳, 准确率 0.5700 最差。常数特征最稳,也最没用。
  3. 以为「噪声和位移可以混着算」。Demo 第 4 段拆开之后才看清: 原始像素掉分的主因是平移(0.2488),不是噪声(0.7095)。 混在一起量,你会把账算错。
  4. 以为「预处理总能加分」。它能对付已知类型的退化。 均值滤波对高斯噪声有效,对椒盐噪声一般,对运动模糊基本无效。 预处理不是万能的,它是针对性的。
自测三问(点开对答案) 1. 为什么 8 维的梯度直方图在这个任务上那么差? —— 因为**这几类数字的身份就写在「笔画落在哪几格」上,而梯度直方图把位置扔了**。 Demo 第 2 段:1 块版本只有 0.5700, **切到 4 块 0.7700,切到 16 块 0.8800,越细越好——正说明它扔掉的都是干货。** 2. 为什么噪声一大,粗特征反而反超? —— 因为**粗特征对单点扰动不敏感**。 Demo 第 3 段:40% 噪声下,**原始像素 0.2150(瞎猜是 0.2000),行列投影还有 0.2600。** **代价是噪声小的时候它白丢判别力——5% 噪声下像素 0.9750,投影 0.9800,其实差不多,** **但 10% 时像素 0.9650 对投影 0.9050,差距就出来了。** 3. 「稳定」和「好用」为什么不是一回事? —— 因为**稳定只要求「输入变了输出别变」,不要求「不同类别输出不同」**。 Demo 第 4 段:梯度直方图对噪声的相似度 0.9421,对平移 0.8837,两项都最稳; **可准确率 0.5700 最低。** **一个恒等于常数的特征,稳定到极点,判别力为零。**

下一节把同一套思路搬到声音上。 像素换成波形采样点,梯度方向直方图换成频谱包络。 会多出一个图像没有的麻烦:声音有长短,同一个词说快说慢,采样点数就不一样。 这个麻烦在图像里不存在,在语音里是头号问题。

相关笔记