📘 第9章 深度学习
📄 一句话概括

这一节讲一场持续了十几年的「怎么把网络叠深」的攻关。它有三个阶段: 先垫好初值(预训练)、再换掉激活(ReLU)、最后加上正则化和挑深度。 三个阶段有一个共同点:它们都在跟同一个东西较劲——那个连乘出来的衰减。 这一节不打算只给结论,而是把每一步的效果都量成一张表。

📝 本节和前面几节的关系

这一节是 9.1 和 9.2 的合流。9.1 说「深了梯度传不回去」,9.2 给了个能无监督学一层的模型, 这一节把两件事拼起来,看能不能救活一个深网络。 它跟 7.2 那个过拟合是同一个故事的两头。7.2 说过「训练集 1.0000 而测试集掉下去」, 这一节会说:深网络既容易学不动,也容易学太满,两件事得分开治。

一、那道坎到底有多高

先把「训不动」量成一个数。搭一个 25 个输入、几个隐层、10 个输出的网络, 算「第一层收到的梯度」跟「输出层的梯度」的比:

隐层数 参数量 第一层除输出层 每多一层乘多少
1 586 2.35e-01 —
2 858 4.73e-02 乘 0.20
4 1402 2.82e-03 乘 0.24
8 2490 8.89e-06 乘 0.24
每多一层,这个数就再乘 0.2 上下。八层下来只剩 11 万分之一。
注意这跟数据、跟任务都没关系——纯粹是「连乘」这件事的算术后果。
只要乘数小于 1,乘得够多就一定趋近于零。
这就是 2010 年之前「深网络训不动」的全部原因。
那时候的共识是「三层以上就别想了」——不是没人试,是试了真的不行。

二、老办法:用 RBM 把每一层先垫起来

2006 年那篇论文的想法很朴素:既然随机初始化不好,那就先无监督地学一批好初值。 关键在「逐层」两个字: 先用一个 RBM 学第一层,把它的隐单元激活当成「新数据」; 再用一个 RBM 学第二层……一路垫上去,最后才拼上输出层,用反向传播整体微调。

# 第一步:三个 RBM 依次垫上去,每层的隐单元激活当成下一层的「数据」
WP, BP, CUR = [], [], XTR
for nh in (32, 32, 32):
    W, c = rbm_train(CUR, nh)                  # 上一节那个 CD-1
    WP.append(W)
    BP.append(np.zeros((1, nh)))
    CUR = (sig(CUR.dot(W) + c) > r.random(...)).astype(float)   # 采成 0/1

# 第二步:拼上输出层,整体微调(这部分就是普通的反向传播)
WP.append(r.normal(0.0, np.sqrt(2.0 / 32), (32, 10)))
WP, BP = fit(WP, BP, u"sigmoid", XTR, LTR)

# 量「梯度传不传得回去」:第一层收到的梯度 ÷ 输出层的梯度
d = (softmax(As[-1]) - Y) / len(LTR)
out = abs(d).max()
for i in range(len(Ws) - 1, 0, -1):
    d = d.dot(Ws[i].T) * ad(As[i], kind)
print(abs(d).max() / out)                      # 越小越传不回去

为什么这能绕过梯度衰减?因为每个 RBM 只学一层——它自己那一层不存在「传不回去」的问题。 多层之间的配合,交给最后那次微调去收拾,而微调的起点已经离答案很近了。 Demo 拿 4 层(25-32-32-32-10)试了一遍:

初值怎么来 训练集 测试集 走到 0.9 要几步
随机初始化 1.0000 0.9100 300 步
三个 RBM 先垫一遍 1.0000 0.9800 50 步
测试集 0.9800 对 0.9100,而且起点离答案近得多(50 步对 300 步)。
差别不在参数量、不在训练步数,只在初值。
这就是那篇论文的贡献:它证明了「深」是可以训的,只要初值别乱给。
但也请注意它有多贵——三个 RBM 各训一遍,训练时间翻了三倍多。

三、新办法:直接把激活换掉

预训练虽然有效,但它是在绕路。真正的病根是「导数小于 1 还连乘」, 那为什么不换一个导数不小于 1 的激活函数? ReLU 的导数只有两个值:正的输入给 1,负的输入给 0。它不会越乘越小。同样 4 层 32, 三种激活各用自己最合适的学习率:

