BP神经网络入门指南:正向传播与反向传播的数学原理与代码实现

1次阅读
没有评论

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

image.webp

神经网络基础概念

在开始讨论 BP 神经网络之前,我们先了解一下神经网络的基本结构。神经网络是一种模仿人脑神经元连接方式的计算模型,它由多个层次组成:输入层、隐藏层和输出层。输入层接收原始数据,隐藏层负责特征提取和转换,输出层给出最终结果。

BP 神经网络入门指南:正向传播与反向传播的数学原理与代码实现

正向传播的数学推导

正向传播是指数据从输入层流向输出层的过程。在这个过程中,每一层的神经元都会对前一层的输出进行加权求和,并通过激活函数进行非线性转换。

  1. 加权求和 :对于第 l 层的第 j 个神经元,其输入为前一层的输出与权重的乘积之和加上偏置。数学表达式为:
z_j^l = \sum_{i} w_{ji}^l a_i^{l-1} + b_j^l
  1. 激活函数计算 :激活函数引入非线性,使神经网络能够拟合复杂函数。常用的激活函数有 Sigmoid、ReLU 和 Tanh。数学表达式为:
a_j^l = \sigma(z_j^l)

反向传播算法

反向传播是通过计算损失函数的梯度来调整网络权重和偏置的过程。它主要包括以下步骤:

  1. 损失函数计算 :损失函数衡量预测值与真实值之间的差距。常用的损失函数有均方误差(MSE)和交叉熵(Cross-Entropy)。

  2. 梯度下降 :通过计算损失函数对权重和偏置的梯度,沿着梯度的反方向更新参数,以最小化损失函数。数学表达式为:

w_{ji}^l = w_{ji}^l - \eta \frac{\partial L}{\partial w_{ji}^l}
  1. 链式法则 :反向传播的核心是链式法则,用于计算梯度。对于输出层的梯度:

\delta_j^L = \frac{\partial L}{\partial a_j^L} \sigma'(z_j^L)

对于隐藏层的梯度:

\delta_j^l = \sum_k w_{kj}^{l+1} \delta_k^{l+1} \sigma'(z_j^l)

Python 实现代码

下面是一个使用 NumPy 实现的 BP 神经网络的简单示例,用于解决一个二分类问题。

import numpy as np

class NeuralNetwork:
    def __init__(self, input_size, hidden_size, output_size):
        self.weights1 = np.random.randn(input_size, hidden_size)
        self.weights2 = np.random.randn(hidden_size, output_size)
        self.bias1 = np.zeros(hidden_size)
        self.bias2 = np.zeros(output_size)

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

    def sigmoid_derivative(self, x):
        return x * (1 - x)

    def forward(self, X):
        self.hidden = self.sigmoid(np.dot(X, self.weights1) + self.bias1)
        self.output = self.sigmoid(np.dot(self.hidden, self.weights2) + self.bias2)
        return self.output

    def backward(self, X, y, output, learning_rate):
        output_error = y - output
        output_delta = output_error * self.sigmoid_derivative(output)

        hidden_error = np.dot(output_delta, self.weights2.T)
        hidden_delta = hidden_error * self.sigmoid_derivative(self.hidden)

        self.weights2 += learning_rate * np.dot(self.hidden.T, output_delta)
        self.bias2 += learning_rate * np.sum(output_delta, axis=0)
        self.weights1 += learning_rate * np.dot(X.T, hidden_delta)
        self.bias1 += learning_rate * np.sum(hidden_delta, axis=0)

    def train(self, X, y, epochs, learning_rate):
        for epoch in range(epochs):
            output = self.forward(X)
            self.backward(X, y, output, learning_rate)
            if epoch % 1000 == 0:
                loss = np.mean(np.square(y - output))
                print(f'Epoch {epoch}, Loss: {loss}')

# 示例数据
X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
y = np.array([[0], [1], [1], [0]])

# 创建并训练网络
nn = NeuralNetwork(2, 4, 1)
nn.train(X, y, 10000, 0.1)

避坑指南

  1. 梯度消失 / 爆炸 :当梯度值过小或过大时,会导致训练不稳定。解决方法包括使用 ReLU 激活函数、梯度裁剪和批归一化。

  2. 学习率选择 :学习率过大可能导致震荡,过小则收敛慢。可以使用学习率衰减或自适应优化器如 Adam。

  3. 激活函数选择 :不同激活函数适用于不同场景。Sigmoid 适合输出层,ReLU 适合隐藏层,Tanh 在中间层表现良好。

思考题

  1. 如何改进基础 BP 算法提高收敛速度?
  2. 批量训练与在线训练的优劣比较?
  3. 不同优化器(如 Adam)的实现原理是什么?

希望通过这篇文章,你能对 BP 神经网络的正向传播和反向传播有一个清晰的理解,并能够动手实现一个简单的神经网络模型。

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