3Blue1Brown神经网络反向传播原理详解与Python实现

1次阅读
没有评论

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

image.webp

几何视角下的反向传播

让我们跟随 3Blue1Brown 的经典视角,将神经网络想象成一个多维空间中的地形图。损失函数构成的高度曲面中,每个点的海拔代表当前参数的误差值。梯度向量就是站在这个点上时,脚下最陡峭的下山方向。反向传播的本质,就是通过计算图(Computational Graph)逐层回溯,找到让所有参数同步下山的优化路径。

3Blue1Brown 神经网络反向传播原理详解与 Python 实现

数学推导:链式法则的舞蹈

  1. 符号定义
  2. $L$:损失函数值
  3. $W^{[l]}$:第 $l$ 层的权重矩阵
  4. $z^{[l]}$:第 $l$ 层的线性输出
  5. $a^{[l]}$:第 $l$ 层的激活输出($a^{[0]}=X$)

  6. 关键步骤分解
    以 Sigmoid 激活的双层网络为例:

  7. 前向传播流程:
    $$z^{[1]} = W^{[1]}X + b^{[1]}$$
    $$a^{[1]} = \sigma(z^{[1]})$$
    $$z^{[2]} = W^{[2]}a^{[1]} + b^{[2]}$$
    $$\hat{y} = a^{[2]} = \sigma(z^{[2]})$$

  8. 反向传播链式:
    $$\frac{\partial L}{\partial W^{[2]}} = \frac{\partial L}{\partial a^{[2]}} \frac{\partial a^{[2]}}{\partial z^{[2]}} \frac{\partial z^{[2]}}{\partial W^{[2]}}$$
    其中每个分项可具体化为:

    • $\frac{\partial L}{\partial a^{[2]}} = \frac{a^{[2]} – y}{a^{[2]}(1-a^{[2]})}$(交叉熵损失推导)
    • $\frac{\partial a^{[2]}}{\partial z^{[2]}} = a^{[2]}(1-a^{[2]})$(Sigmoid 导数特性)
    • $\frac{\partial z^{[2]}}{\partial W^{[2]}} = a^{[1]T}$

NumPy 实战:从公式到代码

import numpy as np

class TwoLayerNet:
    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 sigmoid(self, z):
        return 1/(1+np.exp(-z))

    def forward(self, X):
        self.z1 = np.dot(self.W1, X) + self.b1
        self.a1 = self.sigmoid(self.z1)
        self.z2 = np.dot(self.W2, self.a1) + self.b2
        self.a2 = self.sigmoid(self.z2)
        return self.a2

    def backward(self, X, y, learning_rate=0.1):
        m = X.shape[1]

        # 输出层梯度
        dz2 = (self.a2 - y) * self.a2 * (1 - self.a2)  # ∂L/∂z2
        dW2 = np.dot(dz2, self.a1.T) / m               # ∂L/∂W2
        db2 = np.sum(dz2, axis=1, keepdims=True) / m   # ∂L/∂b2

        # 隐藏层梯度
        dz1 = np.dot(self.W2.T, dz2) * self.a1 * (1 - self.a1) # ∂L/∂z1
        dW1 = np.dot(dz1, X.T) / m                     # ∂L/∂W1
        db1 = np.sum(dz1, axis=1, keepdims=True) / m   # ∂L/∂b1

        # 参数更新
        self.W2 -= learning_rate * dW2
        self.b2 -= learning_rate * db2
        self.W1 -= learning_rate * dW1
        self.b1 -= learning_rate * db1

避坑实践指南

  1. 梯度消失 / 爆炸检测
  2. 监控各层梯度范数:np.linalg.norm(dW1)
  3. 解决方案:

    • 改用 ReLU 激活函数
    • 权重初始化使用 He/Xavier 方法
    • 添加梯度裁剪(Gradient Clipping)
  4. 学习率调优

  5. 初始尝试范围:0.001~0.1
  6. 使用学习率衰减策略:
    lr = initial_lr * (1 / (1 + decay_rate * epoch))

延伸思考

  1. PyTorch 重构挑战
    尝试用 torch.autograd 实现相同网络,比较:
  2. 代码简洁度差异
  3. 计算效率对比

  4. 梯度验证实验
    实现数值梯度检查:

    def numerical_gradient(f, x, eps=1e-4):
        grad = np.zeros_like(x)
        for idx in range(x.size):
            tmp_val = x.flat[idx]
    
            x.flat[idx] = tmp_val + eps
            fxh1 = f(x)
    
            x.flat[idx] = tmp_val - eps
            fxh2 = f(x)
    
            grad.flat[idx] = (fxh1 - fxh2) / (2*eps)
            x.flat[idx] = tmp_val
        return grad

通过这个完整的实现链路,你会发现 3Blue1Brown 将数学直觉可视化的魔力——那些看似复杂的偏导计算,本质上都是在多维空间中寻找最优路径的导航过程。

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