跳到主要内容

打开黑箱 — 把模型每一层看到的东西画出来

这一章讲三件事: 大到画不下的数据怎么画; 模型内部每一层到底看到了什么(这是第 06 章欠下的那笔债); 以及一个反过来用的动作——不改权重,改输入它在全书链条里的位置: 第 08、09 两章讲了数据怎么进来、干不干净, 可数据和模型你都还看不见。这一章把两样都打开。 而它最后那个动作(对输入求梯度)会在第 22 章原样再用一次,那次是用来骗模型的

1. 先看现象:四十二万个点,画出来是一团黑

第 09 章那条纪律是「动手之前先看数据」。可数据一大,「看」这件事本身就成了问题。

书换了一份新数据来演示:耶拿气象数据集——德国耶拿气象站 2009 到 2017 年的记录, 42MB 的 CSV,15 列,每 10 分钟采一次1

8 年 × 365 天 × 24 小时 × 每小时 6 次 × 14 个指标 ≈ 42 万个数据点

全画出来会怎样?
① 浏览器标签页耗尽资源,变慢甚至崩掉
② 就算画出来了,人也看不出任何东西 —— 42 万个点是一团黑

图说:这是两个不同的上限。第一个是机器的,第二个是人的。
书说得很直白:「作为可视化设计师,应该用高效的方式将数据中最相关且最有价值的部分展现出来。」

书给了三招,合起来就能画2:

做什么效果
① 选时间跨度做一个下拉框:一天 / 一周 / 十天 / 一月 / 一年 / 全部;再配左右箭头翻页一次只画一段
② 降采样跨度大于一周时,每 6 个点取 1 个一个月 30 × 24 × 6 = 4320 个点降到 720 个,渲染开销大减,而肉眼看不出区别
③ 选列做两个下拉框,14 列里最多同时画两列一张图里线不超过两条

还有第四件必须做的事,而且它和第 03 章那道手续是同一个:标准化。

不标准化,把气温和气压画在同一张图上:
气温范围 −10 ~ 40
气压范围 980 ~ 1000
→ 纵轴被气压撑到 1000,气温那条线被压成贴着底的一条平线,什么也看不出来

标准化之后:两条线都以 0 为中心、上下浮动约 1,可以并排看

图说:第 03 章标准化是为了让训练稳定,这里是为了让图能看。
同一个动作,两个不同的理由。

画出来之后能看见什么? 书举了两个例子3:

  • 十天跨度下,气温有一个每天重复的波形(中午最高、午夜最低),外加一个十天的整体上升趋势; 而气压没有明显的周期;
  • 换成散点图,把大气密度对气温画出来,是一条明显的负相关——气温上升,密度下降。

这两件事,盯着 CSV 原始数据是绝对看不出来的。

2. 顶层全景:五种图,各管一件事

tfjs-vis 是一个和 TensorFlow.js 配套的可视化库,但它是单独的包,要单独引入4。 它的画图接口一共五种图,这张表就是这一节的全部内容5:

什么时候用书里的例子
折线图一个量随另一个有顺序的量变化训练集和验证集的损失随轮次变化(可以画在同一张图上)
散点图两个量配成一对,点之间没有顺序大气密度对气温
柱状图少数几个类别,每个一个值几个模型在同一个任务上的准确率
直方图只关心分布,不关心具体值一层的权重都分布在什么范围(第 11 章会用它)
热图一个二维数字网格,用颜色表示大小多分类的混淆矩阵(第 05 章);注意力矩阵(第 14 章)
柱状图和另外两种的一个关键差别(书专门点了):
折线图、散点图 —— 纵轴不一定从 0 开始
柱状图 —— 柱子通常「锚定」在 0,所以柱子的相对高度是能直接读的

图说:这就是为什么比较大小要用柱状图。

3. 承重节的前一半:兑现第 06 章那笔债

第 06 章说过:convnet 浅层认边和角、中层认眼睛鼻子、深层才认出这是一只猫。 那一章只给了一张示意图,没有一个数、没有一张实测图。这一节把证据拿出来。

