跳到主要内容

借用别人训好的模型 — 五十张图,几秒钟训完

这一章讲三件事: 为什么别人训好的模型能直接为你所用; 三种借法各自适用于什么情况;以及从模型中间截出来的那一串数为什么比原图更好用。 它在全书链条里的位置: 第 06 章那个 convnet 训完要几十万张图。 可现实里你常常只有五十张。这一章解决的正是这个落差—— 而它引出的那个概念(嵌入),会在第 13 章处理文字时再出现一次,那次是给词用的。

1. 先看两个你真会遇到的场景

书开篇给了两个场景,它们的共同点是:数据少得可怜1

场景一: 你的手写数字识别器上线了,但有一类用户的字迹和训练集差太远,识别得很糟。 你能从这些用户那里收到大约 50 张样例。50 张能干什么?

场景二: 你在做一个电商网站,想自动给用户上传的商品图分类。 这些商品属于某个细分领域,公开的现成模型都没有针对它训练过。 你能标注的图有几百张。

从头训一个 convnet 要多少图? 第 06 章那个 MNIST 模型用了六万张, 而那还只是 28×28 的灰度小图。真正好用的图像模型(比如书里要借的 MobileNet) 训练用的是 ImageNet:上百万张、上千个类别的有标签图像2

你不可能自己收集这个量级的数据。但你可以借别人已经训好的。

2. 顶层全景:借的到底是什么

别人干的活(一次性,很贵):
上百万张 ImageNet 图 ──训练──► 一个大 convnet(比如 MobileNet)

├─ 前面几十层:学到边、角、纹理、形状 …… 这部分是通用的
└─ 最后几层:「这是 ImageNet 那 1000 类里的哪一类」
↑ 这部分只对它自己的任务有用

你干的活(每次,很便宜):
50 张你自己的图 ──► 把上面那个模型的**前面几十层原样拿来**
再接一个你自己的小头部,只训这个头部
几秒钟训完

图说:借的是「前面那些层」。它们对「一张图里有哪些视觉花样」的理解,
和你的任务是什么无关 —— 这就是迁移学习成立的全部前提。

书给了正式的说法3:被借的那个训好的模型叫基模型; 这种事先在大数据集上训好、供人借用的模型,也常被叫作预训练模型; 拿它改出来的新模型叫迁移模型。关键词是**「不同但相关」**: 新任务要和原任务不一样(否则直接用就行了),但又得沾边(否则借来的东西没用)。

两大好处4:

好处具体是什么
数据少得多、算力少得多、时间少得多 —— 少到可以在浏览器里当场训
借来的特征提取能力是几十万张图砸出来的,你自己那五十张图无论如何训不出来

书还打了个很好记的比方:这和人学新东西一样—— 会打一种纸牌游戏之后,学另一种类似的会快得多4比方到此为止。

3. 借法一:输出形状一样,把前面几层冻住

这一节回答:最简单的那种情况怎么做。

书构造了一个能看清机制的例子:先只用 MNIST 里 04 这五个数字训一个模型, 再让它去认它从没见过的 595。任务类型没变(还是五分类), 输出形状也没变(还是五个概率)——这是最省事的一种。

做法只有两步,而且第二步是个坑。 (第一步那个动作,书里叫「固化」,英文是 freeze,中文更常见的译法是冻结; 本章统一说「冻住」,三个词是一回事。)

for (let i = 0; i < 7; ++i) this.model.layers[i].trainable = false; // ① 冻住前 7 层
this.model.compile({ ... }); // ② 必须重新 compile!

第 ② 步为什么是坑? 因为 trainable 这个属性不是设了就生效的compile() 除了配置优化器和损失函数,还会刷新「训练时该更新哪些权重」这份名单。 不重新 compile 就直接 fit,训练照样会更新所有层6

怎么确认冻住了?summary() 的最后三行7:

Total params: 600 165
Trainable params: 590 597
Non-trainable params: 9 568 ← 冻住的那两个卷积层的参数总数

图说:被冻的 7 层里有池化层和扁平化层,它们本来就没有参数,冻了也不增加这个数。
所以 9568 = 两个卷积层的参数之和。

冻住到底买到了什么:四个数

书做了三组对照,这一组数是这一章最该记住的8:

