BP神经网络反向传播算法详解:从数学推导到Python实现

1次阅读
没有评论

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

image.webp

从 XOR 问题理解前向传播

假设我们有一个简单的 XOR 问题,输入是二维数据,输出是 0 或 1。我们构建一个 2 -2- 1 的三层神经网络(输入层 2 个神经元,隐藏层 2 个神经元,输出层 1 个神经元)来解决这个问题。

BP 神经网络反向传播算法详解:从数学推导到 Python 实现

  1. 前向传播过程:
  2. 输入层到隐藏层:z1 = W1 * x + b1,a1 = sigmoid(z1)
  3. 隐藏层到输出层:z2 = W2 * a1 + b2,a2 = sigmoid(z2)
  4. 计算损失:使用均方误差(MSE) L = 1/2*(y – a2)^2

反向传播数学推导

反向传播的核心是链式法则,我们以输出层到隐藏层的权重更新为例:

  1. 计算输出层误差:
    δ2 = ∂L/∂a2 * ∂a2/∂z2 = (a2 – y) * sigmoid'(z2)

  2. 计算隐藏层误差:
    δ1 = (W2.T * δ2) ⊙ sigmoid'(z1)

  3. 计算梯度:
    ∂L/∂W2 = δ2 * a1.T
    ∂L/∂b2 = δ2
    ∂L/∂W1 = δ1 * x.T
    ∂L/∂b1 = δ1

Python 实现

import numpy as np

# 网络初始化
def initialize_parameters():
    W1 = np.random.randn(2, 2) * 0.01
    b1 = np.zeros((2, 1))
    W2 = np.random.randn(1, 2) * 0.01
    b2 = np.zeros((1, 1))
    return {'W1': W1, 'b1': b1, 'W2': W2, 'b2': b2}

# 前向传播
def forward_propagation(X, parameters):
    W1, b1, W2, b2 = parameters['W1'], parameters['b1'], parameters['W2'], parameters['b2']

    Z1 = np.dot(W1, X) + b1
    A1 = 1/(1+np.exp(-Z1))
    Z2 = np.dot(W2, A1) + b2
    A2 = 1/(1+np.exp(-Z2))

    cache = {'Z1': Z1, 'A1': A1, 'Z2': Z2, 'A2': A2}
    return A2, cache

# 计算损失
def compute_cost(A2, Y):
    m = Y.shape[1]
    cost = np.sum(np.square(A2 - Y)) / (2*m)
    return cost

# 反向传播
def backward_propagation(parameters, cache, X, Y):
    m = X.shape[1]

    W1, W2 = parameters['W1'], parameters['W2']
    A1, A2 = cache['A1'], cache['A2']

    dZ2 = (A2 - Y) * A2 * (1 - A2)
    dW2 = np.dot(dZ2, A1.T) / m
    db2 = np.sum(dZ2, axis=1, keepdims=True) / m

    dZ1 = np.dot(W2.T, dZ2) * A1 * (1 - A1)
    dW1 = np.dot(dZ1, X.T) / m
    db1 = np.sum(dZ1, axis=1, keepdims=True) / m

    grads = {'dW1': dW1, 'db1': db1, 'dW2': dW2, 'db2': db2}
    return grads

# 参数更新
def update_parameters(parameters, grads, learning_rate=0.1):
    W1 = parameters['W1'] - learning_rate * grads['dW1']
    b1 = parameters['b1'] - learning_rate * grads['db1']
    W2 = parameters['W2'] - learning_rate * grads['dW2']
    b2 = parameters['b2'] - learning_rate * grads['db2']

    return {'W1': W1, 'b1': b1, 'W2': W2, 'b2': b2}

避坑指南

  1. 学习率选择策略:
  2. 初始学习率可以从 0.1 开始尝试
  3. 使用学习率衰减:learning_rate = learning_rate * (1. / (1. + decay_rate * epoch))
  4. 尝试自适应学习率方法如 Adam

  5. 梯度检查方法:

  6. 计算数值梯度:对每个参数进行微小扰动计算损失变化
  7. 比较数值梯度与反向传播计算的理论梯度
  8. 差异应小于 1e-7

  9. 激活函数选择建议:

  10. 隐藏层推荐使用 ReLU 或 Leaky ReLU
  11. 输出层根据任务选择:二分类用 sigmoid,多分类用 softmax,回归用线性

可视化训练过程

使用 Matplotlib 绘制损失曲线和决策边界变化:

import matplotlib.pyplot as plt

# 训练后绘制损失曲线
plt.plot(loss_history)
plt.xlabel('Iterations')
plt.ylabel('Loss')
plt.title('Training Loss Curve')
plt.show()

# 绘制决策边界
x_min, x_max = X[0, :].min() - 0.5, X[0, :].max() + 0.5
y_min, y_max = X[1, :].min() - 0.5, X[1, :].max() + 0.5
h = 0.01
xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))

Z, _ = forward_propagation(np.c_[xx.ravel(), yy.ravel()].T, parameters)
Z = Z.reshape(xx.shape)

plt.contourf(xx, yy, Z, cmap=plt.cm.Spectral)
plt.scatter(X[0, :], X[1, :], c=Y, cmap=plt.cm.Spectral)
plt.title('Decision Boundary')
plt.show()

思考与延伸

  1. 如何改进基础算法应对梯度消失?
  2. 使用 ReLU 等非饱和激活函数
  3. 引入残差连接(ResNet)
  4. 使用 Batch Normalization

  5. 批量训练与在线训练的差异:

  6. 批量训练:更稳定的梯度方向,但计算开销大
  7. 在线训练:对噪声更敏感,但能更快适应数据变化
  8. 折中方案:小批量 (mini-batch) 训练

通过这个完整的 BP 神经网络实现,我们不仅理解了反向传播的数学原理,还掌握了实际编码实现的技巧。建议读者尝试不同的网络结构、激活函数和超参数,观察它们对模型性能的影响。

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