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

1次阅读
没有评论

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

image.webp

神经网络基础回顾

BP 神经网络是一种典型的前馈神经网络,它由输入层、隐藏层和输出层组成。训练神经网络的核心目标是通过调整权重和偏置,使得网络的输出尽可能接近真实值。而反向传播算法就是实现这一目标的关键技术。

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

反向传播的必要性在于它提供了一种高效计算梯度的方法。通过链式法则,我们可以从输出层开始,逐层反向计算误差对各个参数的梯度,然后使用梯度下降法更新参数。

反向传播数学推导

前向传播

首先我们定义前向传播的过程。对于一个 L 层的神经网络,第 l 层的输出可以表示为:

$$a^l = \sigma(z^l) = \sigma(W^l a^{l-1} + b^l)$$

其中 $\sigma$ 是激活函数,$W^l$ 是权重矩阵,$b^l$ 是偏置向量。

损失函数

我们使用平方误差作为损失函数:

$$J = \frac{1}{2} \sum_{i=1}^n (y_i – a^L_i)^2$$

反向传播

反向传播的核心是计算损失函数对各层参数的梯度。我们首先定义误差项 $\delta^l$:

$$\delta^l = \frac{\partial J}{\partial z^l}$$

对于输出层 (L 层):

$$\delta^L = \frac{\partial J}{\partial a^L} \odot \sigma'(z^L) = (a^L – y) \odot \sigma'(z^L)$$

对于隐藏层 (l 层):

$$\delta^l = ((W^{l+1})^T \delta^{l+1}) \odot \sigma'(z^l)$$

然后可以计算各参数的梯度:

$$\frac{\partial J}{\partial W^l} = \delta^l (a^{l-1})^T$$

$$\frac{\partial J}{\partial b^l} = \delta^l$$

Python 代码实现

import numpy as np

class NeuralNetwork:
    def __init__(self, layer_sizes):
        self.layer_sizes = layer_sizes
        self.weights = [np.random.randn(y, x) * 0.1 
                        for x, y in zip(layer_sizes[:-1], layer_sizes[1:])]
        self.biases = [np.random.randn(y, 1) * 0.1 
                       for y in layer_sizes[1:]]

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

    def sigmoid_prime(self, z):
        return self.sigmoid(z) * (1 - self.sigmoid(z))

    def forward(self, x):
        a = x
        for w, b in zip(self.weights, self.biases):
            z = np.dot(w, a) + b
            a = self.sigmoid(z)
        return a

    def train(self, X, y, epochs=1000, learning_rate=0.1):
        for epoch in range(epochs):
            # 初始化梯度
            weight_gradients = [np.zeros(w.shape) for w in self.weights]
            bias_gradients = [np.zeros(b.shape) for b in self.biases]

            # 对每个样本进行训练
            for x, target in zip(X, y):
                # 前向传播
                activations = [x]
                zs = []
                for w, b in zip(self.weights, self.biases):
                    z = np.dot(w, activations[-1]) + b
                    zs.append(z)
                    activations.append(self.sigmoid(z))

                # 反向传播
                delta = (activations[-1] - target) * self.sigmoid_prime(zs[-1])
                bias_gradients[-1] += delta
                weight_gradients[-1] += np.dot(delta, activations[-2].T)

                for l in range(2, len(self.layer_sizes)):
                    z = zs[-l]
                    sp = self.sigmoid_prime(z)
                    delta = np.dot(self.weights[-l+1].T, delta) * sp
                    bias_gradients[-l] += delta
                    weight_gradients[-l] += np.dot(delta, activations[-l-1].T)

            # 更新参数
            for i in range(len(self.weights)):
                self.weights[i] -= learning_rate * weight_gradients[i] / len(X)
                self.biases[i] -= learning_rate * bias_gradients[i] / len(X)

具体计算例题演示

假设我们有一个简单的 2 -2- 1 神经网络,输入为 [0.5, 0.8],目标输出为 [0.7]。我们随机初始化权重和偏置:

  1. 前向传播计算:
  2. 输入层到隐藏层计算
  3. 隐藏层到输出层计算

  4. 计算输出误差

  5. 反向传播:

  6. 计算输出层梯度
  7. 计算隐藏层梯度

  8. 更新参数

常见问题与优化建议

梯度消失 / 爆炸

梯度消失和爆炸是训练深层网络时的常见问题。解决方案包括:

  • 使用 ReLU 等激活函数
  • 权重初始化技巧
  • Batch Normalization

学习率选择

学习率太大可能导致震荡,太小则收敛慢。可以尝试:

  • 学习率衰减
  • 自适应优化器 (Adam, RMSprop)

性能优化

  • 使用向量化计算
  • 利用 GPU 加速
  • 小批量训练

总结与思考

通过本文,我们详细探讨了 BP 神经网络的反向传播算法,从数学推导到 Python 实现。在实际应用中,还需要考虑很多工程细节和调参技巧。

思考题:如何将反向传播算法应用到卷积神经网络中?关键在于理解卷积层和池化层的反向传播计算方式。

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