共计 1702 个字符,预计需要花费 5 分钟才能阅读完成。
激活函数是神经网络中的关键组件,它决定了神经元是否被激活以及如何将输入信号传递到下一层。在反向传播过程中,激活函数的导数直接影响梯度如何通过网络层进行传递,从而影响权重更新。理解激活函数的反向传播过程,对于实现自定义神经网络和调试模型性能至关重要。

1. Sigmoid 激活函数
Sigmoid 函数的数学表达式为:
$$\sigma(x) = \frac{1}{1 + e^{-x}}$$
它的导数有一个很好的性质:
$$\sigma'(x) = \sigma(x)(1 – \sigma(x))$$
这个特性使得在反向传播时计算非常方便。但是 Sigmoid 函数有两个主要问题:
- 输出不是零中心的,这可能导致梯度更新效率低下
- 当输入绝对值较大时,梯度会变得非常小(饱和梯度问题),导致梯度消失
Python 实现:
import numpy as np
def sigmoid(x):
"""Sigmoid 激活函数前向传播"""
return 1 / (1 + np.exp(-x))
def sigmoid_backward(dA, cache):
"""
Sigmoid 激活函数反向传播
Args:
dA: 上游梯度
cache: 前向传播时保存的变量
Returns:
局部梯度
"""
s = cache
ds = s * (1 - s) # Sigmoid 导数
dZ = dA * ds
return dZ
2. ReLU 激活函数
ReLU(Rectified Linear Unit)的数学表达式为:
$$f(x) = max(0, x)$$
其导数为:
$$f'(x) = \begin{cases}
1 & \text{if} x > 0 \
0 & \text{otherwise}
\end{cases}$$
ReLU 的主要优势是计算简单,不存在饱和问题(正区间)。但也存在 ” 死亡 ReLU” 问题,即神经元可能永远不被激活。
Python 实现:
def relu(x):
"""ReLU 激活函数前向传播"""
return np.maximum(0, x)
def relu_backward(dA, cache):
"""
ReLU 激活函数反向传播
Args:
dA: 上游梯度
cache: 前向传播时保存的变量
Returns:
局部梯度
"""
Z = cache
dZ = np.array(dA, copy=True)
dZ[Z <= 0] = 0 # ReLU 导数
return dZ
3. Tanh 激活函数
Tanh 函数的数学表达式为:
$$tanh(x) = \frac{e^x – e^{-x}}{e^x + e^{-x}}$$
其导数为:
$$tanh'(x) = 1 – tanh^2(x)$$
Tanh 解决了 Sigmoid 不是零中心的问题,但仍然存在梯度消失问题。
Python 实现:
def tanh(x):
"""Tanh 激活函数前向传播"""
return np.tanh(x)
def tanh_backward(dA, cache):
"""
Tanh 激活函数反向传播
Args:
dA: 上游梯度
cache: 前向传播时保存的变量
Returns:
局部梯度
"""
Z = cache
s = np.tanh(Z)
ds = 1 - s**2 # Tanh 导数
dZ = dA * ds
return dZ
对比分析与调参建议
- 梯度消失 / 爆炸问题
- Sigmoid 和 Tanh 在极端值时梯度接近 0,容易导致梯度消失
-
ReLU 在正区间梯度恒为 1,可以缓解梯度消失,但可能导致梯度爆炸
-
实际应用建议
- 隐藏层通常使用 ReLU 或变体(如 Leaky ReLU)
- 输出层根据任务选择:二分类用 Sigmoid,多分类用 Softmax,回归问题可以不用激活函数
-
学习率设置要谨慎,特别是使用 ReLU 时
-
应对策略
- 使用批归一化 (Batch Normalization) 来稳定梯度
- 尝试 ReLU 变体如 Leaky ReLU 或 ELU
- 合理的权重初始化(如 He 初始化)
思考题
如果自定义一个新的激活函数,需要考虑哪些因素来保证反向传播的稳定性?
- 导数是否容易计算
- 梯度范围是否合理(避免太大或太小)
- 是否会导致神经元死亡
- 计算效率如何
- 是否适合你的特定任务
通过本文的讲解和代码实现,你应该已经掌握了常用激活函数的反向传播原理和实现方法。在实践中,建议先从简单的 ReLU 开始,然后根据具体任务需求尝试其他激活函数。记得监控梯度变化情况,这能帮助你及时发现并解决梯度消失或爆炸问题。