训一轮后的损失训一轮后的准确率最终准确率训练耗时
冻住前 7 层0.300.91约 0.9730 秒
不冻,全部一起训0.370.87约 0.9560 秒
权重全部重新随机初始化0.360.88约 0.95——

读这张表要读两件事,不要读错:

第一件,借权重是真的有用。 看第一行和第三行:同样的结构、同样的数据, 只差「用不用别人训好的权重」,起点和终点都明显不同(0.30 对 0.36,0.97 对 0.95)。

第二件,冻不冻的差别没有你想的那么大。 看第一行和第二行: 书自己的原话是「非固化策略并未导致结果的显著改进」8—— 两个点的差距,而且这一组对照本身也不是巨大的。

那冻住的卖点到底是什么?书给的是这三条:

  1. 起点更好。 一轮之后 0.30 对 0.37;
  2. 时间省一半。 30 秒对 60 秒——因为冻住的层不参与反向传播, 每一批的计算量少一大截9;
  3. 最终好两个点。 0.97 对 0.95。

书对「为什么起点更好」给的解释是这样的: 刚开始拿新数据训预训练模型时, 模型对新的五个数字实际上是在瞎猜,所以损失很高、梯度很大,所有权重会剧烈波动; 把前面几层冻住,等于把这段波动挡在了特征提取层之外10

注意措辞:书说的是「波动」,不是「毁掉」。 从第二行那组数看也确实如此—— 不冻的版本最终仍然到了 0.95,并没有崩。如果你在别处读到「不冻会把预训练权重冲毁」, 那不是这本书的说法。

4. 承重节:借法二,从中间截断,取出「嵌入」

这一节是本章的地基,也是这一章唯一一个会在后面章节复用的概念。

先看现象:输出形状对不上怎么办

上一节那个例子太顺了:新任务正好也是五分类。现实里几乎不会这么巧11:

  • 基模型是 ImageNet 训的,输出 1000 个类别;
  • 你的任务只有 4 个类别(比如「头往上 / 下 / 左 / 右」)。

形状对不上,fit() 根本跑不起来。

办法:把模型从中间切开

书做的事很直接:MobileNet 一共 93 层,只留前 87 层,后面 6 层整个不要12

完整的 MobileNet(93 层):
图 → [ 层1 … 层87:特征提取 ] → [ 层88…93:头部,专门答 1000 选 1 ] → 1000 个概率
└──────── 剪掉这一截 ────────┘

截断后(87 层):
图 → [ 层1 … 层87 ] → 一堆数 ← 这堆数就是这一节要讲的东西

图说:深度 convnet 最后那几层常被叫作「头部」。剪掉头部这个动作叫「截断」。
为什么剪 6 层?因为那 6 层只和「ImageNet 那 1000 类」有关,对你的四分类毫无用处。

代码里靠 getLayer('conv_pw_13_relu') 按名字取出那一层。 书特意说明为什么按名字而不是按序号: 93 层的模型按序号取太脆弱, 将来 MobileNet 加了层就全乱了;只要维护者保持关键层的命名不变,按名字取就可靠得多12

顺带一个新概念:符号张量

tf.model({inputs, outputs}) 这个写法要求你交出「输入」和「输出」。 可此刻模型还没跑,哪来的数?

交的不是数,是占位符。 书管它叫符号张量: 它不存具体的值,只声明形状和数据类型13

普通张量: [[0.2, 0.8], [0.5, 0.5]] 有形状,也有具体的数
符号张量: 形状 [null, 4],类型 float32 只有形状,一个数都没有

书打的比方:它就像静态类型语言里的**函数参数** —— 参数声明了类型,但不存值,
调用时才填进去。模型的输入输出就是这样的「参数声明」。
比方到此为止。

关键的一点(不说清就想不通下一步):符号张量不是孤零零的一个对象, 它带着「我是从哪几层算出来的」这个信息。 所以你把第 87 层的输出符号张量交给 tf.model(),它就能顺藤摸瓜把前 87 层整个抓出来14

截出来的那堆数,叫嵌入

对每一张 224×224 的输入图,截断版 MobileNet 吐出来的形状是 [1, 7, 7, 256]这既不是概率,也不是预测值。 那它是什么?

原图: 224 × 224 × 3 ≈ 15 万个数
截出来的: 7 × 7 × 256 ≈ 1.25 万个数