做法只有一句话:多接几个输出

书用的模型是 VGG16——一个很有名的深度 convnet,拿 ImageNet 训的。 它有多大?权重文件 528MB,而第 07 章那个 MobileNet 不到 10MB6。 所以这一章的计算全放在 Node 上跑,不在浏览器里。

取内部输出的办法,你在第 07 章已经见过一半了7:

原模型: 图 → 层1 → 层2 → …… → 层N → 1000 个概率
↑ ↑ ↑
取它的输出 取它的 …… 取它的

用 tf.model({ inputs: 原模型的输入, outputs: [层1输出, 层2输出, …, 原输出] })
造一个「多输出模型」

然后 predict 一次 —— 一次调用同时拿到所有中间层的输出和最终结果。

图说:第 07 章截断模型是「只要中间某一层的输出」;这里是「中间的和最后的都要」。
同一个 tf.model()、同一套符号张量,换个用法而已。

看到了什么

书拿一张 cat.jpg 走了一遍,取了五个卷积层的输出,从浅到深8:

浅层(第 1 组卷积)
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 看得出猫的轮廓│ │ 一片黄和粉 │ │ 某个朝向的边 │ ……几十张
└─────────────┘ └─────────────┘ └─────────────┘
↑ 还很像原图。有的过滤器对黄色和粉色反应强,有的对某个方向的边缘反应强

中层(第 4 组卷积)
┌─────────────┐ ┌─────────────┐
│ 几团亮斑,位置│ │ 一些条纹 │ ……
│ 对着耳朵眼睛 │ │ │
└─────────────┘ └─────────────┘
↑ 已经不像原图了。书判断其中一个过滤器编码的是猫的面部特征(耳朵、眼睛、鼻子)

深层(第 5 组卷积)
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 几块亮斑 │ │ │ │ 两三个点 │ ……
│ │ │ 整片空白! │ │ │
└─────────────┘ └─────────────┘ └─────────────┘
↑ 和原图完全不像了。而且很多张是全空的

图说:这就是第 06 章那句话的证据。
⚠ 注意分辨率:书说为了排版把所有图缩成了同样大小,
实际上越深的层输出图越小(连续的卷积和池化压的),所以看起来越模糊。

书从这组图里读出了两件事,第二件是新的:

第一件(兑现第 06 章):越深越抽象。 靠近输入的层编码的是简单的视觉特征—— 边缘、颜色;越往深走,输出的模式就越远离原始像素9

第二件(第 06 章没有的):亮着的格子一层比一层少。 书管这个叫稀疏度随层深增加——第一层里所有过滤器都被这张图激活了, 而最后一层有些过滤器输出全是零9

空白说明什么? 说明那个过滤器要找的东西,这张图里没有。

于是书给了一个很准的说法10:

深度神经网络相当于一个信息提纯的流水线。 原始数据源源不断输进去,每一层剔除和当前任务无关的部分、放大有用的部分。 层越深,它携带的关于输入的信息越少,关于目标的信息越多。

注意最后那句话的分量:「关于输入的信息越少」——这不是缺陷,这是目的。

顺带一个警告:它看的可能不是你以为的东西

书在这里插了一个案例,它是第 22 章那个落点的第一个预兆11:

有研究者发现,一个模型把图里的狗误认成狼。原因是训练集里有狼的照片背景常常有雪, 而有狗的照片没有。 模型学到的其实是「有雪就是狼」。

模型是数据驱动的,所以它会照单全收数据里的偏差。 这一句和第 09 章那一整章是同一件事,只是这次你能亲眼看见它。

4. 承重节:把训练反过来做——对输入求梯度

这一节是本章的地基,也是全书最漂亮的一个动作。

先看现象:上一节只知道「这个过滤器被激活了」,不知道「它想找什么」

上一节看的是「这张图让哪些过滤器亮了」。换个问法更有用: 第 7 号过滤器,到底什么样的图会让它亮到最大?

办法:把训练的两头对调

回想第 02 章的训练:输入是固定的,权重是要改的。

