BP神经网络反向传播算法公式推导:从数学原理到代码实现

1次阅读
没有评论

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

image.webp

BP 神经网络基础认知

BP(Back Propagation)神经网络是一种典型的多层前馈神经网络,其核心是通过误差反向传播算法调整网络权重。一个标准的三层结构包括:

BP 神经网络反向传播算法公式推导:从数学原理到代码实现

  • 输入层:接收原始特征向量
  • 隐含层:进行非线性特征变换(可有多层)
  • 输出层:产生最终预测结果

网络训练的本质是通过梯度下降最小化损失函数,这个过程需要反复执行:

  1. 前向传播计算预测值
  2. 反向传播调整各层参数

数学推导全流程

前向传播计算

设网络有 $L$ 层,第 $l$ 层的权重矩阵为 $W^l$,偏置为 $b^l$,激活函数为 $\sigma(\cdot)$,则:

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

其中 $a^0$ 即为输入数据,$a^L$ 为最终输出。

损失函数定义

对于分类任务常用交叉熵损失,回归任务常用均方误差(MSE):

$$
J = \frac{1}{2m}\sum_{i=1}^m (y_i – a_i^L)^2
$$

反向传播推导(核心部分)

输出层误差计算

首先计算输出层误差 $\delta^L$:

$$
\delta^L = \frac{\partial J}{\partial z^L} = (a^L – y) \odot \sigma'(z^L)
$$

隐含层误差传播

通过链式法则逐层回传误差:

$$
\delta^l = ((W^{l+1})^T \delta^{l+1}) \odot \sigma'(z^l)
$$

参数梯度计算

最终得到权重和偏置的梯度:

$$
\begin{aligned}
\frac{\partial J}{\partial W^l} &= \delta^l (a^{l-1})^T \
\frac{\partial J}{\partial b^l} &= \delta^l
\end{aligned}
$$

参数更新

使用梯度下降法更新参数:

$$
\begin{aligned}
W^l &:= W^l – \eta \frac{\partial J}{\partial W^l} \
b^l &:= b^l – \eta \frac{\partial J}{\partial b^l}
\end{aligned}
$$

Python 实现详解

import numpy as np

class BPNeuralNetwork:
    def __init__(self, layers):
        """
        初始化网络结构
        :param layers: 各层神经元数量,如 [4,5,1] 表示两层网络
        """
        self.L = len(layers) - 1
        self.weights = [np.random.randn(layers[i+1], layers[i])*0.1 
                        for i in range(self.L)]
        self.biases = [np.zeros((layers[i+1], 1)) 
                       for i in range(self.L)]

    def sigmoid(self, z):
        return 1/(1+np.exp(-z))

    def sigmoid_prime(self, z):
        return self.sigmoid(z)*(1-self.sigmoid(z))

    def forward(self, x):
        """前向传播"""
        a = x.T if x.ndim > 1 else x.reshape(-1,1)
        activations = [a]
        zs = []

        for w,b in zip(self.weights, self.biases):
            z = np.dot(w, a) + b
            a = self.sigmoid(z)
            zs.append(z)
            activations.append(a)

        return activations[-1], activations, zs

    def backward(self, x, y, activations, zs):
        """反向传播"""
        nabla_w = [np.zeros(w.shape) for w in self.weights]
        nabla_b = [np.zeros(b.shape) for b in self.biases]

        # 输出层误差
        delta = (activations[-1] - y) * self.sigmoid_prime(zs[-1])
        nabla_b[-1] = delta
        nabla_w[-1] = np.dot(delta, activations[-2].T)

        # 反向传播误差
        for l in range(2, self.L+1):
            delta = np.dot(self.weights[-l+1].T, delta) * \
                    self.sigmoid_prime(zs[-l])
            nabla_b[-l] = delta
            nabla_w[-l] = np.dot(delta, activations[-l-1].T)

        return nabla_w, nabla_b

    def train(self, X, y, epochs=1000, lr=0.1):
        """训练过程"""
        for epoch in range(epochs):
            # 小批量梯度下降
            output, activations, zs = self.forward(X)
            nabla_w, nabla_b = self.backward(X, y, activations, zs)

            # 参数更新
            self.weights = [w - lr*nw 
                           for w, nw in zip(self.weights, nabla_w)]
            self.biases = [b - lr*nb 
                          for b, nb in zip(self.biases, nabla_b)]

            if epoch % 100 == 0:
                loss = np.mean((output - y)**2)
                print(f"Epoch {epoch}, Loss: {loss:.4f}")

# 使用示例
if __name__ == "__main__":
    # XOR 问题数据集
    X = np.array([[0,0],[0,1],[1,0],[1,1]])
    y = np.array([[0],[1],[1],[0]])

    # 创建 2 -4- 1 结构的网络
    nn = BPNeuralNetwork([2,4,1])
    nn.train(X, y, epochs=2000, lr=0.5)

工程实践关键点

学习率选择

  • 常用范围:0.001~0.1
  • 可采用学习率衰减策略:
    lr = initial_lr / (1 + decay_rate * epoch)

权重初始化技巧

  • 避免全零初始化
  • Xavier 初始化:
    w = np.random.randn(fan_in, fan_out) / np.sqrt(fan_in)

梯度检查方法

通过数值梯度验证解析梯度的正确性:

def numerical_gradient(f, x, eps=1e-4):
    grad = np.zeros_like(x)
    for i in range(x.size):
        tmp = x.flat[i]
        x.flat[i] = tmp + eps
        f1 = f(x)
        x.flat[i] = tmp - eps
        f2 = f(x)
        grad.flat[i] = (f1 - f2)/(2*eps)
        x.flat[i] = tmp
    return grad

常见问题解决方案

  • 梯度消失:使用 ReLU 等激活函数
  • 梯度爆炸:梯度裁剪
  • 过拟合:添加 L2 正则化项
    loss = original_loss + 0.5*lambda_*(np.sum(weights**2))

性能优化方向

  1. 计算加速:
  2. 使用 GPU 加速矩阵运算
  3. 采用 mini-batch 训练

  4. 算法改进:

  5. 引入动量(Momentum)
  6. 自适应学习率(Adam 等)

  7. 结构优化:

  8. 批量归一化(BatchNorm)
  9. 残差连接

总结思考

通过手动推导 BP 算法,我们可以深入理解神经网络的学习机制。虽然现代深度学习框架已经实现了自动微分,但掌握底层原理对于:

  • 调试网络训练问题
  • 设计新型网络结构
  • 优化训练过程

都具有重要意义。建议读者可以进一步探索:

  1. 不同激活函数对梯度传播的影响
  2. 二阶优化方法(如 Hessian 矩阵)的应用
  3. 与其他优化算法(SGD、Adam 等)的对比实验
正文完
 0
评论(没有评论)