# 03. 激活函数与梯度消失 > 接 02 篇:逐元素激活函数的雅可比是**对角矩阵**,对角线就是逐点导数。 > 这一篇解释「为什么 ReLU 成了默认选择」。 --- ## 一、激活函数的雅可比 = 对角矩阵 激活函数是**逐元素**作用的(element-wise),所以输出 $y_i$ 只依赖输入 $x_i$, 雅可比是对角阵: $$J = \text{diag}\big(f'(x_1), f'(x_2), \dots\big)$$ 反向传播时 VJP 退化成**逐元素相乘**: $$\frac{\partial L}{\partial x_i} = f'(x_i)\cdot g_i$$ --- ## 一·补. 具体例子:(B,32) 数据过 ReLU 发生什么? ReLU 公式极简:$\text{ReLU}(x)=\max(0,x)$ —— **正数不变,负数变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 ] ``` 代码验证: ```python x = 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]] ``` **两层含义**: 1. **非线性来源**:负的"折一下"归零,让多层不再退化成单纯线性,才能拟合曲线 2. **神经元开关**:每个样本进来,32个神经元一部分点亮(正)、一部分熄灭(负→0), 不同样本点亮组合不同 → 网络区分输入的方式 **反向时**(呼应VJP):用前向正负当掩码过滤梯度 ``` 前向: [1.5, 0.0(原-0.8), 2.1, 0.0(原-3.0)] 梯度掩码: [ 1, 0, 1, 0 ] ← 负的位置梯度=0 ``` 正数处梯度原样通过(×1),负数处梯度被掐断(×0)。这也是 Dead ReLU 的成因。 --- ## 二、三大激活函数对比 | 函数 | 公式 | 导数 | 导数最大值 | |---|---|---|---| | **ReLU** | $\max(0,x)$ | $x>0:1,\ x\le0:0$ | **1**(正区间恒为1)| | **Sigmoid** | $\frac{1}{1+e^{-x}}$ | $\sigma(1-\sigma)$ | **0.25** | | **Tanh** | $\tanh(x)$ | $1-\tanh^2$ | **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 会梯度消失? 反向传播是**梯度连乘**。每过一层 sigmoid,梯度最多乘 0.25: $$\underbrace{0.25 \times 0.25 \times \dots}_{N\text{层}} = 0.25^N \to 0$$ 10 层之后梯度就几乎归零,输入端的层**学不动**了。 ### 实测:10 层堆叠后输入端梯度 ``` Sigmoid : 2.78e-07 ← 几乎消失,前面的层根本训不动 ReLU : 1.00e+00 ← 完好无损 ``` > 这就是深度网络早期训不深的元凶,也是 ReLU 取代 sigmoid 成为默认的根本原因。 --- ## 四、ReLU 为什么不梯度消失? 正区间导数**恒为 1**,连乘多少层都是 1,梯度原样传到底: $$\underbrace{1\times1\times\dots\times1}_{N} = 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