深度学习基础:从反向传播到残差
系列精华 · 九篇正文每篇一页,按 ↓ 看实验与原论文图
arganzheng · 2026-03-29
这个系列回答一个问题
深网络训练里的每个现象,能不能推到公式、代到数字、用几十行代码复现,然后在 LLM 里认出它的形态?
- 主线是一条连乘链:前向的方差每层乘一个因子、反向的梯度是一串 Jacobian 的乘积——因子偏离 1 就指数放大或消失
- 01 按年代回顾模型演进与 XOR → 02 写出梯度乘积 → 03 激活改变梯度 → 04 稳定深层训练 → 05 步长怎么定 → 06 泛化 → 07–09 看视觉、表示与序列
- 九篇用同一份几百行的 NumPy 小框架和配套脚本,实验可在笔记本 CPU 上复现
全景:一条连乘链
%%{init: {"flowchart": {"wrappingWidth": 190}}}%%
flowchart TB
H["01 深度模型演进史<br/>感知机 → Transformer"] --> D1["02 反向传播<br/>写出那串 Jacobian 的乘积"]
D1 --> D2["03 激活函数<br/>非线性改变梯度与 FFN"]
D2 --> D3["04 初始化 / 归一化 / 残差<br/>稳定深层训练"]
D3 --> D4["05 优化器<br/>拿到稳定的梯度后步长怎么定"]
D4 --> D5["06 正则化与泛化<br/>挑出的解为什么泛化、何时不再"]
D3 --> D6["07 CNN<br/>ResNet 的退化问题 = 连乘的历史形态一"]
D6 --> D7["08 Embedding<br/>离散 token → 可学习向量"]
D7 --> D8["09 RNN<br/>BPTT 衰减;LSTM 遗忘门 ≈ 残差"]
D8 -. "seq2seq 瓶颈 → attention 诞生" .-> T["→ L4 Transformer"]
D6 -. "patch → token(ViT)" .-> T

按年代看,从阈值单元、感知机与两次寒冬,到反向传播、预训练、CNN、Embedding、attention 与 Transformer;结构分支彼此交叉,归纳偏置、可训练性与并行度也随之变化。
XOR:能表示,不等于能学会

感知机的单一线性边界无法解 XOR;隐藏层提供表达能力,初始化与优化仍影响能否找到解。
02 · 反向传播:手推一个两层网络
结论:反向传播 = 沿计算图反向拓扑序对每个算子算一次 VJP,从不构造 Jacobian;每个 Linear 反向做两个 GEMM,所以反向 = 2 × 前向、训练 \(6ND\);\(\partial L/\partial W = X^\top G\) 需要本层输入,所以激活要存。

三个公式与两个数字
| 量 |
公式 |
形状检查 |
| 输出层梯度 |
\(G = (P - Y)/m\) |
与 logits 同形 |
| 权重梯度 |
\(\partial L/\partial W = X^\top G\) |
\([n_{in}, m] \times [m, n_{out}]\)——需要本层输入 \(X\) |
| 传给下一层 |
\(\partial L/\partial X = G\,W^\top\) |
\([m, n_{out}] \times [n_{out}, n_{in}]\) |
- 一个 20 万参数网络的一层 Jacobian 就有 13 GB——所以只算 \(J^\top v\)
- 激活 552 KiB 对权重 795 KiB,batch × 32 → 17.3 MiB:激活显存与 batch × 序列 × 层数 × 宽度成正比,与参数量无关;重算用 33% 算力换掉(\(8ND\))
03 · 激活函数:从 sigmoid 到 SwiGLU
结论:没有非线性,多层 Linear 仍可折叠成一层;激活的导数决定梯度能否穿过深层,门控激活则增加一个按输入选择信息的分支。小规模 MNIST 实验里,sigmoid 梯度在 20 层后消失并停在机会水平,其他五种激活均能训练。

