BP神经网络反向传播算法推导过程详解:从数学基础到代码实现

1次阅读
没有评论

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

image.webp

神经网络基础概念

BP 神经网络(Backpropagation Neural Network)是一种多层前馈网络,通过误差反向传播算法进行训练。它由输入层、隐藏层和输出层组成,每层之间通过权重连接。

BP 神经网络反向传播算法推导过程详解:从数学基础到代码实现

前向传播过程

前向传播是指输入数据从输入层经过隐藏层最终到达输出层的过程。具体步骤如下:

  1. 输入层接收输入向量 x
  2. 计算隐藏层输入:z = W1 * x + b1
  3. 通过激活函数得到隐藏层输出:a = σ(z)
  4. 计算输出层输入:z’ = W2 * a + b2
  5. 最终输出:y = σ(z’)

数学表达式为:
$$ y = \sigma(W_2\cdot\sigma(W_1\cdot x + b_1) + b_2) $$

反向传播算法推导

反向传播算法的核心是通过链式法则计算损失函数对各个参数的梯度,然后使用梯度下降法更新参数。

损失函数

假设我们使用均方误差作为损失函数:
$$ L = \frac{1}{2}(y – t)^2 $$
其中 t 是真实标签。

输出层梯度计算

  1. 计算损失对输出的偏导:
    $$ \frac{\partial L}{\partial y} = y – t $$

  2. 计算输出对 z ’ 的偏导(考虑 sigmoid 激活函数):
    $$ \frac{\partial y}{\partial z’} = y(1-y) $$

  3. 通过链式法则得到输出层的误差项:
    $$ \delta_2 = \frac{\partial L}{\partial z’} = \frac{\partial L}{\partial y} \cdot \frac{\partial y}{\partial z’} = (y-t) \cdot y(1-y) $$

隐藏层梯度计算

  1. 计算 z ’ 对隐藏层输出 a 的偏导:
    $$ \frac{\partial z’}{\partial a} = W_2 $$

  2. 计算 a 对 z 的偏导:
    $$ \frac{\partial a}{\partial z} = a(1-a) $$

  3. 隐藏层的误差项:
    $$ \delta_1 = \delta_2 \cdot W_2^T \cdot a(1-a) $$

参数更新

使用学习率 η 更新参数:

  1. 输出层权重更新:
    $$ W_2 \leftarrow W_2 – \eta \cdot \delta_2 \cdot a^T $$
    $$ b_2 \leftarrow b_2 – \eta \cdot \delta_2 $$

  2. 隐藏层权重更新:
    $$ W_1 \leftarrow W_1 – \eta \cdot \delta_1 \cdot x^T $$
    $$ b_1 \leftarrow b_1 – \eta \cdot \delta_1 $$

Python 代码实现

import numpy as np

class BPNetwork:
    def __init__(self, input_size, hidden_size, output_size):
        # 初始化权重
        self.W1 = np.random.randn(input_size, hidden_size) * 0.1
        self.b1 = np.zeros((1, hidden_size))
        self.W2 = np.random.randn(hidden_size, output_size) * 0.1
        self.b2 = np.zeros((1, output_size))

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

    def forward(self, x):
        self.z1 = np.dot(x, self.W1) + self.b1
        self.a1 = self.sigmoid(self.z1)
        self.z2 = np.dot(self.a1, self.W2) + self.b2
        self.output = self.sigmoid(self.z2)
        return self.output

    def backward(self, x, y, learning_rate):
        # 反向传播
        m = x.shape[0]

        # 输出层误差
        delta2 = (self.output - y) * self.output * (1 - self.output)
        dW2 = np.dot(self.a1.T, delta2) / m
        db2 = np.sum(delta2, axis=0, keepdims=True) / m

        # 隐藏层误差
        delta1 = np.dot(delta2, self.W2.T) * self.a1 * (1 - self.a1)
        dW1 = np.dot(x.T, delta1) / m
        db1 = np.sum(delta1, axis=0, keepdims=True) / m

        # 更新参数
        self.W2 -= learning_rate * dW2
        self.b2 -= learning_rate * db2
        self.W1 -= learning_rate * dW1
        self.b1 -= learning_rate * db1

    def train(self, X, y, epochs=1000, learning_rate=0.1):
        for i in range(epochs):
            output = self.forward(X)
            self.backward(X, y, learning_rate)

算法复杂度分析

时间复杂度

BP 神经网络的时间复杂度主要取决于:

  1. 前向传播:O(n×m + m×k)
  2. 反向传播:O(k×m + m×n)

其中 n 是输入维度,m 是隐藏层节点数,k 是输出维度。

空间复杂度

主要消耗在存储权重矩阵和中间结果:

  1. 权重矩阵:O(n×m + m×k)
  2. 中间结果:O(m + k)

避坑指南

  1. 参数初始化问题
  2. 错误:全零初始化
  3. 解决:使用随机小值初始化

  4. 学习率选择

  5. 错误:固定学习率
  6. 解决:使用学习率衰减或自适应优化器

  7. 梯度消失 / 爆炸

  8. 错误:深层网络梯度不稳定
  9. 解决:使用 ReLU 等激活函数,批归一化

  10. 过拟合

  11. 错误:训练误差小但测试误差大
  12. 解决:使用正则化、dropout、早停

  13. 计算效率

  14. 错误:单样本更新
  15. 解决:使用 mini-batch

思考题

  1. 如何改进基本 BP 算法以提高训练速度?
  2. 除了均方误差,还可以使用哪些损失函数?
  3. 当网络层数增加时,会遇到什么问题?如何解决?
  4. 比较 SGD、Momentum、Adam 等优化算法的优缺点。
  5. 如何确定合适的网络结构和超参数?

总结

本文详细推导了 BP 神经网络的反向传播算法,从数学原理到代码实现,并分析了算法的复杂度。通过避坑指南和思考题,希望能帮助初学者更好地理解和应用 BP 神经网络。反向传播是深度学习的基础,掌握它对后续学习更复杂的神经网络结构至关重要。

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