激活函数 学习率 第一层除输出层 训练集 测试集
sigmoid 0.10 1.04e-02 1.0000 0.9100
tanh 0.10 5.69e-01 1.0000 0.9400
relu 0.02 1.39e+00 1.0000 0.9200
ReLU 那一行的梯度比值是 sigmoid 的一百多倍,而且不需要任何预训练。
所以 ReLU 一到,逐层预训练就从「必需」变成了「历史」。
代价也很实在:神经元死亡。输入一直为负的那个单元,导数永远是 0,
它的权重再也不会更新——等于这个单元被永久关掉了。
这也是为什么 ReLU 的学习率要调得更小心。
顺便看清一件常被误解的事:最后准确率上三种差得不多(0.91 到 0.94)。
ReLU 的价值不在「这个任务上高两个点」,在「它能撑住更深的网络」——
而这张表只有 4 层。

四、还有个开关:初值给多大

换了激活,初始化也得跟着换,这两件事是一套的。 同一套结构、同一份数据,只改初始化时那个正态分布的标准差:

初始化 激活 第一层除输出层 训练集 测试集
标准差 0.01 tanh 1.31e-04 1.0000 0.9300
标准差 0.01 relu 1.11e-04 0.7500 0.7300
Xavier tanh 5.69e-01 1.0000 0.9400
Xavier relu 5.58e-01 1.0000 0.9400
He tanh 1.16e+00 1.0000 0.9500
He relu 1.39e+00 1.0000 0.9200
标准差 0.01 那一行最惨:信号一进网络就被压扁,第一层收到的梯度只剩 1e-04。
注意 ReLU 配小初值更糟(0.7500 训练集)——因为一半单元一上来就是负的,
直接死了,再也不会醒。
Xavier 和 He 的差别在分母:前者按「扇入」开根号除,后者按「扇入的一半」除。
多出来的那个 2 是给 ReLU 补的——它会把一半输出砍成 0,方差得先垫高一倍。
这就是深度学习工程的样子:没有一个「万能的好初值」,只有「跟你的激活函数配套的好初值」。

五、那到底该有多深

最后回到那个最实际的问题。固定宽度 32,只改隐层数:

隐层数 参数量 训练集 测试集 训练集减测试集
1 1162 1.0000 0.9600 0.0400
2 2218 1.0000 0.9700 0.0300
4 4330 1.0000 0.9700 0.0300
8 8554 1.0000 0.9200 0.0800
最好的不是最深那个:2 到 4 层最好(0.9700),8 层掉到 0.9200。
深一点表达力强,但也更容易背答案、更难训。这两件事是同时发生的。
所以「深度学习」这个名字里的「深」,是一个要试出来的超参,不是一个越大越好的方向。
真实工程里,模型深度通常由「数据量」和「算力」共同决定,不是拍脑袋定的。
把这一节收一下,深网络要跑起来需要四样东西,缺一样都不行:
要什么 治的是什么毛病 缺了会怎样
好激活 梯度连乘衰减 前面几层学不动
配套的好初始化 信号一进网络就被压扁 训练集都上不去
正则化(dropout 等) 参数量大、样本少 训练 1.0000 测试 0.6
够多的数据 上面三条都只是缓解 再怎么调也上不去
最后一条最容易被忽略。前面三条都是「怎么把已有的信息用好」,
而数据量决定的是「有多少信息可用」。10 倍的数据往往比 10 倍的调参有效。

六、动手 Demo:叠得越高,梯子越难爬

五段:梯度衰减、RBM 预训练、换激活、改初始化、挑深度。 第 2 段是历史,第 3 和第 4 段是现在的标准做法。 这一节的 Demo 跑得比较久(要训好几个网络),但每一段都只改一个变量。 看这种表的方法只有一个:找「只变了什么」,然后看「什么跟着变了」。

# 第一步:三个 RBM 依次垫上去,每层的隐单元激活当成下一层的「数据」
WP, BP, CUR = [], [], XTR
for nh in (32, 32, 32):
    W, c = rbm_train(CUR, nh)                  # 上一节那个 CD-1
    WP.append(W)
    BP.append(np.zeros((1, nh)))
    CUR = (sig(CUR.dot(W) + c) > r.random(...)).astype(float)   # 采成 0/1

# 第二步:拼上输出层,整体微调(这部分就是普通的反向传播)
WP.append(r.normal(0.0, np.sqrt(2.0 / 32), (32, 10)))
WP, BP = fit(WP, BP, u"sigmoid", XTR, LTR)

# 量「梯度传不传得回去」:第一层收到的梯度 ÷ 输出层的梯度
d = (softmax(As[-1]) - Y) / len(LTR)
out = abs(d).max()
for i in range(len(Ws) - 1, 0, -1):
    d = d.dot(Ws[i].T) * ad(As[i], kind)
print(abs(d).max() / out)                      # 越小越传不回去

实跑输出:

--------------------------------------------------------------------
叠得越高,梯子越难爬
--------------------------------------------------------------------
==============================================================

  数据沿用上一节的 5 乘 5 小图:10 种(5 横 5 竖),一共 400 张。
  **每张图随机翻掉 10% 的像素**,于是它变成一个「带噪识图」的活儿。
  300 张训练、100 张测试。**上一节那个 RBM,这一节正好拿来当第一层。**

[1] 深了训不动:梯度随层数一路缩水

  只看前向那一步是看不出问题的——**问题在反向。**
  把「第一层收到的梯度」跟「输出层的梯度」比一比:

    隐层数      参数量      第一层 ÷ 输出层     每层平摊下来
    ------------------------------------------------------------------------
    1           586         2.35e-01            —
    2           858         4.73e-02            ×0.20
    4           1402        2.82e-03            ×0.24
    8           2490        8.89e-06            ×0.24

  **每多一层,这个数就再乘一个明显小于 1 的因子。**八层下来只剩 11 万分之一,
  **第一层跟没在学差不多。**这就是 2010 年之前「深网络训不动」的全部原因。
  **注意它跟数据、跟任务都没关系——纯粹是「连乘」这件事的算术后果。**

[2] 老办法:拿上一节的 RBM 把每一层先垫起来

  想法很朴素:**既然随机初始化不好,那就先无监督地学一批好初值。**
  做法是**逐层来**:先用 RBM 学第一层,把它的隐单元激活当成「新数据」;
  再用第二个 RBM 学这一层……**一路垫上去,最后才拼输出层做微调。**

  三个 RBM 依次垫完,每一层的权重范围:
    第 1 层  -2.13 到 2.07(25 乘 32)
    第 2 层  -2.62 到 2.55(32 乘 32)
    第 3 层  -2.78 到 2.49(32 乘 32)
  **随机初始化只有 ±0.20 上下,而它自己长到了 ±2 上下。**

    初值怎么来              训练集      测试集      走到 0.9 要几步
    ------------------------------------------------------------------------
    随机初始化              1.0000      0.9100      300 步
    三个 RBM 先垫一遍       1.0000      0.9800      50 步

  **同样的结构、同样的步数、同样的学习率,只换了初值。**
  **测试集是 0.9800 对 0.9100**——差的那几点,就是「好初值」值钱的地方。
  **这就是 2006 年那篇论文的贡献:它证明了「深」是可以训的,只要初值别乱给。**

[3] 新办法:直接把 sigmoid 换掉

  **sigmoid 的导数最大 0.25,tanh 到 1.00,ReLU 的导数是 0 或 1。**
  换掉它,就不需要什么预训练了。同样 4 层 32,三种激活各用自己最合适的学习率:

    激活函数      学习率      第一层 ÷ 输出层     训练集      测试集
    ------------------------------------------------------------------------
    sigmoid       0.10        1.04e-02            1.0000      0.9100
    tanh          0.10        5.69e-01            1.0000      0.9400
    relu          0.02        1.39e+00            1.0000      0.9200

  **ReLU 那一行的梯度比值比 sigmoid 大了好几个数量级,而且不用预训练就到手了。**
  **代价是「神经元死亡」**:输入一直是负的那个单元导数为 0,它就永远不动了。
  **所以 ReLU 一到,逐层预训练就从「必需」变成了「历史」。**

[4] 还有个开关:初值给多大

  **同一套结构、同一份数据,只改初始化时那个正态分布的标准差:**

    初始化        激活      学习率    第一层 ÷ 输出层     训练集      测试集
    --------------------------------------------------------------------------------
    标准差 0.01   tanh      0.10      1.31e-04            1.0000      0.9300
    标准差 0.01   relu      0.02      1.11e-04            0.7500      0.7300
    Xavier        tanh      0.10      5.69e-01            1.0000      0.9400
    Xavier        relu      0.02      5.58e-01            1.0000      0.9400
    He            tanh      0.10      1.16e+00            1.0000      0.9500
    He            relu      0.02      1.39e+00            1.0000      0.9200

  **标准差 0.01 那一行最惨:信号一进网络就被压扁,第一层收到的梯度几乎为零。**
  **Xavier 和 He 的差别在分母:**前者按「扇入」除,后者按「扇入的一半」除,
  **多出来的那个 2 是给 ReLU 补的——它会把一半的输出砍成 0,方差得先垫高一倍。**
  **所以「换个初始化」这件小事,跟「换个激活」是一套的,必须配套改。**