少了一个数量级,可它反而更好用。

图说:这一串数是这张图在一个「模型自己造出来的空间」里的坐标。
这种「对输入的低维表示」就叫**嵌入**(embedding)。

为什么小了却更好用? 因为这 1.25 万个数不是像素,是「这张图有哪些视觉花样」—— 边、角、纹理、块状结构,是那上百万张 ImageNet 图砸出来的判断力15。 原图那 15 万个像素里,绝大部分信息(亮度、精确位置、噪点)对分类根本没用。

记住「嵌入」这个名字,它出门会撞见。 第 13 章处理文字时会再出现一次: 那里每个也会有一串数,规则完全一样——意思相近的东西,这串数也相近。 两处的来源不同(这里是截模型,那里是一张可训练的表),但概念是同一个。

接上你自己的小头部

嵌入有了,再接一个三层的小模型就行16:

[7, 7, 256] 嵌入
↓ flatten
[12544]
↓ dense,ReLU ← 隐藏层,和第 04 章那种一模一样
[若干]
↓ dense 4,softmax ← 第 05 章那套多分类配方
[4] 四个方向各自的概率

注意这个头部有多小:三层。 而它前面站着一个 87 层的 MobileNet—— 你只训这三层,那 87 层一动不动。这就是「几秒钟训完」的全部原因。

5. 主走查:四个方向各五十张图,到一个能玩的游戏

这一章的每个承重机制,在这条走查上各占一步。 ⚠ 书没有给这个例子的准确率,只给了形状和帧率;所以这条走查里没有准确率。

发生了什么具体的数 / 状态
1按住屏幕上四个方框之一,摄像头开始录每秒采 20~30 帧;书建议每个方向至少 50 张,而且录的时候要轻轻晃头
2加载基模型MobileNet,93 层,从一个固定网址取 model.json
3按名字取出第 87 层,把后面 6 层剪掉getLayer('conv_pw_13_relu'),得到一个符号张量
4用这个符号张量造一个新模型tf.model({inputs, outputs}) → 一个 87 层的截断版
5每张图过一遍截断版,把输出存下来一张图 → [1, 7, 7, 256]1.25 万个数(原图是 15 万个)
6造一个三层的小头部扁平化 → 一个 ReLU 隐藏层 → 4 个单元的 softmax
7只训这个头部,输入是第 5 步存下来的嵌入几秒钟训完;损失一路降到很小的正数(比如 0.00010)后不再变
8玩的时候:摄像头每抓一帧,连着调两次 predict第一次:图 → 嵌入;第二次:嵌入 → 四个概率
9取概率最大的那个方向吃豆人往那个方向走

第 5 步是这条走查的心脏: 15 万个数变成 1.25 万个数,而且变得更好用。 第 7 步是这一章的卖点: 五十张图、几秒钟——这在第 06 章是不可想象的。

6. 这种双模型结构的三个软肋

上面那个做法有个特点:截断版和新头部是两个独立的模型对象。 好处是嵌入被明明白白地暴露出来了(书顺带介绍了一种直接用嵌入的非神经网络办法, 叫 K 近邻:算出新输入离哪几个已知样例最近,让它们投票)17

但它有三个软肋18:

  1. 代码啰嗦。 每次推断要调两次 predict();
  2. 存盘麻烦。 要存成两个模型文件,以后换个框架用更麻烦;
  3. 最要命的一条:没法微调——也就是没法回过头去,连基模型自己那几层一起再调一调。 两个独立对象之间,梯度传不过去, 所以你永远只能训头部,碰不到基模型的任何一层。

第 3 条正是下一节的入口。

7. 借法三:焊成一个模型,然后微调

这一节回答:怎么把最后那一点性能榨出来。

先看现象:光训头部,准确率卡住了

书换了个更难的例子:让第 06 章那个口令识别器去认四个发音极像的新词—— feelsealvealzeal。这四个词中间的元音和结尾的辅音完全一样, 开头的辅音也很接近,人在信号差的电话里都会听混19

只训新头部,一百轮之后验证准确率卡在约 84%,曲线开始走平20。 (书提醒:这时候只看训练集准确率是非常有误导性的,它轻轻松松就接近 100%。)

办法:先焊成一个模型