ReLU 到 SwiGLU
- sigmoid:导数最大 0.25,重复相乘;\(0.25^{10}\approx 9.54\times10^{-7}\)
- ReLU:正半轴梯度为 1,但实验中约 12.26% 的隐藏单元对测试样本恒为 0
- GELU / SiLU:平滑门控;GPT-2 使用
gelu_new
- SwiGLU:三矩阵门控 FFN;Llama、PaLM 等采用
04 · 训练为什么不稳定:初始化、归一化与残差
结论:深网络的稳定性是连乘问题;初始化只管 \(t = 0\),归一化切断前向的连乘但修不了反向,残差把 Jacobian 变成 \(I + J\) 给梯度一条恒等通路;三样必须同用,Pre-Norm 是当前摆法。

七种接法只有 Pre-Norm 两个学习率都能训

- Kaiming 初始化的 64 层 plain 网络:梯度范数在层间 1.7 到 13,lr 0.05 三步 NaN——初始化只管 \(t = 0\)
- 加了 LN 不加残差:300 步 37%;Post-Norm 顶层梯度是底层 4 倍,lr 0.05 时 8.8% 对 Pre-Norm 91.6%
Post-LN vs Pre-LN(Xiong 等 2020)

- 残差流无归一化每层翻倍(\(2^{64}\)),Pre-Norm 下线性增长;GPT-2 把残差分支缩放 \(1/\sqrt{2L}\);LLM 用 Pre-Norm 加 final norm
05 · 优化器:从 SGD 到 AdamW 与学习率调度
结论:Adam 让每个参数的步长 \(\approx \eta\)、与梯度大小无关——所以第一步是满步长 \(\eta \cdot \text{sign}(g)\)、必须 warmup;\(L_2\) 被 \(1/\sqrt v\) 缩放而 AdamW 不被;线性 scaling 只在临界 batch 内成立。

warmup 为什么不能省

- 偏差修正后第一步是 \(\eta \cdot \text{sign}(g)\),\(v\) 还没学到尺度
- batch 翻倍 lr 翻倍:512 成立、2048 NaN——只在「\(k\) 步内梯度基本不变」时成立;裁剪 1.0 限制步长上界
Adam + L2 不是 AdamW(Kingma & Ba 2014 Algorithm 1)

- \(\lambda\theta\) 混进 \(g\) 被 \(1/\sqrt v\) 缩放,梯度小的参数被过度衰减;同一 \(\lambda = 0.1\):\(\lVert W_1\rVert\) 2.33 对 23.59,准确率 86.2% 对 95.6%
- \(\beta_2 = 0.95\) 记 20 步;Momentum 有效学习率 \(\times 1/(1-\beta)\)
06 · 正则化与泛化:为什么参数比样本多却不过拟合
结论:先算参数 / 数据比定体制;过参数化时优化器挑最小范数、平坦、先学简单的解——隐式正则化;double descent 越过插值阈值再变好;预训练在数据体制里不用 dropout、一个 epoch。

过拟合最常见的形态:loss 涨而准确率不动

- 测试 loss 0.40 → 0.55 而准确率不动——是过度自信;dropout 0.5 到 300 epoch 反而最差(0.62)
- 重复数据 4 epoch 以内几乎无损;2 万字符第 16 epoch 拐点、记忆率 10%,200 万字符未到
07 · CNN:从 LeNet 到 ResNet,再到 ViT
结论:卷积 = 全连接 + 局部性 + 参数共享两条约束;深度是为了感受野,深了就训不动——BN 修前向、残差修反向;数据够多时约束成负担,ViT 把图切成 token,只留下 patch embedding 这一个 stride 卷积。


- plain 56 层 loss 0.72、首末层梯度比 2849——退化问题就是连乘;bottleneck 69K 参数对 1.18M
- ResNet-50 25.6M 参数、8.2 GFLOPs(4.1 数的是 MACs)、每参数用 320 次
- ViT:196 / 576 / 5476 个 token;patch embedding = kernel = stride = p 的卷积,两种写法差 \(10^{-6}\)、参数同为 590,592
案例:复现 LeNet-5

