BP神经网络详解:从数学原理到Python实战

1次阅读
没有评论

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

image.webp

数学原理:链式求导详解

BP 神经网络的核心是反向传播算法,其本质是复合函数的链式求导。设网络有 L 层,第 l 层的权重为 $W^l$,偏置为 $b^l$,激活函数为 $\sigma(\cdot)$,则前向传播过程为:

BP 神经网络详解:从数学原理到 Python 实战

$$
z^l = W^l a^{l-1} + b^l \
a^l = \sigma(z^l)
$$

对于损失函数 $J$,需要计算 $\frac{\partial J}{\partial W^l}$ 和 $\frac{\partial J}{\partial b^l}$。根据链式法则:

  1. 输出层误差:
    $$
    \delta^L = \frac{\partial J}{\partial a^L} \odot \sigma'(z^L)
    $$

  2. 反向传播误差:
    $$
    \delta^l = (W^{l+1})^T \delta^{l+1} \odot \sigma'(z^l)
    $$

  3. 参数梯度:
    $$
    \frac{\partial J}{\partial W^l} = \delta^l (a^{l-1})^T \
    \frac{\partial J}{\partial b^l} = \delta^l
    $$

激活函数对比

  • Sigmoid:
    $$
    \sigma(z) = \frac{1}{1+e^{-z}} \
    \sigma'(z) = \sigma(z)(1-\sigma(z))
    $$
    易导致梯度消失,输出非零均值

  • Tanh:
    $$
    \tanh(z) = \frac{e^z – e^{-z}}{e^z + e^{-z}} \
    \tanh'(z) = 1 – \tanh^2(z)
    $$
    梯度消失问题缓解,但计算量较大

  • ReLU:
    $$
    \text{ReLU}(z) = \max(0,z) \
    \text{ReLU}'(z) = \begin{cases}
    1 & z>0 \
    0 & \text{otherwise}
    \end{cases}
    $$
    计算高效,但需注意死亡 ReLU 问题

Python 实现核心代码

import numpy as np

class NeuralNetwork:
    def __init__(self, layer_dims, activation='relu'):
        self.params = {}
        self.activation = activation

        # He 初始化(ReLU 适用)for l in range(1, len(layer_dims)):
            self.params[f'W{l}'] = np.random.randn(layer_dims[l], layer_dims[l-1]) * np.sqrt(2/layer_dims[l-1])
            self.params[f'b{l}'] = np.zeros((layer_dims[l], 1))

    def forward(self, X):
        cache = {'A0': X}

        # 前向传播
        for l in range(1, len(self.params)//2 + 1):
            Z = np.dot(self.params[f'W{l}'], cache[f'A{l-1}']) + self.params[f'b{l}']

            if l == len(self.params)//2:  # 输出层用 sigmoid
                A = 1/(1+np.exp(-Z))
            else:  # 隐层用指定激活函数
                if self.activation == 'relu':
                    A = np.maximum(0, Z)
                elif self.activation == 'tanh':
                    A = np.tanh(Z)

            cache[f'Z{l}'] = Z
            cache[f'A{l}'] = A

        return cache

    def backward(self, cache, y):
        grads = {}
        m = y.shape[1]
        L = len(self.params)//2

        # 输出层误差
        dZ = cache[f'A{L}'] - y
        grads[f'dW{L}'] = np.dot(dZ, cache[f'A{L-1}'].T)/m
        grads[f'db{L}'] = np.sum(dZ, axis=1, keepdims=True)/m

        # 反向传播
        for l in reversed(range(1, L)):
            if self.activation == 'relu':
                dA = np.dot(self.params[f'W{l+1}'].T, dZ)
                dZ = dA * (cache[f'Z{l}'] > 0)
            elif self.activation == 'tanh':
                dA = np.dot(self.params[f'W{l+1}'].T, dZ)
                dZ = dA * (1 - np.square(cache[f'A{l}']))

            grads[f'dW{l}'] = np.dot(dZ, cache[f'A{l-1}'].T)/m
            grads[f'db{l}'] = np.sum(dZ, axis=1, keepdims=True)/m

        return grads

参数初始化策略

  • Xavier 初始化 :
    适用于 Sigmoid/Tanh 激活函数
    $$
    W \sim \mathcal{N}(0, \sqrt{\frac{1}{n_{in}}})
    $$

  • He 初始化 :
    适用于 ReLU 系列激活函数
    $$
    W \sim \mathcal{N}(0, \sqrt{\frac{2}{n_{in}}})
    $$

训练注意事项

  1. 学习率选择 :
  2. 典型初始值 0.01,观察损失曲线
  3. 出现震荡时降低学习率 10 倍
  4. 使用学习率衰减策略

  5. 梯度裁剪 :

    max_norm = 5
    for key in grads:
        norm = np.linalg.norm(grads[key])
        if norm > max_norm:
            grads[key] = grads[key] * max_norm / norm

  6. 隐层设计 :

  7. 首层神经元数建议为输入维度的 2 / 3 到 4 /3
  8. 后续每层减半
  9. 总层数不超过 $\log_2(n_{features})$ + 2

扩展思考

  1. 训练日志记录 :

    def log_metrics(metric_names):
        def decorator(train_func):
            def wrapper(*args, **kwargs):
                results = train_func(*args, **kwargs)
                with open('training.log', 'a') as f:
                    f.write(','.join(str(results[name]) for name in metric_names) + '\n')
                return results
            return wrapper
        return decorator

  2. 批归一化影响 :

  3. 将每层的输入分布标准化为 $\mathcal{N}(0,1)$
  4. 允许使用更大的学习率
  5. 减少对初始化的敏感度
  6. 需在反向传播时增加 BN 层的梯度计算

实战建议

  1. 从单隐层网络开始调试
  2. 先在小数据集(如 MNIST)上验证正确性
  3. 使用 Adam 优化器替代 SGD
  4. 添加 L2 正则化防止过拟合
  5. 可视化每层的权重分布
正文完
 0
评论(没有评论)