常规训练(权重空间的梯度下降):
输入(固定) ──►[ 权重 ]──► 输出 ──► 损失
▲ │
└──── 反传,改权重 ──┘
目标:让损失变小

这一节要做的(输入空间的梯度上升):
输入(要改!) ──►[ 权重固定 ]──► 某个过滤器的输出
▲ │
└────────── 反传,改输入 ───────────┘
目标:让那个过滤器的输出变大

图说:同一套反向传播,两头对调 —— 一个改权重,一个改输入;
一个往下走(下降),一个往上爬(上升)。

具体怎么跑12:

① 造一张随机噪声图当起点
② 算「这个过滤器的输出」对「这张图每个像素」的梯度
③ 把梯度加到图上(往变大的方向走)
④ 重复 80 次

⚠ 中间有一个不起眼但很要紧的技巧:每一步都先把梯度标准化再用。
这样每一步对图的改动量级是一致的 —— 否则有的步子迈得巨大、有的几乎不动。

看到了什么

书对 VGG16 的四个卷积层各跑了一遍,结果和上一节互相印证13:

层的深度浮现出来的花样
最浅(第 1 组)整片的颜色,和某个朝向的边
中间(第 2 组)由几种边组合成的简单材质
更深颗粒、孔洞、彩条、羽毛、波浪——已经很像自然图像里的东西了

结论一句话:越往深走,它想找的花样越复杂、规模越大、越脱离单个像素。 而且同一层内不同过滤器找的东西差别很大——书的解读是: 同一层要从输入里榨出多个互补的表示,才能把有用信息最大化地提取出来13

这个动作的名字要记住:「对输入求梯度」。 它在这一章用来解释模型;第 22 章会用完全一样的动作去欺骗模型—— 一张大熊猫照片加上一层由「长臂猿」那个类别算出来的梯度,模型就改口说是长臂猿。 同一把钥匙,一次开锁,一次撬锁。

5. 主走查:一张 cat.jpg,从像素到「模型看的是这一块」

这一章的每个承重机制,在这条走查上各占一步。数字全部来自书里。

发生了什么具体的数 / 状态
1一张 cat.jpg 进入 VGG16模型权重文件 528MB(对照:MobileNet 不到 10MB)
2造一个多输出模型,一次拿到五个卷积层的输出 + 最终结果tf.model(),输出是一个数组
3看第 1 组卷积层的输出还很像原图:一张对黄和粉反应强,一张对某个朝向的边反应强
4看第 4 组不像原图了;书判断其中一个编码的是猫的面部(耳朵、眼睛、鼻子)
5看第 5 组只剩几块亮斑,而且有的整片空白 —— 空白 = 这张图里没有它要找的东西
6反过来做一次:固定权重,让一张随机噪声图去最大化某个过滤器迭代 80 次;浅层浮现出颜色和边,深层浮现出颗粒、羽毛、波浪
7看模型最终的判决「埃及猫」,概率 0.89
8问它:你凭图里的哪一块得出这个结论?拿这个概率对最后一层卷积的输出求梯度
9把梯度沿高、宽、样例三个方向求平均得到一串数,长度等于过滤器数——每个过滤器有多重要
10拿这串重要性去加权那一层的实际输出,再沿过滤器方向求平均得到一张 14×14 的灰度图
11负值归零,放大回 224×224,盖在原图上亮的地方正好是猫的头部轮廓

第 3~5 步是第 06 章那笔债的兑现;第 6 步和第 8 步是同一个动作的两种用法。

第 8~11 步那个热图,书里叫类激活图

它回答的问题是:输入图的哪些部分,对模型最终那个判决影响最大14

读法一句话:图上每个像素表示,如果这个位置的值增加一点,当前最高分那个类别的概率会变多少。

书给了三张实测热图15:

输入热图亮在哪书的解读
头部轮廓头部形状是猫的独特特征
猫头鹰头部和翅膀同上
两头大象两头的鼻子和耳朵鼻子长度和耳朵尺寸正是区分非洲象(模型的第一选择)和印度象(第三选择)的关键