- 61,706 个参数、5 个 epoch、0.82%——MLP 的 1/3 参数、1/3 错误率;第一层学出边缘检测器
08 · Embedding 与表示学习:从查表到句向量
结论:one-hot 矩阵乘法等价于 embedding 表查一行;梯度只更新被访问的行。Word2Vec 从上下文预测目标或从目标预测上下文,使向量几何反映共现关系;Transformer 则让表示随上下文变化。

参数账与实验读数
- GPT-2 small 输入 embedding:50,257 × 768 = 38,597,376 个参数,占模型约 31.0%
- 训练后
computer 的近邻包括 computers / machines / computing / hardware / software;king - man + woman 排除输入词后没有命中 queen
- 完整语料训练仍不保证每个类比都命中预期词;PCA 展示投影,不代表高维距离被完整保留
09 · RNN:从 LSTM 到 attention 的诞生
结论:BPTT 是同一个 \(W\) 的连乘,训练信号传不到远处;LSTM 的 \(c_t = f_t \odot c_{t-1} + i_t \odot \tilde c_t\) 是时间上的残差,遗忘门偏置要初始化为 1;attention 为绕过 seq2seq 的固定向量瓶颈而生,然后人们发现循环可以不要。

从固定向量瓶颈到对齐(Bahdanau 等 2015)

- seq2seq 把整句压进一个向量(Sutskever 2014);倒序 16 token 任务整句正确率 0% → 76%
- attention 算力 4.5 倍、\(O(T^2 d)\) 对 \(O(Td^2)\)——换来的是可并行与不衰减的通路
案例:字符级 LSTM 写莎士比亚,与 nanoGPT 同预算

- 这个规模看不出 Transformer 的优势——差别在顺序 vs 并行;RNN 记不住远处不是隐状态太小(64 维装得下 69 bit),是训练信号传不到
%%{init: {"flowchart": {"wrappingWidth": 210}}}%%
flowchart TB
CHAIN["梯度 = Jacobian 连乘(02,04)"] --> VAN["因子偏离 1 → 指数消失 / 爆炸(04,07,09)"]
VAN --> INIT["初始化:t = 0 时因子期望为 1"]
VAN --> NORM["归一化:前向方差拉回 1"]
VAN --> RES["残差:I + J 的恒等通路"]
RES --> GROW["残差流方差增长 → 归一化 + 1/√(2L)"]
RES --> LSTM["LSTM 遗忘门 = 时间上的残差(09)"]
RES --> PRE["Pre-Norm / ResNet-v2(04,07)"]
NORM --> SCALE["尺度不变性 → 有效学习率由范数决定"]
SCALE --> WD["AdamW weight decay = 范数平衡点(05,06)"]
ADAM["Adam 步长 ≈ η(05)"] --> WARM["warmup、β₂ = 0.95、η ∝ 1/n_in"]
常见误区
- 反向传播要算出每层的 Jacobian——一层就 13 GB,只算 VJP
- 激活显存与参数量成正比——与 batch × 序列 × 层数 × 宽度成正比
- 初始化对了深网络就能训——初始化只管 \(t = 0\),64 层 plain 三步 NaN
- Adam + L2 就是 AdamW——\(\lambda\theta\) 被 \(1/\sqrt v\) 缩放,86.2% 对 95.6%
- 参数比样本多就会过拟合——407 倍的网络测试 loss 最好;别停在插值阈值
- 验证准确率没掉就没过拟合——loss 涨而准确率不变是过度自信
- ViT 完全不用卷积——patch embedding 就是 stride 卷积
- Embedding 是 one-hot 的另一个名字——它是按 token id 查表得到的可学习向量
- RNN 记不住远处是隐状态太小——是训练信号传不到