关键动作叫 apply()每个层和模型对象都有这个方法,它把自己「应用到」一个符号张量上, 返回一个新的符号张量21:

① 从基模型里取出倒数第二个密集层的输出符号张量 truncatedBaseOutput
② 造一个新的头部层(一个 softmax 密集层,输出 5 个词)
③ 新头部.apply(truncatedBaseOutput) ← 焊接就发生在这一步
④ tf.model({inputs: 基模型的输入, outputs: ③ 的结果})

图说:第 ③ 步之后,新头部和基模型的前半截连成了一张图。
于是第 ④ 步造出来的是**一个**端到端的模型:进去是时频谱,出来是新词的概率。
推断只要一次 predict,而且 —— 梯度现在能一路传回基模型了。

然后:解冻顶部几层,用很小的学习率再训

微调是初步训练之后的一个可选步骤,分三步22:

① 把基模型顶部的一层(或几层)的 trainable 改回 true
② 重新 compile ← 又是这一步!不重新 compile,解冻不生效
③ 再 fit 一轮,用**更小的学习率**

为什么学习率要更小?因为你这次动的是别人辛苦训好的权重,
步子迈大了会把它们推离原来的好位置。

成绩: 那四个混淆词的验证准确率从约 84% 提到 90~92%; 而作为对照,不做微调、只是把初步训练从 100 轮拉长到 400 轮,准确率停在约 85%20

▲ 验证准确率
92%│ ╭───────── 有微调:100 轮处有个明显的拐点
│ ╭───╯
85%│ ╭───────────┤ ← 拐点(这里解冻 + 重新 compile + 换小学习率)
│ ╭───╯ ╭─────────────────── 无微调:一路走平,停在 85%
│ ╭─╯ ╭──╯
└──┴────────┴──────────────────────▶ 训练轮次
0 100 400

图说:同样训 400 轮,差别只在 100 轮处做没做那一次解冻。

为什么微调有用? 书给的解释是:解冻顶部几层等于增加了模型的容量—— 可以在更高维的参数空间里去降损失。而且那些层原本是为老任务 (onetwoyesno 那些词)调好的,对新词不一定最优; 微调让它们的内部表示也针对新词优化一遍23

那多解冻几层是不是更好? 书的回答是「要看情况」: 容量越大,过拟合风险越大——尤其你手上的数据本来就少;而且训练也更慢24

三种借法的对照

书把三种做法列成了一张表25:

做法好在哪差在哪
① 冻住前几层,直接接着训简单、方便只在输出形状和激活函数都能对上时可用
② 截断取嵌入,另建一个小模型输出形状对不上也能用;嵌入被暴露出来,可以喂给 K 近邻这类别的算法要管两个模型对象;没法微调
③ 焊成一个端到端模型输出形状对不上也能用;只管一个对象;能微调拿不到嵌入

8. 借得更远一点:从分类跨到「画框」

这一节回答:借来的模型能不能干完全不同类型的活。

前面三种借法有个共同点:任务类型没变(分类借给分类)。 书最后演示了一次跨类型:拿一个为分类训的模型,去做目标检测—— 不只要说出图里是什么,还要说出它在哪儿26

数据是合成的:224×224 的白底图,中间放一个三角形或长方形(大小、朝向随机), 再随机撒 10 个圆和 10 条线段当噪声,有些还会盖住目标27合成数据有两个好处:标签自动就有,而且要多少有多少28

模型要输出 5 个数29:

[ 形状, 左, 右, 上, 下 ]
│ └──── 4 个数:目标周围那个框的四条边坐标(取值 0~224)
└────────── 1 个数:三角形还是长方形

这里出了一个新问题,而且它的解法后面还会再用一次。

两路误差量级差太远,得配平

模型的最后一层用的是默认的线性激活(不加任何东西),那这 5 个数各自是什么意思, 是由损失函数定的,不是由模型定的30

书写了一个自定义损失函数,做的事只有一步31:

正确答案 yTrue 的第一列(形状标签,值是 0 或 1) × 224
其余四列不动
然后和模型输出算均方误差

为什么?
边框那四个数的取值范围是 0 ~ 224
形状那个数的取值范围是 0 ~ 1
→ 不配平的话,形状那一路的误差信号**会被边框那一路完全淹没**,模型学不会判形状。

