BP神经网络示意图解析:从数学原理到Python实现

1次阅读
没有评论

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

image.webp

3D 结构示意图入门

先来看一个典型的三层 BP 网络 3D 示意图(想象一下立体结构):

  • 输入层:假设是 3 个神经元,对应矩阵维度 [3×1]
  • 隐藏层:设定为 4 个神经元,第一层权重矩阵维度 [4×3]
  • 输出层:2 个神经元输出,第二层权重矩阵维度 [2×4]

箭头连接线粗细代表权重大小,红色表示正向传播,蓝色表示反向传播。这种可视化能帮我们直观理解 ”W 的维度 = 下一层神经元数×当前层神经元数 ” 的黄金法则。

数学原理拆解

1. 前向传播的向量化计算

前向传播就是数据流过网络的过程:

  1. 输入数据 X(形状[batch_size, input_dim])
  2. 隐藏层计算:Z1 = W1·X + b1(注意矩阵乘法的行列对应关系)
  3. 激活函数:A1 = relu(Z1)(ReLU 比 sigmoid 更常用,能缓解梯度消失)
  4. 输出层计算:Z2 = W2·A1 + b2
  5. 最终输出:A2 = softmax(Z2)(分类任务用 softmax,回归用线性输出)

关键技巧:始终用 np.dot() 做矩阵乘法,避免用 * 导致逐元素相乘的错误。

2. 反向传播梯度推导

损失函数对 W2 的偏导(以交叉熵损失为例):

∂L/∂W2 = (A2 - Y) · A1.T  # Y 是真实标签的 one-hot 编码

对 W1 的偏导(链式法则的经典应用):

∂L/∂W1 = (W2.T · (A2-Y)) * relu_derivative(Z1) · X.T

BP 神经网络示意图解析:从数学原理到 Python 实现
(示意图:展示∂L/∂W 的分步推导过程)

3. 学习率与权重更新

实现权重更新的核心代码:

learning_rate = 0.01
W1 -= learning_rate * dW1  # 注意是 -= 不是 +=
b1 -= learning_rate * db1

Python 完整实现

import numpy as np
import matplotlib.pyplot as plt

class NeuralNetwork:
    def __init__(self, input_size, hidden_size, output_size):
        self.W1 = np.random.randn(hidden_size, input_size) * 0.01
        self.b1 = np.zeros((hidden_size, 1))
        self.W2 = np.random.randn(output_size, hidden_size) * 0.01
        self.b2 = np.zeros((output_size, 1))

    def forward(self, X):
        self.Z1 = np.dot(self.W1, X) + self.b1
        self.A1 = np.maximum(0, self.Z1)  # ReLU
        self.Z2 = np.dot(self.W2, self.A1) + self.b2
        exp_scores = np.exp(self.Z2 - np.max(self.Z2))
        self.A2 = exp_scores / np.sum(exp_scores, axis=0)
        return self.A2

    def backward(self, X, Y):
        m = X.shape[1]
        dZ2 = self.A2 - Y
        dW2 = np.dot(dZ2, self.A1.T) / m
        db2 = np.sum(dZ2, axis=1, keepdims=True) / m

        dZ1 = np.dot(self.W2.T, dZ2) * (self.Z1 > 0)
        dW1 = np.dot(dZ1, X.T) / m
        db1 = np.sum(dZ1, axis=1, keepdims=True) / m

        return dW1, db1, dW2, db2

    def train(self, X, Y, epochs, lr):
        losses = []
        for i in range(epochs):
            A2 = self.forward(X)
            loss = -np.mean(Y * np.log(A2 + 1e-8))
            losses.append(loss)

            dW1, db1, dW2, db2 = self.backward(X, Y)
            self.W1 -= lr * dW1
            self.b1 -= lr * db1
            self.W2 -= lr * dW2
            self.b2 -= lr * db2

            if i % 100 == 0:
                print(f"Epoch {i}, loss: {loss:.4f}")

        plt.plot(losses)
        plt.title("Training Loss Curve")
        plt.show()

# 梯度检查实现
def gradient_check(nn, X, Y, epsilon=1e-7):
    params = nn.W1.flatten()
    grad_approx = np.zeros_like(params)

    for i in range(len(params)):
        theta_plus = params.copy()
        theta_plus[i] += epsilon
        theta_minus = params.copy()
        theta_minus[i] -= epsilon

        loss_plus = -np.mean(Y * np.log(nn.forward(X)))
        loss_minus = -np.mean(Y * np.log(nn.forward(X)))
        grad_approx[i] = (loss_plus - loss_minus) / (2*epsilon)

    grad = nn.backward(X, Y)[0].flatten()
    difference = np.linalg.norm(grad_approx - grad) / np.linalg.norm(grad_approx + grad)
    print(f"Gradient check difference: {difference}")

五大避坑指南

  1. 隐藏层神经元数量:
  2. 起始公式:hidden_size = (input_size + output_size) // 2
  3. 根据验证集表现调整,通常 2 - 3 倍输入维度效果较好

  4. ReLU 死亡神经元:

  5. 使用 LeakyReLU:np.where(Z1>0, Z1, 0.01*Z1)
  6. 初始化权重时减小方差:W1 = np.random.randn(...) * np.sqrt(2/n_input)

  7. 批量归一化实战:

    # 在前向传播中加入
    self.gamma = np.ones((hidden_size, 1))
    self.beta = np.zeros((hidden_size, 1))
    Z1_normalized = (Z1 - np.mean(Z1, axis=0)) / np.std(Z1, axis=0)
    A1 = np.maximum(0, self.gamma * Z1_normalized + self.beta)

  8. 学习率选择:

  9. 先用 0.001 测试,观察损失曲线
  10. 如果震荡太大降低 10 倍,下降太慢增大 5 倍

  11. 维度检查技巧:

  12. 在每步计算后添加assert W1.shape == (hidden_size, input_size)
  13. 使用 np.sum(axis=1, keepdims=True) 保持维度一致

思考题与扩展

如何实现带动量 (Momentum) 的优化器?核心思路:

# 在类初始化中添加
self.v_dW1 = np.zeros_like(self.W1)
self.v_db1 = np.zeros_like(self.b1)

# 修改权重更新部分
beta = 0.9  # 动量系数
self.v_dW1 = beta * self.v_dW1 + (1-beta) * dW1
self.W1 -= lr * self.v_dW1

通过记录历史梯度方向,能加速收敛并减少震荡。完整实现还需要考虑偏差校正(特别是训练初期)。这个改进能让你的神经网络在复杂任务上训练速度提升 3 - 5 倍!

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