从人工神经元到神经网络实战:前向传播与反向传播算法详解

1次阅读
没有评论

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

image.webp

神经网络已成为计算机视觉、自然语言处理等领域的核心工具,能够自动学习数据特征并实现端到端的复杂映射。其核心价值在于:1) 通过多层非线性变换提取高阶特征;2) 对图像 / 文本等非结构化数据具有强大表征能力;3) 在 GPU 加速下可处理海量数据。下面我们从最基础的人工神经元开始,逐步解析神经网络的实现原理。

从人工神经元到神经网络实战:前向传播与反向传播算法详解

人工神经元模型

单个神经元 (Neuron) 是神经网络的基本单元,其数学表示为:

$$
z = \sum_{i=1}^n w_i x_i + b
$$
$$
a = \sigma(z)
$$

其中:
– $x_i$: 输入特征(input features)
– $w_i$: 对应权重(weights)
– $b$: 偏置项(bias)
– $\sigma$: 激活函数(activation function)

图示说明:

   x₁ ──── w₁ ────\
   x₂ ──── w₂ ───── (Σ + b) ── σ ── a
   ...             /
   xₙ ──── wₙ ────

前向传播与反向传播

前向传播(Forward Propagation)

对于 L 层神经网络,前向计算流程为:

  1. 输入层:$a^{[0]} = X$
  2. 隐藏层:$z^{[l]} = W^{[l]}a^{[l-1]} + b^{[l]}$
  3. 激活层:$a^{[l]} = \sigma(z^{[l]})$
  4. 输出层:$\hat{y} = a^{[L]}$

反向传播(Backpropagation)

通过链式法则计算梯度:

  1. 输出层误差:$\delta^{[L]} = \frac{\partial \mathcal{L}}{\partial a^{[L]}} \odot \sigma'(z^{[L]})$
  2. 隐藏层误差:$\delta^{[l]} = (W^{[l+1]T}\delta^{[l+1]}) \odot \sigma'(z^{[l]})$
  3. 参数梯度:
    $\frac{\partial \mathcal{L}}{\partial W^{[l]}} = \delta^{[l]}a^{[l-1]T}$
    $\frac{\partial \mathcal{L}}{\partial b^{[l]}} = \delta^{[l]}}$

Python 实现

import numpy as np

class NeuralNetwork:
    def __init__(self, layer_dims, activation='relu'):
        """
        初始化网络参数
        :param layer_dims: 各层神经元数量,如[784, 128, 10]
        :param activation: 激活函数类型
        """
        self.params = {}
        self.activation = activation

        # He 初始化
        for l in range(1, len(layer_dims)):
            self.params[f'W{l}'] = np.random.randn(layer_dims[l], layer_dims[l-1]) * np.sqrt(2/layer_dims[l-1])
            self.params[f'b{l}'] = np.zeros((layer_dims[l], 1))

    def _activate(self, z):
        """激活函数实现"""
        if self.activation == 'sigmoid':
            return 1 / (1 + np.exp(-z))
        elif self.activation == 'relu':
            return np.maximum(0, z)

    def _activate_derivative(self, a):
        """激活函数导数"""
        if self.activation == 'sigmoid':
            return a * (1 - a)
        elif self.activation == 'relu':
            return (a > 0).astype(float)

    def forward(self, X):
        """前向传播"""
        self.cache = {'A0': X}
        L = len(self.params) // 2

        for l in range(1, L+1):
            W = self.params[f'W{l}']
            b = self.params[f'b{l}']
            Z = W.dot(self.cache[f'A{l-1}']) + b
            self.cache[f'Z{l}'] = Z
            self.cache[f'A{l}'] = self._activate(Z)

        return self.cache[f'A{L}']

    def backward(self, Y, learning_rate=0.01):
        """反向传播"""
        L = len(self.params) // 2
        m = Y.shape[1]
        grads = {}

        # 输出层误差
        dZ = self.cache[f'A{L}'] - Y
        grads[f'dW{L}'] = dZ.dot(self.cache[f'A{L-1}'].T) / m
        grads[f'db{L}'] = np.sum(dZ, axis=1, keepdims=True) / m

        # 隐藏层误差
        for l in reversed(range(1, L)):
            dA = self.params[f'W{l+1}'].T.dot(dZ)
            dZ = dA * self._activate_derivative(self.cache[f'A{l}'])
            grads[f'dW{l}'] = dZ.dot(self.cache[f'A{l-1}'].T) / m
            grads[f'db{l}'] = np.sum(dZ, axis=1, keepdims=True) / m

        # 参数更新
        for l in range(1, L+1):
            self.params[f'W{l}'] -= learning_rate * grads[f'dW{l}']
            self.params[f'b{l}'] -= learning_rate * grads[f'db{l}']

实践建议

梯度消失问题

当使用 Sigmoid 激活函数时,其导数最大值为 0.25,多层连乘会导致梯度指数级减小。解决方案:

  • 使用 ReLU 及其变体(LeakyReLU, PReLU)
  • 添加 Batch Normalization 层
  • 残差连接(ResNet)

优化算法选择

算法 优点 缺点
SGD 简单 容易陷入局部最优
Momentum 加速收敛 需调整动量参数
Adam 自适应学习率 内存占用较大

推荐初始学习率设置:
– SGD: 0.01-0.1
– Adam: 0.001-0.0001

思考题

  1. 如何利用 NumPy 的广播机制实现批量样本的并行计算?
  2. Xavier 初始化与 Kaiming 初始化分别适用于什么场景?
  3. Dropout 和 L2 正则化哪种更适合处理过拟合问题?为什么?

通过本文的实践,读者应能掌握神经网络的核心原理与实现方法。建议在 MNIST 或 CIFAR-10 等标准数据集上测试代码效果,逐步调整网络深度和超参数以获得更好性能。

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