图说:乘完之后,推断时的判据也跟着变 —— 不是「输出大于 0.5 就是长方形」,
而是「输出大于 112(也就是 224 的一半)就是长方形」。

这个手法在第 16 章还会再出现一次。 那一章的 VAE 也要同时算两笔量级差很远的账, 用的是同一个配平思路。书没有点破这条线,是我们接上的。

训练分两个阶段,和上一节一样:先只训新头部(学习率 5e-3), 再解冻基模型顶部 9 层做微调(学习率降到 2e-3,同时把批尺寸减半省内存)32

9. 作者的判断与证据

书里给了证据的:

  • 借权重比从头训好。 有三组对照数据(起点损失 0.30 / 0.36 / 0.37,最终 0.97 / 0.95 / 0.95)8
  • 冻住能省一半时间。 30 秒对 60 秒,而且给了机制解释(冻住的层不参与反向传播)9
  • 微调能突破瓶颈。 84% → 90~92%,并且有「不微调、同样训 400 轮只到 85%」的对照20
  • 嵌入比原图小一个数量级。 1.25 万对 15 万,是算出来的15
  • 不重新 compile 就不生效。 书把验证方法写成了练习题,让你自己去打印可训练参数数33

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

  • 「迁移学习和人类学新任务的方式相同。」 这是一个类比4
  • 「多解冻几层要看情况。」 书给的是权衡原则,不是实验24
  • 合成数据那一段说「这种方式非常高效,被广泛用于测试和原型设计」——这是行业经验28

判断(我们的,不是书里的): 这一章在 2019 年写的时候,「迁移学习」几乎等于 「复用卷积特征提取层」。今天更常见的做法是拿一个通用基础模型 + 少量样例做提示或微调—— 载体变了,但这一章那条「借前面、换后面」的推理一点没变如果错,会错在: 如果某天的主流做法变成「每个任务都从头训一个专用小模型」 (比如因为训练成本骤降),那么这一章的价值就只剩历史意义了。 目前看不到这个迹象。补充(不在书里,来自通用知识)。

10. 边界与局限

  • 「不同但相关」里的「相关」没有量化标准。 书没有给出「什么时候借不动」的判据, 只举了正面例子(59 和 04 都是黑底灰度、笔画相似)34
  • 数据集太小时验证集会失真。 书为此专门写了一个函数:按类别均衡地切验证集, 因为四个词各八个样例、随机切两成的话,很可能某个词在验证集里一个样例都没有35这是这一章最实用的一个工程细节。
  • 微调只解冻了一层(口令那个例子)或九层(目标检测那个例子)。 该解几层,书说要看情况。
  • 目标检测那个例子极其简化。 书自己在信息栏里列了它和真实系统的五点差距: 真实系统类别多得多(COCO 有 80 类)、一张图能检测多个目标、架构上会挂多个头部、 损失是两种损失的加权和、还会先生成一堆候选框再裁剪36
  • 数据本身的问题一个字没提。 这一章所有数据集都是洗干净的。 书在末尾自己说:机器学习从业者绝大部分时间花在数据的获取、预处理、清洗上—— 那是第 08、09 章的事37

11. 可带走的

  1. 别人在百万张图上训好的模型,前面那些层学到的边角纹理对你的任务同样有用。 借它,你只要换掉最后几层;
  2. 三种借法,按「输出形状对不对得上」和「要不要微调」来挑: 冻住前几层直接训 / 截断取嵌入另建模型 / 焊成一个端到端模型;
  3. 改了 trainable 一定要重新 compile 不重新编译,冻住不生效——这是这一章最容易踩的坑;
  4. 冻住买到三样:起点更好(0.30 对 0.37)、时间省一半(30 秒对 60 秒)、最终好两个点。 书自己说非固化「并未导致结果的显著改进」——别把它说成「不冻会毁掉权重」;
  5. 截断 = 按名字取出中间某一层,把后面的头部剪掉。 MobileNet 93 层只留 87 层;
  6. 嵌入 = 从模型中间截出来的那串数。 一张图从 15 万个数变成 1.25 万个, 少一个数量级却更好用,因为它记的是视觉花样不是像素;
  7. 符号张量 = 只有形状没有值的占位符,而且它带着「我从哪几层来」的信息—— 靠这一点才能把模型截开、焊上;
  8. 微调 = 初步训练之后,解冻顶部几层、重新 compile、用更小的学习率再训一轮。 实测 84% → 90~92%;不做微调、光训久一点只到 85%;
  9. 借来的模型能跨任务类型用:分类模型能改成「画框」的回归模型;
  10. 多个任务合到一个损失里时要配平量级。 那个例子里形状标签要乘 224, 否则形状那一路的信号会被边框那一路淹没。第 16 章还会用同一个手法。

