直觉:一个会自我修正的可微分函数
神经网络去掉神秘外壳后,就是一个带很多旋钮(参数)的函数。前向传播 = 把输入塞进去算出预测;反向传播 = 根据「错了多少」反推「每个旋钮该往哪拧、拧多少」。整个训练就是这两步的循环,靠链式法则把误差信号从输出一路传回输入端。
下面用一个最小的单隐层网络把数据流、公式和代码全部走通。
机制:前向传播的数据流
设网络结构为 输入 x∈Rd → 隐层(权重 W1、偏置 b1、激活 σ)→ 输出层(W2,b2)。前向传播逐层计算:
\begin{aligned}
z_1 &= W_1 x + b_1 \\
a_1 &= \sigma(z_1) \\
z_2 &= W_2 a_1 + b_2 \\
\hat{y} &= z_2 \quad (\text{回归}) \quad \text{或} \quad \text{softmax}(z_2)\ (\text{分类})
\end{aligned}
每一层都是「线性变换 + 非线性」。非线性 σ(如 ReLU:σ(z)=max(0,z))是灵魂——没有它,多层会塌缩成单层线性映射。
然后用损失函数衡量预测与真值的差距。回归常用均方误差:
L=21∥y^−y∥2
公式:反向传播就是链式法则的工程化
我们要的是 ∂L/∂Wl 和 ∂L/∂bl,好用梯度下降更新。直接对每个参数求偏导会重复大量计算;反向传播的精髓是从后往前逐层复用中间梯度,把复杂度从指数级降到与前向传播同量级。
定义每层的「误差信号」 δl=∂L/∂zl。对上面的网络:
δ2∂W2∂Lδ1∂W1∂L=y^−y=δ2a1⊤,∂b2∂L=δ2=(W2⊤δ2)⊙σ′(z1)=δ1x⊤,∂b1∂L=δ1
其中 ⊙ 是逐元素相乘,σ′ 是激活的导数。注意这个递推结构:深层的 δ 通过 W⊤ 反向「传播」到浅层,再乘上本层激活的导数。这就是「反向传播」名字的由来。
更新参数(学习率 η):
θ←θ−η∂θ∂L
代码:30 行手写一个训练步
不用任何框架,把上面的公式落成 NumPy,最能看清没有魔法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| import numpy as np
def relu(z): return np.maximum(0, z) def relu_grad(z): return (z > 0).astype(z.dtype)
def train_step(x, y, W1, b1, W2, b2, lr=0.01): z1 = W1 @ x + b1 a1 = relu(z1) z2 = W2 @ a1 + b2 y_hat = z2 loss = 0.5 * np.sum((y_hat - y) ** 2)
d2 = (y_hat - y) dW2 = np.outer(d2, a1) db2 = d2 d1 = (W2.T @ d2) * relu_grad(z1) dW1 = np.outer(d1, x) db1 = d1
W1 -= lr * dW1; b1 -= lr * db1 W2 -= lr * dW2; b2 -= lr * db2 return loss, (W1, b1, W2, b2)
|
现代框架(PyTorch/JAX)用自动微分自动完成反向部分:前向时构建计算图,loss.backward() 沿图反向遍历套用链式法则。你写的还是前向逻辑,梯度是「免费」得到的——但底层做的正是上面这套递推。
工程权衡与踩坑
- 梯度消失/爆炸:δ 每反向穿过一层就乘一次 W⊤ 和 σ′。层很深时,这些因子连乘会指数级缩小(消失)或放大(爆炸)。Sigmoid/tanh 的导数最大值小于 1,深层尤其容易消失——这正是 ReLU、残差连接、归一化层流行的原因。
- 学习率是头号超参:太大震荡甚至发散,太小训练奇慢。实践中配合 Adam 这类自适应优化器和学习率调度。
- batch 的作用:单样本梯度噪声大。用 mini-batch 求平均梯度,既稳定又能用矩阵乘法吃满 GPU 并行。
- 显存账:训练显存 ≈ 参数 + 梯度 + 优化器状态 + 激活值。反向传播需要前向的中间激活 al,所以激活值往往是显存大头;这也是「梯度检查点(gradient checkpointing)」用计算换显存的前提——丢弃部分激活,反向时重算。
- 初始化不能全零:若 W 全相同,所有神经元对称地接收相同梯度,永远学不出不同特征。需用随机初始化(如 He/Xavier)打破对称。
小结
神经网络 = 前向传播算预测 + 反向传播算梯度 + 梯度下降更新参数,三步循环。前向是逐层「线性 + 非线性」的复合;反向是链式法则的工程化——用 δ 递推从输出把误差信号传回每一层,从而高效复用中间结果。框架的自动微分替你写了反向代码,但理解 δl=(Wl+1⊤δl+1)⊙σ′(zl) 这条递推,才能解释梯度消失、显存占用、初始化这些真实工程问题的根源。
Author:
cola
Permalink:
http://www.zjrzb.cn/2026/01/11/how-neural-networks-work/
License:
Copyright (c) 2021
Slogan:
Do you believe in DESTINY?