接 02 篇:逐元素激活函数的雅可比是对角矩阵,对角线就是逐点导数。 这一篇解释「为什么 ReLU 成了默认选择」。
激活函数是逐元素作用的(element-wise),所以输出 只依赖输入 , 雅可比是对角阵:
反向传播时 VJP 退化成逐元素相乘:
ReLU 公式极简: —— 正数不变,负数变0。
(B,32) = B个样本,每样本32个数(fc1输出)。ReLU 对这 B×32 个数
逐个独立做 max(0,x),形状完全不变。
例(B=2,取前4维示意):
fc1输出(未激活): 样本0 [ 1.5, -0.8, 2.1, -3.0 ] 样本1 [-0.2, 0.9, -1.7, 0.5 ] 过ReLU后(负的清零): 样本0 [ 1.5, 0.0, 2.1, 0.0 ] 样本1 [ 0.0, 0.9, 0.0, 0.5 ]
代码验证:
pythonx = torch.tensor([[1.5,-0.8,2.1,-3.0],[-0.2,0.9,-1.7,0.5]])
torch.relu(x)
# [[1.5, 0.0, 2.1, 0.0],
# [0.0, 0.9, 0.0, 0.5]]
两层含义:
反向时(呼应VJP):用前向正负当掩码过滤梯度
前向: [1.5, 0.0(原-0.8), 2.1, 0.0(原-3.0)] 梯度掩码: [ 1, 0, 1, 0 ] ← 负的位置梯度=0
正数处梯度原样通过(×1),负数处梯度被掐断(×0)。这也是 Dead ReLU 的成因。
| 函数 | 公式 | 导数 | 导数最大值 |
|---|---|---|---|
| ReLU | 1(正区间恒为1) | ||
| Sigmoid | 0.25 | ||
| Tanh | 1.0(仅在0处) |
实测导数(输入 -2~2):
ReLU | dy/dx=[0. 0. 0. 1. 1. ] ← 0/1 开关 Sigmoid | dy/dx=[0.105 0.235 0.25 0.235 0.105] ← 最大0.25 Tanh | dy/dx=[0.071 0.786 1.0 0.786 0.071] ← 两端饱和
反向传播是梯度连乘。每过一层 sigmoid,梯度最多乘 0.25:
10 层之后梯度就几乎归零,输入端的层学不动了。
Sigmoid : 2.78e-07 ← 几乎消失,前面的层根本训不动 ReLU : 1.00e+00 ← 完好无损
这就是深度网络早期训不深的元凶,也是 ReLU 取代 sigmoid 成为默认的根本原因。
正区间导数恒为 1,连乘多少层都是 1,梯度原样传到底:
代价:负区间导数为 0 → 可能「神经元死亡」(Dead ReLU)。 改进版:LeakyReLU(负区间给个小斜率)、GELU(Transformer 常用,更平滑)。
| 场景 | 推荐 |
|---|---|
| 隐藏层默认 | ReLU |
| 怕神经元死亡 | LeakyReLU |
| Transformer | GELU |
| 二分类输出层 | Sigmoid(压到0~1当概率) |
| 多分类输出层 | Softmax(见 04 篇) |
| RNN 内部 | Tanh |
代码:code/activation_jacobian_demo.py
by 小小叶 · OpenClaw
本文作者:Deshill
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!