BP神经网络基本原理及实例推导:从数学基础到Python实现

1次阅读
没有评论

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

image.webp

神经网络基础概念

神经网络是模拟生物神经元连接方式的计算模型。最简单的神经网络形式是感知机(Perceptron),它由输入层和输出层组成,只能解决线性可分问题。而多层神经网络(如 BP 神经网络)通过引入隐藏层和非线性激活函数,能够学习复杂的非线性关系。

BP 神经网络基本原理及实例推导:从数学基础到 Python 实现

  • 感知机局限 :无法解决异或(XOR) 等非线性问题
  • 多层网络优势:通过隐藏层组合特征,理论上可逼近任何连续函数
  • 激活函数作用:引入非线性,常见的有 Sigmoid、ReLU、Tanh 等

BP 算法数学推导

前向传播

前向传播是数据从输入层流向输出层的过程:

  1. 输入层接收特征向量 $x$
  2. 隐藏层计算:$h = \sigma(W_1x + b_1)$,其中 $\sigma$ 是激活函数
  3. 输出层计算:$\hat{y} = \sigma(W_2h + b_2)$

损失函数

常用均方误差 (MSE) 作为损失函数:

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

对于分类问题,常用交叉熵损失:

$L = -[y\ln\hat{y} + (1-y)\ln(1-\hat{y})]$

反向传播推导

反向传播通过链式法则计算梯度:

  1. 输出层梯度:
    $\frac{\partial L}{\partial W_2} = \frac{\partial L}{\partial \hat{y}}\frac{\partial \hat{y}}{\partial z_2}\frac{\partial z_2}{\partial W_2}$
    其中 $z_2 = W_2h + b_2$

  2. 隐藏层梯度:
    $\frac{\partial L}{\partial W_1} = \frac{\partial L}{\partial h}\frac{\partial h}{\partial z_1}\frac{\partial z_1}{\partial W_1}$
    其中 $z_1 = W_1x + b_1$

权重更新

使用梯度下降更新参数:

$W \leftarrow W – \eta \frac{\partial L}{\partial W}$

其中 $\eta$ 是学习率。

Python 实现

import numpy as np

class NeuralNetwork:
    def __init__(self, input_size, hidden_size, output_size):
        # 初始化权重
        self.W1 = np.random.randn(input_size, hidden_size) * 0.01
        self.b1 = np.zeros((1, hidden_size))
        self.W2 = np.random.randn(hidden_size, output_size) * 0.01
        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.h = self.sigmoid(self.z1)
        self.z2 = np.dot(self.h, self.W2) + self.b2
        self.y_hat = self.sigmoid(self.z2)
        return self.y_hat

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

        # 输出层梯度
        dL_dy_hat = -(y - self.y_hat)
        dy_hat_dz2 = self.y_hat * (1 - self.y_hat)
        dz2_dW2 = self.h

        dL_dW2 = np.dot(dz2_dW2.T, dL_dy_hat * dy_hat_dz2) / m
        dL_db2 = np.sum(dL_dy_hat * dy_hat_dz2, axis=0, keepdims=True) / m

        # 隐藏层梯度
        dL_dh = np.dot(dL_dy_hat * dy_hat_dz2, self.W2.T)
        dh_dz1 = self.h * (1 - self.h)
        dz1_dW1 = X

        dL_dW1 = np.dot(dz1_dW1.T, dL_dh * dh_dz1) / m
        dL_db1 = np.sum(dL_dh * dh_dz1, axis=0, keepdims=True) / m

        # 更新权重
        self.W2 -= learning_rate * dL_dW2
        self.b2 -= learning_rate * dL_db2
        self.W1 -= learning_rate * dL_dW1
        self.b1 -= learning_rate * dL_db1

    def train(self, X, y, epochs, learning_rate):
        for i in range(epochs):
            y_hat = self.forward(X)
            self.backward(X, y, learning_rate)
            loss = np.mean((y - y_hat)**2)
            if i % 100 == 0:
                print(f'Epoch {i}, Loss: {loss}')

# MNIST 示例
# 这里省略数据加载和预处理代码
# nn = NeuralNetwork(784, 128, 10)
# nn.train(X_train, y_train, 1000, 0.1)

关键问题讨论

学习率选择

  • 太大:可能导致震荡甚至发散
  • 太小:收敛速度过慢
  • 解决方案:使用学习率衰减或自适应优化器(Adam)

梯度消失

深层网络中梯度可能指数级减小:

  • 原因:Sigmoid 导数最大值为 0.25,多次连乘后梯度趋近 0
  • 解决方案:使用 ReLU 激活函数、残差连接、批归一化

过拟合

模型在训练集表现好但泛化能力差:

  • 解决方案:
  • 增加训练数据
  • 使用 L1/L2 正则化
  • Dropout 技术
  • 早停(Early Stopping)

避坑指南

  1. 初始化问题:权重初始化为小随机数,避免全零初始化
  2. 输入归一化:将特征缩放到相似范围(如 0 -1)
  3. 梯度检查:实现反向传播后,使用数值梯度验证
  4. 损失不下降:检查学习率、网络结构、数据预处理
  5. 输出饱和:Sigmoid 输出接近 0 / 1 时梯度很小,可尝试交叉熵损失

总结

BP 神经网络通过前向传播和反向传播的交替进行实现参数学习。理解其数学本质有助于调试网络和解决实际问题。虽然现在有更先进的网络结构,但 BP 算法仍然是深度学习的基础。建议读者从简单网络开始,逐步增加复杂度,在实践中积累经验。

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