[5] 那到底该有多深

  固定宽度 32,只改隐层数。数据回到 300 张,不加 dropout:

    隐层数      参数量      训练集      测试集      训练集减测试集
    ------------------------------------------------------------------
    1           1162        1.0000      0.9600      0.0400
    2           2218        1.0000      0.9700      0.0300
    4           4330        1.0000      0.9700      0.0300
    8           8554        1.0000      0.9200      0.0800

  **最好的往往是中间那档,不是最深那个:这次是 2 个隐层(0.9700)。**
  **深一点表达力强,但也更容易背答案、更难训。**这两件事是同时发生的。

  五段连起来:**深了梯度传不回去**,所以先垫好初值;
  **后来发现直接换激活更快**;**换了激活,初始化也得跟着换**;
  **最后发现,深度本身也得挑。**
  **深度学习的四件工具——好激活、好初始化、正则化、够多的数据——缺一样都跑不起来。**

第 1 段那个「每多一层乘 0.2」是最该记住的一行。它是纯算术, 跟数据无关、跟任务无关——只要你用 sigmoid 并且叠得够深,它一定发生。 第 2 段那个「50 步对 300 步」比 0.98 对 0.91 更能说明问题。 好的初值不只是让终点更高,是让起点更近。 第 3 段要看清一件事:最后准确率三种差不多,差别在梯度比值(1e-02 对 1e+00)。 只看准确率会得出「ReLU 没什么用」的错误结论——它的价值在更深的网络上才显出来。 第 4 段那个「标准差 0.01 配 ReLU 只有 0.75」是个很好的反面教材。 换个激活而不换初始化,等于把新引擎装到旧底盘上。 第 5 段是这一节唯一该带回工作中的表:深度是超参,要试。 而且「训练集减测试集」那一列比准确率更有信息量——它告诉你还有多少余量。

30 秒实验

  1. 把第 1 段的隐层宽度从 16 改成 64,重跑。 梯度比值会好看一些,但仍然随层数衰减——加宽只能缓解,不能改变「连乘」这个机制。
  2. 把第 2 段那三个 RBM 的训练轮数从 1200 改成 200,重跑。 预训练的优势会明显缩水——垫得不好,等于没垫。
  3. 把第 5 段的宽度从 32 改成 8,重跑。 最优深度会往后移——每层能装的东西少了,就得多叠几层;容量和深度是可以互相换的。

常见误区

  1. 以为「预训练是过时的技术,学它没用」。它是「为什么现在不需要它」的答案。 不理解那段历史,就看不懂 ReLU 和初始化为什么重要。
  2. 以为 ReLU 一定比 sigmoid 好。在 Demo 的 4 层网络上,三者最后差不多。 ReLU 的优势在深网络上,浅网络上它的优势是「好训」,不是「更准」。
  3. 以为「初始化随便给个小随机数就行」。Demo 第 4 段:标准差 0.01 让第一层梯度只剩 1e-04, 配 ReLU 更是直接死了三分之一的单元。
  4. 以为「越深越好」。Demo 第 5 段:8 层掉到 0.9200,比 2 层还低。 深度、宽度、数据量、正则化是四个要一起调的旋钮。
自测三问(点开对答案) 1. 逐层预训练为什么能绕开梯度衰减? —— 因为**它每次只训一层**——一个 RBM 只关心「这一层的隐单元怎么表示下一层的输入」, **它自己那一层不存在「梯度要传过很多层」的问题。** **多层之间的配合,留给最后那次整体微调**, 而微调的起点已经很接近答案了(Demo 里 50 步就到 0.9,随机初始化要 300 步)。 2. 既然 ReLU 这么好,为什么还会有「神经元死亡」这个说法? —— 因为**ReLU 对负输入的导数是 0**。 一个单元的输入一旦长期为负, **它的梯度永远是 0,权重再也不会更新——这个单元就被永久关掉了。**Demo 第 4 段里, 标准差 0.01 配 ReLU 只有 0.7500,就是大半个网络一上来就死了。 **所以 ReLU 要配更大的初始化和更小的学习率。** 3. 「深了掉分」的时候,怎么判断是过拟合还是训不动? —— **看训练集。**Demo 第 5 段里 8 层的训练集是 1.0000、测试集 0.9200,**那是过拟合**; 而第 1 段那种「第一层梯度只剩 1e-06」的情况,**训练集自己都上不去,那是训不动。** **一个看「训练集减测试集」,一个看「训练集本身」,两个数就能分开。**

下一节把「图是有空间结构的」这条信息也塞进网络里。 上一节那个全连接网络,把一张图摊成 64 个互不相关的数——可图里的规律明明是局部的、可搬的。 下一节的卷积就是干这件事。

相关笔记