JOVANA
Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

为什么深度网络难以训练

你可以堆叠一百层——但直到大约 2010 年,训练它们几乎都行不通。本文讲清楚深度网络为何会被自己的梯度噎住,以及最终让深度成为可能的那些出人意料的简单修复方法。

谜题:深度本应有帮助,但当初却没有

现在你已经搭好了整台机器:一个做加权求和的人工神经元,把层堆叠成的多层感知机,把输入变成预测的前向传播,以及把误差信号送回去微调每一个权重反向传播。理论上,层数越多能力越强——通用逼近定理甚至承诺:只要网络足够宽,就能拟合几乎任何函数。所以配方看起来很明显:想要更聪明的网络?多加几层。

几十年来,这个配方一直失败。在整个 1990 和 2000 年代,研究者发现:层数超过几层的网络训练得很慢,甚至根本学不动——深层版本往往*还不如*浅层版本,即使在训练数据上也是如此。这不是过拟合(死记训练集);而是更诡异的东西。网络就是学不会。罪魁祸首原来就藏在反向传播自己内部。

梯度消失与梯度爆炸

回想反向传播是怎么工作的:要算出某个靠前的权重该改变多少,链式法则会把这个权重与损失之间每一层的局部斜率连乘起来。网络越深,这条连乘链就越长。而把很多个数连乘有一个残酷的性质:如果它们大多小于 1,乘积会飞速冲向零;如果它们大多大于 1,则会爆炸般冲向无穷。

这就是梯度消失问题(以及它邪恶的孪生兄弟:梯度爆炸)。经典的罪魁是 sigmoid 激活函数。它的斜率在中间最陡,但当输入很大或很小时会压平到几乎为零——而且它的最大斜率也只有 0.25。把十个 sigmoid 串起来,到达第一层的梯度最多只有 0.25¹⁰ ≈ 原来的百万分之一。最靠前的那些层——本应学习最基础特征的地方——几乎得不到任何信号,只能冻结在随机的初始值上。

# gradient reaching layer 1 = product of per-layer slopes
grad_L1 = slope_10 * slope_9 * ... * slope_2 * slope_1

# if every slope ~ 0.25 (sigmoid's best case):
0.25 ** 10  = 0.00000095   # vanished

# if every slope ~ 1.5 (poorly scaled weights):
1.5  ** 10  = 57.7         # exploded
一长串连乘要么坍缩为零,要么炸开——这正是深度难以训练的核心原因。

梯度爆炸则是镜像情形:当乘积增大时,权重更新变得巨大而失控,损失跳成 `NaN`,训练直接引爆。这两种失败有同一个根源——梯度的*大小*会随深度被连乘放大或缩小,而我们必须让这个累积乘积一路上下都保持在 1 附近,既不收缩也不膨胀。

修复一:从正确的尺度起步(权重初始化)

第一个杠杆是你从*哪里*起步。每个网络都从随机权重开始——但这种随机的尺度极其关键。初始权重太大,信号(和梯度)就会逐层放大、爆炸;太小,则会层层缩水、消失。把尺度调对,信号就能一路保持大致恒定的大小。这个选择叫做权重初始化,它正是那种看似无足轻重、却决定一个深度网络到底能不能学起来的细节。

2010 年的想法(Xavier/Glorot 初始化)简单得让人意外:给随机权重设定尺度,使信号的*方差*在每一层之后保持不变。粗略地说,就是除以输入连接数的平方根,这样一个汇总许多输入的神经元就不会炸开。2015 年的改进(He 初始化)则针对会砍掉一半输入的 ReLU 层调整了那个常数。没有新的数学机器——只是把起始旋钮调到合适的位置,让那条连乘链一开始就是平衡的。

修复二:更好的激活函数,以及给梯度拴上缰绳

第二个杠杆是激活函数本身——这是最直接干掉梯度消失的改动。把会饱和的 sigmoid 换成 ReLU(正数就原样输出、否则输出零),它对每个正值的斜率恰好是 1。斜率为 1 时,梯度穿过它不会被缩小;串上一百个,信号仍然存活。这个简单到几乎令人尴尬的改动,正是 2012 年深度学习突破得以发生的重要原因之一。

ReLU 并不完美——卡在负区的神经元会永远输出零(“ReLU 死亡”),这正是 leaky ReLU 等变体存在的原因。要诚实地说:这些都是务实的工程修复,而不是一套干净的理论。对于*爆炸*那一侧,缰绳是梯度裁剪:如果梯度的整体长度超过阈值,就在更新前把它按比例缩回去。手法粗糙,却能可靠地阻止损失引爆——尤其在循环网络里,因为同一组权重被反复施加、链条会变得极长。

修复三:归一化信号,并给梯度一条捷径

良好的初始化只在训练*开始*时设定好尺度,但随着权重变化,信号可能又漂回爆炸或消失。修复办法是在训练*过程中*、在每一层把激活值重新居中、重新缩放。这就是批归一化及其表亲层归一化背后的想法:让每一层的输出保持在健康的范围内,这样无论权重怎么变,梯度都表现良好。下一阶梯会完整介绍它们;现在只需知道,它们的存在是为了让那条连乘链在整个训练过程中、而不只是开头,都保持在 1 附近。

最优雅的修复是残差连接(2015),它是 100 多层网络背后的诀窍。与其逼着每个模块去变换信号,不如让它在输入上*加*一个小的调整,同时让原始信号原封不动地穿过:`输出 = 输入 + 模块(输入)`。这小小的 `+ 输入` 给了梯度一条直通前面各层的高速公路,完全绕开了那条漫长的连乘链。一夜之间,更多的层真的可以意味着更好的结果——那个古老的配方终于奏效了。

这到底意味着什么(以及不意味着什么)

退一步,看看这个故事的形状。深度学习在 2012 年腾飞,并不是因为有人发现了关于智能的某种深奥新理论。它腾飞,是因为一批朴实无华的修复——ReLU、合理的初始化、归一化、残差连接,再加上快得多的 GPU 和更大的数据集——合在一起,让梯度下降在深度网络上*真正能用了*。反向传播的数学从未改变。我们只是学会了如何让它的信号穿越许多层而不死掉。

有两个迷思值得抵制。第一,“越深越好”是错的——超过某个点,多加的层只会带来成本和不稳定却毫无收益,而且深度永远替代不了好数据。第二,这些技巧在一定程度上是*经验性*的:残差连接和批归一化显然有帮助,但学界至今仍在争论*究竟为什么*。这份诚实很重要。明白今天的深度网络靠的是一把精心设计的妙招、而非一套完成的理论,正是这条阶梯其余部分赖以建立的那种清醒认知。

  1. 诊断:堆叠许多层后,梯度作为各层斜率的长连乘,要么消失趋近于零,要么爆炸趋于无穷。
  2. 起步要对:给随机权重设定尺度(Xavier/He),让信号方差逐层保持不变。
  3. 使用不饱和的激活函数(ReLU),让接近 1 的斜率不缩小梯度;当它有爆炸之虞时进行裁剪。
  4. 在训练过程中用归一化稳住阵线,并用残差连接给梯度一条回家的捷径。