大象那一张最值得玩味:它一次说明了三件事——热图能同时框住两个目标、 亮的地方确实是判别性最强的部位、以及这个模型的判断依据居然和人类的分类学依据对得上

6. 作者的判断与证据

书里给了证据的:

  • 越深越抽象、越稀疏。 有五组实测特征图,而且书指出最后一层里有过滤器输出全零9
  • 过滤器各自敏感于不同花样。 有四层的梯度上升结果图13
  • 模型的判决依据可以定位。 有三张类激活图,而且大象那张能分辨鼻子和耳朵15
  • 降采样六分之一肉眼看不出差别。 书直接这么说,而且给了绘图代码让你自己比2

属于作者判断、书里没给证据的:

  • 「深度神经网络相当于一个信息提纯的流水线。」 这是一个概括性说法, 书说它「对其他深度神经网络类型(比如 MLP)同样适用」——但只在 convnet 上演示过10
  • 对中层特征图的解读(「这个过滤器编码的是猫的面部特征」) 是作者看图得出的判断。 书自己承认:并不是所有层、尤其是后面数层的过滤器,都能用通俗易懂的语言解释9
  • 狗被误认成狼那个案例是书引用的别人的研究,不是它自己的实验11
  • 「黑箱」那个词的含义——书在脚注里给了自己的定义: 不是「拿不到内部数据」,而是「内部那些数学运算很难用常人能理解的话描述」16这是一个立场,不是一个事实。

判断(我们的,不是书里的): 这一章在全书里的作用,远大于它表面上的「教你画图」。 它是第 06 章那句话唯一的证据来源,也是第 22 章那个落点唯一的技术预演。 抽掉这一章,「越深越抽象」就永远只是一句说法,而「对抗样例」也会显得像魔法。 如果错,会错在: 如果读者只关心「怎么把模型训出来」,不关心「它凭什么这么判」, 那这一章确实可以跳——代价是第 22 章会完全读不懂。

7. 边界与局限

  • 可视化训练过程这一章不讲。 书自己说这是故意的:那件事和欠拟合、过拟合绑在一起, 整个留给第 11 章17
  • 这三个技巧只对 convnet 好用。 书自己承认这很大程度上是因为「它们学习的正是视觉特征」; 换成表格数据或文本,同样的图画出来没什么可看的。
  • 梯度上升那一步在浏览器里跑不动。 书把整套分析放在 Node 里跑, 原因是 VGG16 太大;书说小一点的 convnet 在浏览器里也许可行18
  • 类激活图不是唯一的解释办法。 书提了另一种叫 LIME 的,但只给了一个链接19
  • 热图的分辨率很粗。 原始热图只有 14×14,放大到 224×224 之后是一片模糊的色块—— 它能指出「大概哪一块」,指不出「哪个像素」。
  • 书没有讲怎么把这些图用于决策。 看到「模型在看雪不是在看狼」之后该怎么办, 书没有给流程。

8. 可带走的

  1. 数据大到画不下时,三招:选一段时间、每 N 个点取 1 个、只挑两列。 耶拿那份数据一个月 4320 个点降到 720 个,肉眼看不出区别;
  2. 多条量纲不同的线画在一张图上,先标准化。 气温 −1040 和气压 9801000 同图, 不标准化就只能看到一条平线;
  3. 五种图各管一件事: 折线看趋势、散点看两个量的关系、柱状看大小、直方看分布、热图看矩阵;
  4. 取模型中间层的输出,只要造一个多输出模型。 和第 07 章截断模型是同一套接口;
  5. 第 06 章那句话在这里被证实了: 同一张猫照片,第 1 组卷积层的输出还看得出边缘和色块, 第 5 组只剩几块亮斑;
  6. 还多了一条:亮着的格子一层比一层少,深层甚至整片空白。 空白 = 这张图里没有那个过滤器要找的东西;
  7. 深度网络是一条信息提纯流水线:层越深,关于输入的信息越少,关于目标的信息越多。 这是目的,不是缺陷;
  8. 训练可以反过来做:固定权重,改输入。 让一张噪声图去最大化某个过滤器的输出, 迭代 80 次,浮现出来的花样就是它想找的东西——浅层是颜色和边,深层是颗粒、羽毛、波浪;
  9. 类激活图能让模型指出它看的是哪一块: 拿最高分类别的概率对最后一层卷积输出求梯度, 平均成一张 14×14 的灰度图,放大盖回原图。猫那张亮在头部轮廓上;
  10. 模型看的可能不是你以为的东西。 有个案例是模型把狗认成狼, 因为训练集里狼的背景常有雪——它学到的是「有雪就是狼」。

