你有没有想过这样一件事:现在深度学习里到处是 ReLU,它像一个折线,在 x=0 的地方有一个不可导的拐点。但训练神经网络靠的是梯度下降,反向传播每走一步都要算导数。一个没有导数的点,梯度该怎么算?用 ReLU 训练的模型,真的没出问题吗?

这个问题我琢磨了很久。我最早以为,算法设计者是把 ReLU 在 0 点的导数硬定义成 1 或者 0,靠这种&token;作弊&token;骗过优化器。后来我才发现,事情远不是这么简单。可导性这个东西,在神经网络里的位置,比教科书上说的微妙得多。
反向传播真正需要的不是&token;处处可导&token;,而是&token;梯度能传回去&token;
先看最基础的反向传播长什么样。给了一个输入,信息一层层往前传到输出,算出 loss;然后优化器要问:如果把某个权重动一小下,loss 会变多少?答案来自链式法则:通过计算图一路上每层函数的导数乘起来。
- 最后一层输出对 loss 求导,得到初始反向传播梯度。
- 对于每一层,把上游梯度乘以本层激活函数对输入 z 的导数 f'(z),得到传给下一层的梯度。
- 同时,本层权重对 z 的导数与上游梯度结合,算出权重的梯度。
在这条链上,只要某一个环节出现了不存在的导数,整个链条就断了。所以理论上,激活函数必须处处可微,才算&token;合法&token;。但问题来了:ReLU 在 0 点不可微,凭什么还能用?
硬切换函数的死因不是不可导,而是&token;几乎没有导数&token;
把时间拨回 20 世纪。感知机用的激活函数是阶跃函数:输入小于 0,输出 0;输入大于 0,输出 1。这个函数在 x=0 处有个跳变,在其余所有点上导数都是 0。这意味着无论你输入什么,只要不是恰好在 0 上,激活函数这一层对输入的敏感度就是零。反向传播试图把误差从输出层传回输入层,传到这里发现梯度全部变成 0,一点信息都过不去。多层感知机的训练因此难产,直到 1986 年,Rumelhart、Hinton 和 Williams 把 sigmoid 换掉了阶跃函数,才第一次让反向传播在多层网络上跑通。
同样不可导,阶跃函数和 ReLU 的差距在哪里?你看导数:阶跃的导数处处为 0,而 ReLU 的导数在正半轴恒为 1。前者把信息全部掐死,后者保留了正输入的梯度。可导性只是数学上的说法,真正决定能不能训练的是梯度的信息量。
ReLU 的不可导点是纸老虎:次梯度补上了
那 ReLU 在 0 点到底怎么办?一个工程上的做法是:真实数值计算时,如果输入恰好是 0,你可以选任意一侧的导数,0 或 1。因为浮点数落在 0 上的概率极小,你甚至可以把它当作不存在。但理论研究需要一个更严谨的说法。
数学上这叫次梯度(subgradient):对于一个凸函数,在某点虽然没有切线,但存在所有不越过函数下方的直线的斜率。ReLU 在 0 点的次梯度是区间 [0,1] 上的任意值。优化算法用次梯度代替梯度,依然能收敛。所以 ReLU 虽然不是处处可导,但它具备足够的次梯度信息。
既然 ReLU 可以,为什么还要用 GELU、SiLU?
这要从两个问题说起。
第一个问题:死亡 ReLU。 ReLU 对负输入的梯度是 0。如果某个神经元经常收到负的输入,它的权重梯度永远是 0,就再也无法更新,这个神经元就永久&token;死亡&token;了。在同一个小批量里,如果大量神经元一起变死,网络的有效容量就下降了。
第二个问题:梯度突变。 即使是 ReLU 在 0 点次梯度,它的导数曲线也像一道闪电:左边 0,右边 1,中间没有过渡。这种非连续性会让 loss 曲面产生&token;山脊&token;。优化器(特别是带动量的)冲过山脊时,动量项会被突然改变方向,造成震荡。
平滑激活函数(如 GELU、SiLU)正是冲着这两点来的。以 SiLU(也叫 Swish)为例,它的定义是 x * sigmoid(x),处处可导,导数是一条连续光滑的曲线,从 0 慢慢升到 1 再慢慢降到接近 0,没有跳变。Google 的 Swish 论文 在实验中发现,用 Swish 替换 ReLU 在深层网络上(比如 ResNet 和 MobileNet)能稳定提升精度,作者把这归功于平滑性带来的更顺畅的梯度流。
注意这里的逻辑:不是&token;因为可导所以好&token;,而是&token;因为平滑,所以梯度流的质量好&token;。可导是一个离散的二值属性,平滑是一个连续的度量。硬切换函数即使可导,比如 Leaky ReLU 在 0 点左侧斜率 0.01、右侧 1,它依然有折痕,导数不连续。而 GELU 这类函数没有任何折痕。
一张表看清不同激活函数的梯度性格
| 激活函数 | 零点可导? | 导数连续性 | 负输入梯度 | 训练稳定性 |
|---|---|---|---|---|
| 阶跃(step) | 否 | 跳变 | 0 | 极差,无法多层 |
| ReLU | 否(次梯度) | 跳跃(0 到 1) | 0 | 正常,但会死亡神经元 |
| Leaky ReLU | 否(折点) | 跳跃(0.01 到 1) | 0.01 常数 | 缓解死亡,但不平滑 |
| GELU | 是 | 连续 | 接近 0 但非零 | 很稳定 |
| SiLU/Swish | 是 | 连续光滑 | 负时出现一个小负峰,不为 0 | 很稳定 |
从表格能看出,真正的坑不是&token;某个点不可导&token;,而是导数在很多地方为 0 或者 导数剧烈跳变。这种函数的梯度信息没法用。
我当初踩过的认知坑:把&token;可导&token;当成了前提条件
很长一段时间里,我以为神经网络对激活函数的数学要求就是&token;必须处处可导&token;。所以当我在一篇博客里看到 ReLU 在 0 点不可导时,第一反应是作者写错了。直到我读了次梯度的资料,才发现我脑子里那个&token;处处可导&token;的需求,其实是把充分条件当成了必要条件。真正严格的表述是:在几乎所有点上存在导数,并且在不可导点上有可用的次梯度,训练就能进行。你看,数学并不限制工程,工程也不一定服从数学的洁癖。
后来我又看到很多新激活函数的论文,其中 Mish 这篇论文提出了一个新的平滑激活函数,作者在摘要里强调平滑性有助于优化。此时我才意识到,真正让 ReLU 家族被挑战的,不是可导性,而是平滑性带来的优化优势。
所以,选激活函数时你到底该看什么?
把整个讨论压缩成一句话:可导性决定&token;能不能&token;用梯度法,平滑性决定&token;好不好&token;用梯度法。
对大多数深层网络来说,优先考虑平滑激活函数(GELU 或 SiLU)通常能带来更稳定的收敛和更低最终 error。但这不是免费的:GELU 和 SiLU 的计算量比 ReLU 大,在移动端和推理时可能成为瓶颈。ReLU 依旧不是退场,因为它简单、快速,而且在一些任务上精度差距很小。至于阶跃这类硬切换,除了读写器的输出门、量化网络或某些符号计算,几乎不会出现在可训练的深层网络里。
最后给你一个提醒:如果你在用 ReLU 时发现网络训不出来,别把锅甩给&token;0 点不可导&token;。先看看是不是学习率太高导致神经元死光,或者初始化太差。可导性这个问题,永远排不到第一个需要考虑的事项。
进阶:次梯度的严格定义
对于实值凸函数 f,x0 处的次梯度是所有满足 f(x) ≥ f(x0)+g·(x-x0) 的 g 的集合。ReLU 在 0 点的次梯度是 [0,1]。对于可导点,这个集合退化为唯一元素 f'。次梯度法用任何次梯度替代梯度,在凸优化中有收敛性保证。深度学习中虽然 loss 非凸,但经验上次梯度法几乎总是有效。
原创文章,作者:guanweilu,如若转载,请注明出处:https://guanweilu.cn/article/569.html