12. 原文地图

主题原书章原文位置
两个开场场景(50 张手写图、电商商品图)第 5 章text/16-ch05.txt:15(搜「50个样例」)
迁移学习定义、基模型与迁移模型、模型自适应第 5 章text/16-ch05.txt:23(搜「基模型」) · text/16-ch05.txt:29(搜「模型自适应」)
两大优势、和人类学习的类比、ImageNet 规模第 5 章text/16-ch05.txt:39(搜「玩一种纸牌游戏」) · text/16-ch05.txt:41(搜「上百万个来自上千个类别」)
04 训、59 迁移;96%、30 秒第 5 章text/16-ch05.txt:47(搜「前5个数字」) · text/16-ch05.txt:52(搜「96%的识别准确率」)
固化前 7 层的代码;必须重新 compile第 5 章text/16-ch05.txt:66(搜「固化」) · text/16-ch05.txt:89(搜「否则trainable属性不会生效」)
600165 / 590597 / 9568第 5 章text/16-ch05.txt:91(搜「600165」) · text/16-ch05.txt:94(搜「9568」)
三组对照的数;「并未导致结果的显著改进」第 5 章text/16-ch05.txt:117(搜「重新随机初始化权重参数使损失的起点变高很多」) · text/16-ch05.txt:121(搜「损失值的起点比之前更高」) · text/16-ch05.txt:123(搜「并未导致结果的显著改进」)
30 秒对 60 秒;固化层不参与反向传播第 5 章text/16-ch05.txt:127(搜「两倍的时间」) · text/16-ch05.txt:127(搜「不会参与反向传播」)
输出形状对不上的三种情形第 5 章text/16-ch05.txt:137(搜「输出形状和新模型的输出形状相同」) · text/16-ch05.txt:141(搜「3种类型」)
摄像头采集:20~30 帧、每方向 ≥50 张第 5 章text/16-ch05.txt:153(搜「20~30帧」) · text/16-ch05.txt:159(搜「至少收集50张图像」)
按名字取层、MobileNet 93 层、截断前 87 层第 5 章text/16-ch05.txt:167(搜「conv_pw_13_relu」) · text/16-ch05.txt:222(搜「前87层」)
符号张量:占位符、带着子图第 5 章text/16-ch05.txt:216(搜「符号张量」) · text/16-ch05.txt:222(搜「并不是独立的对象」)
嵌入:1.25 万对 15 万第 5 章text/16-ch05.txt:230(搜「1.25万个维度」)
新头部三层第 5 章text/16-ch05.txt:267(搜「输入层是一个扁平化层」)
双模型的三个软肋第 5 章text/16-ch05.txt:301(搜「调用两次predict」)
K 近邻信息栏第 5 章text/16-ch05.txt:311(搜「KNN算法不需要训练」)
apply() 焊接、单模型方案第 5 章text/16-ch05.txt:399(搜「apply()方法」) · text/16-ch05.txt:407(搜「反向传播」)
四个混淆词、84% → 90~92%、无微调 85%第 5 章text/16-ch05.txt:361(搜「feel」) · text/16-ch05.txt:493(搜「90%~92%」) · text/16-ch05.txt:497(搜「无微调则停滞在约0.85」)
微调为什么有用;多解冻几层要看情况第 5 章text/16-ch05.txt:499(搜「增加了模型的容量」) · text/16-ch05.txt:501(搜「更大的过拟合风险」)
均衡切验证集第 5 章text/16-ch05.txt:491(搜「balancedTrainValSplit」)
三种做法的对照表 5-1第 5 章text/16-ch05.txt:505(搜「三种迁移学习方法的优势和劣势」)
目标检测:合成场景、5 个输出第 5 章text/16-ch05.txt:559(搜「噪声目标」) · text/16-ch05.txt:561(搜「5个数字」)
自定义损失函数与 ×224 配平第 5 章text/16-ch05.txt:634(搜「函数签名」) · text/16-ch05.txt:644(搜「labelMultiplier」) · text/16-ch05.txt:655(搜「完全淹没」)
两阶段训练与学习率第 5 章text/16-ch05.txt:666(搜「5e-3」) · text/16-ch05.txt:687(搜「减小batchSize」)
真实目标检测系统的五点差距第 5 章text/16-ch05.txt:704(搜「不同之处」)
从业者大部分时间花在数据上第 5 章text/16-ch05.txt:737(搜「预处理、清洗」)