9. 原文地图

主题原书章原文位置
tfjs-vis 是单独的包第 7 章text/19-ch07.txt:31(搜「分开管理」)
五种图与它们各自的用途第 7 章text/19-ch07.txt:43(搜「折线图」) · text/19-ch07.txt:91(搜「散点图」) · text/19-ch07.txt:115(搜「柱状图」) · text/19-ch07.txt:134(搜「直方图」) · text/19-ch07.txt:158(搜「热图」) · text/19-ch07.txt:182(搜「5种图的类型总结」)
柱状图锚定在 0第 7 章text/19-ch07.txt:115(搜「锚定」)
耶拿数据集:42MB、15 列、10 分钟一次第 7 章text/19-ch07.txt:216(搜「耶拿气象档案数据集」)
42 万个点、两个上限第 7 章text/19-ch07.txt:228(搜「42万多个数据点」) · text/19-ch07.txt:230(搜「人脑的信息处理能力也是有限的」)
三招:时间跨度、降采样、选列第 7 章text/19-ch07.txt:234(搜「时间跨度」) · text/19-ch07.txt:236(搜「降采样」) · text/19-ch07.txt:238(搜「Data Series 1」)
为什么可视化也要标准化第 7 章text/19-ch07.txt:274(搜「980~1000」)
气温的日周期与年周期;密度对气温负相关第 7 章text/19-ch07.txt:270(搜「每天重复的变化模式」) · text/19-ch07.txt:278(搜「负相关性」)
「黑箱」真正的含义第 7 章text/19-ch07.txt:288(搜「很难以常人能理解的话语描述」)
三个技巧的清单第 7 章text/19-ch07.txt:292(搜「可视化convnet中间层」)
VGG16 权重 528MB 对 MobileNet 不到 10MB第 7 章text/19-ch07.txt:305(搜「528MB」)
多输出模型取内部激活第 7 章text/19-ch07.txt:334(搜「compositeModel」)
浅层像原图、深层不像;稀疏度增加;空过滤器第 7 章text/19-ch07.txt:322(搜「箭头A」) · text/19-ch07.txt:324(搜「稀疏度」)
信息提纯流水线第 7 章text/19-ch07.txt:326(搜「信息提纯的流水线」)
狗被误认成狼(雪)第 7 章text/19-ch07.txt:328(搜「雪」)
输入空间的梯度上升:两头对调第 7 章text/19-ch07.txt:381(搜「调换输入和权重的角色」) · text/19-ch07.txt:383(搜「输入空间的梯度上升」)
梯度标准化的技巧;80 次迭代第 7 章text/19-ch07.txt:416(搜「标准化梯度值」) · text/19-ch07.txt:420(搜「80个迭代」)
四层浮现出的花样第 7 章text/19-ch07.txt:393(搜「全局性的颜色」) · text/19-ch07.txt:397(搜「羽毛」) · text/19-ch07.txt:399(搜「互补的表示」)
类激活图:是什么、埃及猫 0.89第 7 章text/19-ch07.txt:459(搜「类激活图」) · text/19-ch07.txt:459(搜「0.89」)
类激活图的五步与 14×14 升采样第 7 章text/19-ch07.txt:469(搜「增加一个单位」) · text/19-ch07.txt:479(搜「升采样」)
三张热图的解读(猫、猫头鹰、两头大象)第 7 章text/19-ch07.txt:461(搜「非洲象」)
训练过程的可视化留给下一章第 7 章text/19-ch07.txt:483(搜「故意省略了这两步中间的一步」)
LIME第 7 章text/19-ch07.txt:463(搜「LIME」)

