深度学习实战:手把手实现常用激活函数的反向传播(头歌)

1次阅读
没有评论

共计 1702 个字符,预计需要花费 5 分钟才能阅读完成。

image.webp

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

深度学习实战:手把手实现常用激活函数的反向传播(头歌)

1. Sigmoid 激活函数

Sigmoid 函数的数学表达式为:
$$\sigma(x) = \frac{1}{1 + e^{-x}}$$

它的导数有一个很好的性质:
$$\sigma'(x) = \sigma(x)(1 – \sigma(x))$$

这个特性使得在反向传播时计算非常方便。但是 Sigmoid 函数有两个主要问题:

  1. 输出不是零中心的,这可能导致梯度更新效率低下
  2. 当输入绝对值较大时,梯度会变得非常小(饱和梯度问题),导致梯度消失

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

对比分析与调参建议

  1. 梯度消失 / 爆炸问题
  2. Sigmoid 和 Tanh 在极端值时梯度接近 0,容易导致梯度消失
  3. ReLU 在正区间梯度恒为 1,可以缓解梯度消失,但可能导致梯度爆炸

  4. 实际应用建议

  5. 隐藏层通常使用 ReLU 或变体(如 Leaky ReLU)
  6. 输出层根据任务选择:二分类用 Sigmoid,多分类用 Softmax,回归问题可以不用激活函数
  7. 学习率设置要谨慎,特别是使用 ReLU 时

  8. 应对策略

  9. 使用批归一化 (Batch Normalization) 来稳定梯度
  10. 尝试 ReLU 变体如 Leaky ReLU 或 ELU
  11. 合理的权重初始化(如 He 初始化)

思考题

如果自定义一个新的激活函数,需要考虑哪些因素来保证反向传播的稳定性?

  1. 导数是否容易计算
  2. 梯度范围是否合理(避免太大或太小)
  3. 是否会导致神经元死亡
  4. 计算效率如何
  5. 是否适合你的特定任务

通过本文的讲解和代码实现,你应该已经掌握了常用激活函数的反向传播原理和实现方法。在实践中,建议先从简单的 ReLU 开始,然后根据具体任务需求尝试其他激活函数。记得监控梯度变化情况,这能帮助你及时发现并解决梯度消失或爆炸问题。

正文完
 0
评论(没有评论)