Footnotes

  1. 出处:「第 5 章 迁移学习:复用预训练的神经网络」第 15 段(text/16-ch05.txt:15,搜「50个样例」)。

  2. 出处:「第 5 章」第 41 段(text/16-ch05.txt:41,搜「上百万个来自上千个类别」)。书里点名的、用 ImageNet 训出来的模型有 ResNet、Inception 和 MobileNet。

  3. 出处:「第 5 章」第 23 与 27~31 段(text/16-ch05.txt:23,搜「基模型」;text/16-ch05.txt:29,搜「模型自适应」)。书把「新数据不同但任务相同」的那一类专门叫作模型自适应。

  4. 出处:「第 5 章」第 33~39 段(text/16-ch05.txt:39,搜「玩一种纸牌游戏」)。 2 3

  5. 出处:「第 5 章」第 47~52 段(text/16-ch05.txt:47,搜「前5个数字」;text/16-ch05.txt:52,搜「96%的识别准确率」)。原文说这个例子「看起来有点刻意,但它诠释了迁移学习的基本工作流程」。

  6. 出处:「第 5 章」第 89 段(text/16-ch05.txt:89,搜「否则trainable属性不会生效」)。原文:compile() 会「刷新训练时应该更新的权重变量的列表」。

  7. 出处:「第 5 章」第 91~94 段(text/16-ch05.txt:91,搜「600165」;text/16-ch05.txt:94,搜「9568」)。原文明确说 9568 等于两个有权重的固化层(两个卷积层)的参数总和,而池化层和扁平化层本来就没有权重。

  8. 出处:「第 5 章」第 117123 段(text/16-ch05.txt:117,搜「重新随机初始化权重参数使损失的起点变高很多」;text/16-ch05.txt:121,搜「损失值的起点比之前更高」;text/16-ch05.txt:123,搜「并未导致结果的显著改进」)。**这一组数全部来自 MNIST 那个例子(04 迁到 5~9),不是摄像头那 50 张图的例子。** 2 3

  9. 出处:「第 5 章」第 127~129 段(text/16-ch05.txt:127,搜「两倍的时间」;text/16-ch05.txt:127,搜「不会参与反向传播」)。注意: 书在第 52 段说整个迁移过程约 30 秒,在第 115 段又说约 15 秒;第 127 段那组 30 秒对 60 秒是直接对照,本章采用后者。 2

  10. 出处:「第 5 章」第 121 段(text/16-ch05.txt:121,搜「大幅波动」)。原文:训练早期损失很高、曲线斜率很大,梯度值非常大,「会导致模型所有权重值的大幅波动」;固化把这段波动「屏蔽」在外。

  11. 出处:「第 5 章」第 137 段(text/16-ch05.txt:137,搜「输出形状和新模型的输出形状相同」)。

  12. 出处:「第 5 章」代码清单 5-3 与第 167、222 段(text/16-ch05.txt:167,搜「conv_pw_13_relu」;text/16-ch05.txt:222,搜「前87层」)。 2

  13. 出处:「第 5 章」第 216~218 段(text/16-ch05.txt:216,搜「符号张量」)。函数参数那个比方是原书打的。

  14. 出处:「第 5 章」第 222 段(text/16-ch05.txt:222,搜「并不是独立的对象」)。原文:原始模型是符号张量组成的图,层是连接它们的边;把输入输出符号张量交给 tf.model(),实际是从原图里提取一个子图。

  15. 出处:「第 5 章」第 230 段(text/16-ch05.txt:230,搜「1.25万个维度」)。原文:「这种对输入的低维表示叫作嵌入。」 2

  16. 出处:「第 5 章」代码清单 5-5 与第 267~271 段(text/16-ch05.txt:267,搜「输入层是一个扁平化层」)。

  17. 出处:「第 5 章」信息栏 5-2,第 309~335 段(text/16-ch05.txt:311,搜「KNN算法不需要训练」;text/16-ch05.txt:331,搜「可扩展性并不太好」)。书同时指出 K 近邻的软肋:参考样例越多,每次推断要算的距离越多;而神经网络推断的计算量和训练集大小无关。

  18. 出处:「第 5 章」第 299~305 段(text/16-ch05.txt:301,搜「调用两次predict」;text/16-ch05.txt:305,搜「就不能进行反向传播」)。

  19. 出处:「第 5 章」第 361 段(text/16-ch05.txt:361,搜「feel」)。

  20. 出处:「第 5 章」第 493~497 段(text/16-ch05.txt:493,搜「90%~92%」;text/16-ch05.txt:497,搜「无微调则停滞在约0.85」)。原文提醒:训练集准确率「轻轻松松地就达到了接近100%的准确率」,单看它非常有误导性。 2 3

  21. 出处:「第 5 章」代码清单 5-7 与第 399~403 段(text/16-ch05.txt:399,搜「apply()方法」)。

  22. 出处:「第 5 章」代码清单 5-8 与第 411、487 段(text/16-ch05.txt:411,搜「解除固化」;text/16-ch05.txt:487,搜「都必须再次调用模型的compile()方法」)。

  23. 出处:「第 5 章」第 499 段(text/16-ch05.txt:499,搜「增加了模型的容量」)。

  24. 出处:「第 5 章」第 501 段(text/16-ch05.txt:501,搜「更大的过拟合风险」)。 2

  25. 出处:「第 5 章」表 5-1,第 505~521 段(text/16-ch05.txt:505,搜「三种迁移学习方法的优势和劣势」)。

  26. 出处:「第 5 章」第 531~533 段(text/16-ch05.txt:531,搜「跨领域迁移学习」;text/16-ch05.txt:533,搜「目标在图像中的位置」)。

  27. 出处:「第 5 章」第 559 段(text/16-ch05.txt:559,搜「噪声目标」)。

  28. 出处:「第 5 章」第 569 段(text/16-ch05.txt:569,搜「自动得到数据真正的标签」)。原文也提醒:要用到真实图像上,仍然需要人工标注的真实场景数据(比如 COCO 数据集)。 2

  29. 出处:「第 5 章」第 561~565 段(text/16-ch05.txt:561,搜「5个数字」)。

  30. 出处:「第 5 章」第 634 段(text/16-ch05.txt:634,搜「函数签名」)。原文:「决定这些输出含义的并不是模型本身,而是模型的损失函数。」

  31. 出处:「第 5 章」代码清单 5-10 与第 651~655 段(text/16-ch05.txt:644,搜「labelMultiplier」;text/16-ch05.txt:655,搜「完全淹没」)。推断阈值改成 CANVAS_SIZE/2(即 112)见第 651 段(text/16-ch05.txt:651,搜「CANVAS_SIZE/2」)。

  32. 出处:「第 5 章」代码清单 5-11,第 663~689 段(text/16-ch05.txt:666,搜「5e-3」;text/16-ch05.txt:687,搜「减小batchSize」)。解冻的是截断版 MobileNet 顶部的 9 层(text/16-ch05.txt:659,搜「顶部的9层」)。

  33. 出处:「第 5 章」练习(1),第 743~747 段(text/16-ch05.txt:745,搜「留意打印出的可训练参数」)。

  34. 出处:「第 5 章」第 117 段(text/16-ch05.txt:117,搜「黑色背景的灰度图像」)。原文列举的相似之处是:都是黑底灰度图,笔画粗细和曲线模式相似。

  35. 出处:「第 5 章」第 489~491 段与脚注 8(text/16-ch05.txt:491,搜「balancedTrainValSplit」;text/16-ch05.txt:523,搜「至少8个样本」)。

  36. 出处:「第 5 章」信息栏 5-3,第 694~727 段(text/16-ch05.txt:704,搜「不同之处」)。书点名的真实系统是 SSD 和 YOLO,并给出了可在 TensorFlow.js 里直接用的 coco-ssd

  37. 出处:「第 5 章」第 735~737 段(text/16-ch05.txt:737,搜「预处理、清洗」)。