Footnotes

  1. 出处:「第 7 章 可视化数据和模型」第 216 段(text/19-ch07.txt:216,搜「耶拿气象档案数据集」)。

  2. 出处:「第 7 章」第 228~238 段(text/19-ch07.txt:228,搜「42万多个数据点」;text/19-ch07.txt:236,搜「降采样」)。原文:每 6 个点取 1 个之后,「相较于原数据量,这极大地减少了渲染的开销。但是对于人眼而言,减少至原来 1/6 的数据量不会有任何影响」。 2

  3. 出处:「第 7 章」第 270 与 278 段(text/19-ch07.txt:270,搜「每天重复的变化模式」;text/19-ch07.txt:278,搜「负相关性」)。

  4. 出处:「第 7 章」第 31~39 段(text/19-ch07.txt:31,搜「分开管理」)。

  5. 出处:「第 7 章」表 7-1,第 182~206 段(text/19-ch07.txt:182,搜「5种图的类型总结」)。

  6. 出处:「第 7 章」脚注 5,第 305 段(text/19-ch07.txt:305,搜「528MB」)。

  7. 出处:「第 7 章」代码清单 7-8 与第 334 段(text/19-ch07.txt:334,搜「compositeModel」)。原文明说这和第 5 章的吃豆人、目标检测示例用的是同一个 tf.model() 接口。

  8. 出处:「第 7 章」图 7-8 说明,第 320 段与第 320 段(text/19-ch07.txt:320,搜「block1_conv1」;text/19-ch07.txt:322,搜「箭头A」)。五个层依次是 block1_conv1、block2_conv1、block3_conv2、block4_conv2、block5_conv3。

  9. 出处:「第 7 章」第 322~324 段(text/19-ch07.txt:324,搜「稀疏度」)。原文同段承认:「并不是所有层(尤其是后面数层)的过滤器都能用通俗易懂的语言来解释。」 2 3 4

  10. 出处:「第 7 章」第 326 段(text/19-ch07.txt:326,搜「信息提纯的流水线」)。 2

  11. 出处:「第 7 章」第 328 段(text/19-ch07.txt:328,搜「雪」)。这个案例出自 Marco Ribeiro 等人的论文(见第 7.3 节的延展阅读)。 2

  12. 出处:「第 7 章」代码清单 7-9 与第 416~420 段(text/19-ch07.txt:416,搜「标准化梯度值」;text/19-ch07.txt:420,搜「80个迭代」)。原文强调这个标准化技巧「重要但不易察觉」,它保证每次迭代对输入的更新量级一致。

  13. 出处:「第 7 章」第 393~399 段(text/19-ch07.txt:393,搜「全局性的颜色」;text/19-ch07.txt:397,搜「羽毛」;text/19-ch07.txt:399,搜「互补的表示」)。 2 3

  14. 出处:「第 7 章」第 457~469 段(text/19-ch07.txt:459,搜「类激活图」;text/19-ch07.txt:469,搜「增加一个单位」)。CAM 算法出自 Bolei Zhou 等人 2016 年的论文。

  15. 出处:「第 7 章」图 7-11 说明,第 461 段(text/19-ch07.txt:461,搜「非洲象」)。 2

  16. 出处:「第 7 章」脚注 4,第 288 段(text/19-ch07.txt:288,搜「很难以常人能理解的话语描述」)。原文拿决策树作对比:决策树的每一步都能用「因为 x 大于 0.35」这样的话讲出来。

  17. 出处:「第 7 章」第 483 段(text/19-ch07.txt:483,搜「故意省略了这两步中间的一步」)。

  18. 出处:「第 7 章」第 303 段与脚注 8,第 303 段(text/19-ch07.txt:303,搜「资源限制更小的Node.js环境」;text/19-ch07.txt:405,搜「更小的convnet」)。

  19. 出处:「第 7 章」脚注 9,第 463 段(text/19-ch07.txt:463,搜「LIME」)。