BP神经网络入门:详解前向传播、反向传播与链式求导的数学本质

1次阅读
没有评论

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

image.webp

背景痛点:新手常见误区

很多初学者第一次接触反向传播时,容易产生两个典型误解:

BP 神经网络入门:详解前向传播、反向传播与链式求导的数学本质

  1. 混淆权重更新与偏导计算 :误以为反向传播直接更新权重,实际上它只计算梯度,权重更新由优化器(如 SGD)完成
  2. 忽略链式法则的逐层性 :错误认为可以一步求出输出层对任意权重的偏导,而实际上需要从输出层逐步回传误差

数学推导:单隐藏层网络示例

前向传播公式

设网络结构为输入层($x$)→隐藏层($h$)→输出层($\hat{y}$),使用 Sigmoid 激活函数:

$$
\begin{aligned}
z_h &= W_{xh}x + b_h \
h &= \sigma(z_h) \
z_y &= W_{hy}h + b_y \
\hat{y} &= \sigma(z_y)
\end{aligned}
$$

其中 $\sigma(z) = \frac{1}{1+e^{-z}}$,$W_{xh}$ 和 $W_{hy}$ 分别为输入到隐藏层和隐藏层到输出层的权重矩阵。

损失函数与反向传播

采用均方误差(MSE)损失:

$$
L = \frac{1}{2}(y – \hat{y})^2
$$

关键推导步骤(以 $W_{hy}$ 为例):

  1. 输出层梯度
    $$
    \frac{\partial L}{\partial W_{hy}} = \frac{\partial L}{\partial \hat{y}} \cdot \frac{\partial \hat{y}}{\partial z_y} \cdot \frac{\partial z_y}{\partial W_{hy}}
    $$

  2. 逐项计算

  3. $\frac{\partial L}{\partial \hat{y}} = -(y – \hat{y})$
  4. $\frac{\partial \hat{y}}{\partial z_y} = \sigma(z_y)(1-\sigma(z_y))$(Sigmoid 导数特性)
  5. $\frac{\partial z_y}{\partial W_{hy}} = h^T$

  6. 最终表达式
    $$
    \delta_y = (\hat{y} – y) \odot \sigma'(z_y) \
    \nabla W_{hy} = h^T \delta_y
    $$

隐藏层权重 $W_{xh}$ 的梯度计算需要继续链式求导,体现反向传播的「反向」特性。

Python 实现核心代码

import numpy as np

class BPNetwork:
    def __init__(self, input_size, hidden_size):
        # 初始化权重(Xavier 初始化)self.Wxh = np.random.randn(hidden_size, input_size) * np.sqrt(1/input_size)
        self.Why = np.random.randn(1, hidden_size) * np.sqrt(1/hidden_size)
        self.bh = np.zeros((hidden_size, 1))
        self.by = np.zeros((1, 1))

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

    def forward(self, x):
        self.z_h = self.Wxh @ x + self.bh
        self.h = self.sigmoid(self.z_h)
        self.z_y = self.Why @ self.h + self.by
        return self.sigmoid(self.z_y)

    def backward(self, x, y, lr=0.1):
        # 输出层梯度
        error = self.y_pred - y
        delta_y = error * self.y_pred * (1 - self.y_pred)  # ⊙表示逐元素乘
        grad_Why = delta_y @ self.h.T  # 注意 h 需要转置

        # 隐藏层梯度(关键链式传导)delta_h = (self.Why.T @ delta_y) * self.h * (1 - self.h)
        grad_Wxh = delta_h @ x.T

        # 参数更新
        self.Why -= lr * grad_Why
        self.Wxh -= lr * grad_Wxh
        self.by -= lr * delta_y
        self.bh -= lr * delta_h

避坑指南

  • 梯度消失问题 :当输入值较大时,Sigmoid 导数接近 0,导致浅层权重更新缓慢。解决方法:
  • 使用 ReLU 等非饱和激活函数
  • 实施梯度裁剪(gradient clipping)

  • 学习率设置 :建议从 0.01 开始尝试,观察损失曲线:

  • 震荡剧烈→学习率过大
  • 下降过慢→学习率过小

  • 数据标准化 :输入特征应缩放至相近范围(如 [-1,1]),否则:

  • 不同权重的梯度数量级差异大
  • 需要为每个特征设置不同的学习率

验证实验

from sklearn.datasets import make_moons
import matplotlib.pyplot as plt

# 生成数据
X, y = make_moons(n_samples=1000, noise=0.1)
y = y.reshape(-1, 1)  # 转为列向量

# 训练网络
net = BPNetwork(input_size=2, hidden_size=4)
losses = []
for epoch in range(1000):
    total_loss = 0
    for i in range(len(X)):
        x_sample = X[i].reshape(-1, 1)
        y_pred = net.forward(x_sample)
        net.backward(x_sample, y[i], lr=0.1)
        total_loss += 0.5 * (y_pred - y[i])**2
    losses.append(total_loss / len(X))

# 可视化
plt.plot(losses)
plt.xlabel('Epoch')
plt.ylabel('MSE Loss')
plt.show()

关键总结

  1. 反向传播本质 :是链式法则的高效实现,避免重复计算中间梯度
  2. 矩阵求导要点 :注意各矩阵维度匹配,特别是转置操作的位置
  3. 工程实践 :建议先在小规模网络(如 2 - 4 个隐藏单元)上手动推导验证,再扩展到复杂结构

通过代码与公式的对照实现,可以深刻理解为什么深度学习框架能自动求导——其核心正是基于计算图的链式法则传播机制。

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