目录
引论:为什么这一讲是 CS224N 全程的核心
第一章:词向量评估(Lecture 2 回顾)
1.1 评估的两种范式:Intrinsic vs Extrinsic
1.2 词类比任务与"国王-皇后"几何
1.3 WordSim-353 与相关性评估
1.4 GloVe 在 NER 上的下游表现
第二章:从 NER 切入神经分类器
2.1 NER 任务与子模块化
2.2 窗口分类:上下文的最小单元
2.3 监督学习符号体系
2.4 从 Softmax 到神经分类器
2.5 二元 NER 网络的完整结构
第三章:激活函数大全
3.1 Sigmoid 与 Tanh:饱和家族
3.2 ReLU 与"死亡 ReLU"问题
3.3 现代激活:GELU、SiLU、Swish
3.4 门控线性单元:GLU 与 SwiGLU (LLaMA/Qwen)
3.5 为什么必须非线性?通用近似定理
第四章:损失函数与优化
4.1 交叉熵的信息论起源
4.2 等价的极大似然视角
4.3 随机梯度下降 (SGD)
4.4 词向量是不是参数?
第五章:矩阵微积分速成
5.1 标量函数的梯度
5.2 向量函数的雅可比矩阵
5.3 链式法则的矩阵化
5.4 三大基本雅可比
5.5 形状约定 (Shape Convention)
第六章:手工推导神经网络梯度
6.1 拆分式:把网络写成原子操作
6.2 链式法则三步走
6.3 复用:上游误差信号 δ
6.4 对矩阵参数 W 求导
6.5 转置之谜:为什么是外积?
6.6 对输入 x 求导:词向量学习的关键
第七章:反向传播与计算图
7.1 计算图:神经网络的中间表示
7.2 前向传播与缓存
7.3 单节点 backprop:上游 / 局部 / 下游
7.4 多输入节点:多个局部梯度
7.5 分支处的梯度求和
7.6 算子直觉:+ 分发 / max 路由 / × 切换
7.7 一次性完成整张图的反向
7.8 数值实例:手工算到底
第八章:自动微分与工业实现
8.1 反向模式 AutoDiff 的本质
8.2 PyTorch 是怎么算梯度的?
8.3 数值梯度检查 (gradcheck)
8.4 backprop 不是黑盒:你必须懂的坑
8.5 梯度爆炸与梯度消失预告
第九章:综合实战与扩展阅读
9.1 从零实现 NER 窗口分类器(PyTorch)
9.2 与 Assignment 2 的衔接
9.3 进一步阅读与论文
引论:为什么这一讲是 CS224N 全程的核心
在 CS224N 的整个学期中,Lecture 3 是分水岭。前两讲讨论的是"词向量"——一种数据驱动但相对静态的表示;从这一讲开始,你将接触到所有现代 NLP 系统(包括 GPT、Claude、Qwen、LLaMA)依赖的两个核心机制:
神经分类器 (neural classifier) :在表示与决策之间引入多层非线性变换 ,让模型自己学习"什么样的特征对任务有用"。
反向传播 (backpropagation) :用矩阵微积分自动计算梯度,让"自己学习"在数学上可行、在工程上高效。
为什么必须自己推导一遍 backprop?
PyTorch 早就把
loss.backward() 封装到一行了——你完全可以不懂这些原理就训练 BERT。但只要你做研究,就会撞到以下问题:
梯度爆炸 / 消失(RNN、深层 Transformer 的训练崩溃)
自定义算子(写 CUDA kernel、Triton kernel、新型注意力)
调试模型不收敛(80% 的 bug 在反向,而不是前向)
论文阅读(LoRA、FlashAttention、Mixture-of-Experts 的推导都建立在本讲的符号上)
当你能够把
∂L/∂W 写到纸上、亲手验证形状对应,你就掌握了与 Karpathy 在
"Yes you should understand backprop" 中所说"同样的 mental model"。
本讲的两条主线
本讲容易让初学者迷失:表面上 Diyi 在白板上推了几十张公式,但其实只在做两件事——
前向传播
x → h → s → J(θ)
"算 loss"
反向传播
∂J/∂θ via chain rule
"算 gradient"
计算图记录中间值
梯度沿计算图反向流
SGD: θ ← θ − α ∂J/∂θ
图 0.1:神经网络训练的双轨:前向算损失,反向算梯度。中间的"计算图"是连接两者的桥梁。
本教材将循序渐进地把这两条主线讲透。你会先做一个完整的 NER 二分类例子(章节 2–4),然后把同一个例子用三种粒度求导:单变量手算 → 向量化雅可比 → 通用计算图(章节 5–8)。最后我们用 PyTorch 把所有公式落地为代码(章节 9)。
第一章:词向量评估(Lecture 2 回顾)
进入神经网络之前,先回顾上一讲的 word2vec/GloVe。你可能问:词向量有什么"评估"的?给个余弦相似度不就好了吗?事实上,整个 NLP 评估方法论可以浓缩为两条线:
1.1 评估的两种范式:Intrinsic vs Extrinsic
维度 Intrinsic(内在) Extrinsic(外在)
定义 在子任务 或探针任务 上测 在真实下游任务 上测
例子 词类比、相似度相关性、语义聚类 NER 准确率、问答 EM、机器翻译 BLEU
计算成本 秒级,可在 notebook 跑 小时级,需要完整 pipeline
解释力 能定位单一组件 的好坏 反映系统整体 的好坏
风险 子任务好≠下游好(相关性问题) 难定位是哪个子模块拖后腿
研究生应该知道的潜规则
当你在论文中只汇报 intrinsic 指标(比如"我们的词向量在 WordSim-353 上 +2 分"),审稿人会立刻质疑:
"so what for downstream?" ——这就是为什么 ELMo / BERT / GPT 等里程碑论文都拼命堆砌下游榜单(GLUE, SuperGLUE, MMLU),因为只有 extrinsic 评估才说服得了人。
1.2 词类比任务与"国王-皇后"几何
词类比 (word analogy) 是 Mikolov 2013 提出的经典内在测评。给定一对"前提"a:b 与一个"提问" c:? ,要求模型给出 d 使得 a:b :: c:d 。例如:
man : woman :: king : ? ⟶ 期望 queen
解法基于向量加法:把 a→b 的方向加到 c 上,然后在词表中找最近邻:
x₁
x₂
man
woman
king
queen
"性别"方向
"性别"方向(平移)
图 1.1:king − man + woman ≈ queen。性别方向在嵌入空间中是一个近似平移向量。
这种几何并非总是可靠
2019 年的
Ethayarajh 等 指出 "king − man + woman = queen" 实际上对 SGNS 模型只有约 50% 的成功率,更多归功于
余弦距离的偏置 而非真正的几何加法。但这一现象作为
探针任务 仍然有价值——它让我们看到嵌入
能够 承载语义结构,而这是后续所有上下文表示(ELMo/BERT)的研究起点。
1.3 WordSim-353 与相关性评估
WordSim-353 是另一类内在评估:给定 353 个词对,每对由人工打 0–10 分的相似度,然后看模型的余弦相似度与人类打分的 Spearman/Pearson 相关系数。例如:
Word 1 Word 2 Human (mean) 余弦相似度示例
tiger cat 7.35 0.78
tiger tiger 10.00 1.00
book paper 7.46 0.72
stock phone 1.62 0.21
stock jaguar 0.92 0.06
用 Spearman 而不是 Pearson 是因为:人类打分是序号尺度("7.35" 与 "7.46" 的差异未必比 "1.62" 与 "1.31" 的差异更小),排序相关性 更鲁棒。
GloVe 论文里的关键表
Lecture 2 给出的对比表里,GloVe 42B 在 WS353/MC/RG/SCWS/RW 上几乎全面超过 SVD 与 word2vec:
GloVe 42B → WS353: 75.9 , MC: 83.6 , RG: 82.9
CBOW 100B → 68.4 / 79.6 / 75.4(数据更多但模型架构吃亏)
这说明
训练目标 (GloVe 的共现矩阵分解)有时比
数据规模 更重要——这是一个值得记住的反直觉结论。
1.4 GloVe 在 NER 上的下游表现
把视角切到 extrinsic 评估。在 CoNLL-2003 NER 任务上,几种词向量做特征接入相同分类器的结果:
Model Dev F1 Test F1 ACE MUC7
Discrete (sparse one-hot) 91.0 85.4 77.4 73.4
SVD 90.8 85.7 77.3 73.7
HPCA 92.6 88.7 81.7 80.7
CBOW 93.1 88.2 82.2 81.1
GloVe 93.2 88.3 82.9 82.2
两个观察:(1) 任何密集嵌入都比 one-hot 好,因为它在未见词 上有泛化能力;(2) GloVe 和 CBOW 差异微小——下游任务对嵌入的具体方法 不敏感,但对嵌入维度、训练语料大小 很敏感。
回顾到这里,我们已经能回答 Lecture 2 留下的最后一个问题——"信息存在但不是线性的怎么办?" 答案就是:用非线性 的神经网络代替线性投影。下面正式进入神经分类器。
第二章:从 NER 切入神经分类器
2.1 NER 任务与子模块化
命名实体识别 (Named Entity Recognition, NER) 是 NLP 最经典的序列标注 任务:给定一段文本,找出并分类其中的实体提及。例如:
Last night, Paris Hilton wowed in a sequin gown.
[Paris, Hilton] → PER (人名)
Samuel Quinn was arrested in the Hilton Hotel in Paris in April 1989 .
[Samuel, Quinn] → PER, [Hilton, Hotel, Paris] → LOC, [April, 1989] → DATE
注意:同一个单词 "Paris" 在不同上下文里既可能是 PER 也可能是 LOC——这正是 word2vec 这种静态嵌入 无法独立解决的问题,必须依靠上下文 (即"窗口")。
NER 的用处覆盖整个 NLP 栈:
问答 (QA) :90% 的事实型问题的答案都是命名实体。
关系抽取 (RE) :先识别实体,再判断它们的关系。
实体链接 (EL) :把识别到的"Paris"映射到知识库中的 Wikidata Q90(巴黎,法国首都)。
情感分析 :评论中是对哪个实体的情感?
2.2 窗口分类:上下文的最小单元
Lecture 3 把 NER 简化成一个二分类问题:"中心词是否为地点 (LOC)?" 。给定一句话,对每个位置 t 取一个固定大小的窗口(如左右各 2 个词),拼接成一个长向量:
the
museums
in
Paris
are
amazing
窗口 (window size = 2)
↑ 中心词
x_window = [ x_museums ; x_in ; x_Paris ; x_are ; x_amazing ] ∈ ℝ^(5d)
任务:y = 1 if Paris 是 LOC else 0
每个 x_w 是 d 维 GloVe 向量,拼接后整窗维度为 5d
图 2.1:窗口分类把"序列标注"转化为"逐词分类",是最简单的上下文建模。
实际系统通常用 multi-class softmax 同时输出 {PER, LOC, ORG, DATE, O} 五类,但二分类版本让我们能把数学讲清楚。
2.3 监督学习符号体系
用一套贯穿全讲的符号:
在统计学习 里,参数 θ 是要找的对象;在深度学习 里,θ 包含嵌入 本身——这一点稍后会反复出现。
2.4 从 Softmax 到神经分类器
先复习经典的 softmax 分类器:
这种模型给出的决策边界是线性 的——在原始特征空间画一条直线(高维下是一张超平面)把类别分开。如果数据像图 2.2(左)那样线性可分,softmax 就足够;但如果数据呈环形(图 2.2 右),无论怎么调 W 都无能为力。
(a) softmax 能搞定的线性可分
(b) 需要非线性边界
绿/红 = 两类样本
图 2.2:左边线性分类器够用;右边需要把数据"扭曲"到一个新的表示空间。神经网络的中间层就是干这件事。
神经网络分类器与 softmax 的差别 有两点:
同时学习 W 和表示 :词向量 $x = L e$(其中 $L$ 是嵌入矩阵,$e$ 是 one-hot)也被当做参数训练。
引入中间层 :在 softmax 之前先做若干个 $h = f(Wx + b)$ 的非线性变换,把数据搬到一个对线性 softmax 友好 的新空间。
关键洞察
深度网络
看似复杂 ,本质上还是
"在最后一层做线性分类" ——只不过在那之前用 L−1 层网络把数据重塑成线性可分的形状。这就是 Manning 在课件上写的"
typically, it is linear relative to the pre-final layer representation "。
2.5 二元 NER 网络的完整结构
我们用最小的两层网络完成 NER 二分类:
x ∈ ℝ⁵ᵈ
museums
in
Paris
are
amazing
h = f(Wx+b)
s
= uᵀ h
σ(s) → P(LOC)
图 2.3:两层网络的标准结构。x 在底部,经过 W、bias、非线性 f、点积 u、sigmoid σ 后输出概率。
这个网络有 3 组可学习参数:W(n×5d)、b(n)、u(n)。如果再把嵌入 L 也算上,整体 θ 包括 所有词向量 + 网络权重 ——这是 Lecture 3 反复强调的"在深度学习里 θ 包括数据表示本身"。
剩下要解决的问题只有一个:如何让这个网络的输出 $\hat p$ 接近真实标签 y? 下面的章节给出答案。
第三章:激活函数大全
中间层 $h = f(z)$ 中的 $f$ 是逐元素 的非线性函数。本节系统地介绍从 1980s 的 sigmoid 到 2024 的 SwiGLU 的演化,并解释为什么非线性是必需的 。
3.1 Sigmoid 与 Tanh:饱和家族
Sigmoid σ(z)
z
1
0
Tanh
1
-1
ReLU
GELU / SiLU
图 3.1:四类典型激活函数的形状对比。Sigmoid/Tanh 饱和,ReLU 分段线性,GELU/SiLU 是 ReLU 的平滑版本。
Sigmoid 与 Tanh 的两个关键缺陷 (这是它们在深网络里被淘汰的原因):
梯度饱和 :当 $|z|$ 大时,$\sigma'(z) \to 0$,反向传播的梯度乘到深层时几乎被压缩为零。
非零中心 (仅 sigmoid):输出永远 ≥ 0,导致下一层的权重在更新时只能同正或同负,训练效率低。Tanh 解决了这个问题(输出 [-1,1] 对称),但仍有饱和。
什么时候还该用它们?
输出层概率 :sigmoid 仍是二分类输出层的标准(产生 [0,1])。
门控机制 :LSTM 的输入门、遗忘门、输出门都用 sigmoid 控制 [0,1] 的"通过比例"。
注意力权重 :softmax 是 sigmoid 的多元推广。
所以这些"老"激活并没有消失,只是退到了特定的位置。
3.2 ReLU 与"死亡 ReLU"问题
ReLU 由 Nair & Hinton (2010) 推广,是深度学习革命的"幕后英雄"之一。它的好处显而易见:
计算便宜 :max 不需要 exp。
不饱和(正半轴) :在 $z > 0$ 时梯度恒为 1,缓解梯度消失。
稀疏激活 :约一半神经元输出为 0,给模型带来正则化效果。
但 ReLU 有个臭名昭著的问题——死亡 ReLU (Dying ReLU) :一旦某神经元落入 $z < 0$ 区域并且学习率较大,更新步会让它进一步远离零点,导致梯度永远为 0,神经元"死掉"。Leaky ReLU 与 PReLU 是直接的补丁:
3.3 现代激活:GELU、SiLU、Swish
2017 年起,Transformer 时代催生了一族平滑的 非线性,它们在大模型上表现更好:
这两个家族都满足:
在 $x \to +\infty$ 接近 $x$(线性);
在 $x \to -\infty$ 接近 $0$(关闭);
在 $0$ 附近平滑 (处处可导,无 ReLU 的角点)。
这种平滑特性让 Adam 等基于二阶估计的优化器(Adam/AdamW)更稳定,所以现代大模型(BERT、GPT-2、ViT)默认用 GELU。
3.4 门控线性单元:GLU 与 SwiGLU (LLaMA/Qwen)
Dauphin et al. (2017) 在语言建模上提出门控线性单元:
Shazeer 2020 的 GLU Variants Improve Transformer 把 sigmoid 换成 Swish:
SwiGLU 现在是 LLaMA 系列、Qwen、Mistral、DeepSeek 的标配。它有两个隐含的好处:
表达力更高 :门控机制是一种"内置的二阶交互",类似于 Mixture-of-Experts 的软选择。
不增加 FLOPs (按维度归一):把 FFN 隐层维度从 4d 调整为 8d/3,参数量与计算量与 ReLU FFN 相同。
实战经验
对绝大多数 NLP 研究任务:
训练 RNN/小模型 → 首选 ReLU (简单、快、稳)。
训练 Transformer encoder/decoder → 首选 GELU 。
训练 LLM-scale 模型 → SwiGLU (与现代论文对齐)。
用 sigmoid/tanh 当激活会被审稿人怀疑你是不是 2014 年穿越来的。
3.5 为什么必须非线性?通用近似定理
假设网络是纯线性的:$h^{(1)} = W_1 x$,$h^{(2)} = W_2 h^{(1)}$,…,最后输出 $y = W_L h^{(L-1)}$。把代换全部展开:
$$ y = W_L W_{L-1} \cdots W_1\, x \;=\; W\, x \quad \text{where} \quad W = W_L \cdots W_1 $$
任何无激活 的多层网络,等价于一个单层线性变换 。所谓的"深度"完全没有任何意义。
反之,通用近似定理 (Universal Approximation Theorem, Cybenko 1989; Hornik 1991) 保证:一个仅含一层 隐藏单元的前馈网络,只要激活函数是非常数有界连续,就能以任意精度近似任何紧致集上的连续函数。这是深度学习的表达力依据 。
线性 (欠拟合)
2 个非线性 (恰好)
过多非线性 (过拟合)
图 3.2:从欠拟合到过拟合:非线性的"用量"决定了函数族的容量。模型容量与正则化是另一个 trade-off(CS 229 / CS 230 的主题)。
深度 vs 宽度
通用近似定理只说"宽度足够大的一层网络"可以近似任意函数,但需要的宽度可能是
指数级 的。
Telgarsky 2016 等工作证明:深度网络可以用
多项式 参数表达浅网络需要指数参数才能表达的函数。这就是"为什么要深"的理论解释——而不只是"经验上效果更好"。
第四章:损失函数与优化
4.1 交叉熵的信息论起源
到此为止我们的目标是最大化正确类的概率 $p(y_i | x_i)$ 。但深度学习习惯最小化 损失。两者等价:
$$ \max_\theta \prod_{i=1}^{N} p(y_i \mid x_i; \theta) \;\;\iff\;\; \min_\theta \;-\sum_{i=1}^{N} \log p(y_i \mid x_i; \theta) $$
左边是极大似然 ,右边是负对数似然 (NLL) 。这种"对数+负号"在工程上有数值稳定性优势(避免溢出)。
用交叉熵 (cross entropy) 的视角来看,假设:
真实分布 $p$:one-hot,即 $p(c) = 1$ 当且仅当 $c = y_i$;
模型分布 $q$:softmax 输出。
那么单样本的交叉熵为:
$$ H(p, q) \;=\; -\sum_{c=1}^{C} p(c)\,\log q(c) $$
由于 $p$ 是 one-hot,求和只剩 $-\log q(y_i)$ —— 这就是 NLL!
展开交叉熵:
$$ H(p, q) = -\sum_{c=1}^{C} p(c)\,\log q(c) = -p(y_i)\,\log q(y_i) - \sum_{c \ne y_i} 0 \cdot \log q(c) = -\log q(y_i) $$
所以 cross-entropy loss 与 NLL 在 one-hot 目标下完全一致。$\square$
信息论直觉
$H(p, q)$ 衡量"用 q 的码本去编码来自 p 的样本,平均要花多少 bits"。当 $q = p$ 时 $H(p,q) = H(p)$ 是
熵 ,是理论下界。优化交叉熵=让你的模型分布 q 尽量接近真实分布 p。
4.2 等价的极大似然视角
把 N 个样本累计起来,得到训练目标:
对二分类 NER,可以写成 sigmoid 的形式:
$$ J(\theta) = -\sum_{i=1}^{N} \big[ y_i \log \sigma(s_i) + (1 - y_i)\log(1 - \sigma(s_i)) \big] $$
这就是logistic loss ,是二分类时 NLL 的简化形式。
4.3 随机梯度下降 (SGD)
有了损失函数 $J(\theta)$,我们要找最小化它的 θ。基本算法就是梯度下降 :
实际中我们用 SGD:每次只用一个 mini-batch 近似真实梯度。这在 NLP 中尤其重要——训练语料动辄 10 亿 token,全数据集梯度根本算不起。
SGD vs full-batch GD
full-batch:梯度精确,但单步太慢,且容易卡在 saddle point。
SGD:梯度是有噪声的估计,但每步便宜;噪声反而帮助逃出 saddle。
实际选择:mini-batch SGD(batch size = 32~4096)+ Adam/AdamW 优化器。
4.4 词向量是不是参数?
这是 Lecture 3 的一个隐藏问题。答案:看你怎么用 。三种主流策略:
策略 描述 适用场景
冻结 (frozen) 用 GloVe 加载嵌入,不更新 训练数据少(< 10k 样本),怕过拟合到训练词
微调 (fine-tune) 嵌入和网络一起更新 训练数据多(> 100k 样本),追求最佳性能
从零训练 (from scratch) 嵌入用随机初始化,从头训 数据量极大(如 LLM 预训练),或词表与预训练完全不重合
微调的危险
当训练集很小、词表很大时,微调会让
训练集中出现过的词 朝任务目标移动,而
未出现过的词 停在原始位置——这导致测试时两者距离变远,模型反而泛化变差。Manning 在 Lecture 2 用一张图演示了这个现象。
核心信息:在深度学习里,$\theta$ 不只是 W 和 b,还可以包含输入表示 $x = L e$ 。这是接下来推导梯度时要特别注意的。
第五章:矩阵微积分速成
要计算 $\nabla_\theta J(\theta)$,必须把"一元微积分"扩展到向量和矩阵。这一章给出全部需要的工具,并解释 Lecture 3 中令很多人困惑的"形状约定 vs 雅可比形式"之争。
5.1 标量函数的梯度
单输入单输出
$f(x) = x^3 \Rightarrow \dfrac{df}{dx} = 3x^2$。直觉:"输入变 ε,输出变多少?"
在 $x=1$ 处,$f'(1) = 3$,意味着 $f(1.01) \approx 1 + 3 \cdot 0.01 = 1.03$(实际 $1.01^3 \approx 1.0303$)。
多输入单输出
$f(\mathbf{x}) = f(x_1, x_2, \dots, x_n) \in \mathbb{R}$ 的梯度:
$$ \frac{\partial f}{\partial \mathbf{x}} = \left[ \frac{\partial f}{\partial x_1},\;\frac{\partial f}{\partial x_2},\;\dots,\;\frac{\partial f}{\partial x_n} \right] $$
这是一个 $1 \times n$ 的行向量 (按雅可比约定)。
5.2 向量函数的雅可比矩阵
把"多输入"再扩展到"多输出",就是雅可比 (Jacobian):
关键约定 :雅可比的形状是 m × n (输出维度 × 输入维度)。换句话说:
5.3 链式法则的矩阵化
单变量复合函数的链式法则:
$$ z = 3y,\; y = x^2 \;\Rightarrow\; \frac{dz}{dx} = \frac{dz}{dy}\frac{dy}{dx} = 3 \cdot 2x = 6x $$
"导数相乘"。
多变量复合函数:
$$ \mathbf{h} = f(\mathbf{z}),\; \mathbf{z} = W\mathbf{x} + b \;\Rightarrow\; \frac{\partial \mathbf{h}}{\partial \mathbf{x}} = \frac{\partial \mathbf{h}}{\partial \mathbf{z}} \cdot \frac{\partial \mathbf{z}}{\partial \mathbf{x}} $$
"雅可比相乘"。这是 backprop 的唯一 数学武器。
链式法则为什么这么"配合"?
形状对应得天衣无缝:若 $\mathbf{h} \in \mathbb{R}^a, \mathbf{z} \in \mathbb{R}^b, \mathbf{x} \in \mathbb{R}^c$,则:
$\partial \mathbf{h}/\partial \mathbf{z}$ 是 a × b
$\partial \mathbf{z}/\partial \mathbf{x}$ 是 b × c
它们的乘积是 a × c ,正是 $\partial \mathbf{h}/\partial \mathbf{x}$ 该有的形状 ✓
这就是为什么 Manning 反复说"multivariable calculus is just like single-variable calculus if you use matrices "。
5.4 三大基本雅可比
NER 网络只用到 3 类操作,对应 3 个基本雅可比。把它们背下来 ——以后做 Transformer、RNN、注意力的推导都建立在这之上。
(A) 逐元素激活函数
$$ \mathbf{h} = f(\mathbf{z}),\quad h_i = f(z_i) $$
$$ \frac{\partial \mathbf{h}}{\partial \mathbf{z}} = \begin{pmatrix} f'(z_1) & & 0 \\ & \ddots & \\ 0 & & f'(z_n) \end{pmatrix} = \text{diag}(f'(\mathbf{z})) $$
$$ \left(\frac{\partial \mathbf{h}}{\partial \mathbf{z}}\right)_{ij} = \frac{\partial h_i}{\partial z_j} = \frac{\partial}{\partial z_j} f(z_i) = \begin{cases} f'(z_i) & i = j \\ 0 & i \ne j \end{cases} $$
当 $i \ne j$ 时,$h_i$ 只与 $z_i$ 有关,与 $z_j$ 无关,所以偏导为 0。结果是对角矩阵。 $\square$
(B) 线性变换
$$ \mathbf{z} = W\mathbf{x} + \mathbf{b} $$
$$ \boxed{ \frac{\partial \mathbf{z}}{\partial \mathbf{x}} = W \qquad \frac{\partial \mathbf{z}}{\partial \mathbf{b}} = I } $$
$z_i = \sum_k W_{ik} x_k + b_i$,所以 $\partial z_i / \partial x_j = W_{ij}$,正是 $W$ 的元素。
对偏置:$\partial z_i / \partial b_j = \delta_{ij}$,即单位矩阵。 $\square$
(C) 内积(标量输出)
$$ s = \mathbf{u}^\top \mathbf{h} = \sum_i u_i h_i $$
$$ \frac{\partial s}{\partial \mathbf{u}} = \mathbf{h}^\top \qquad \frac{\partial s}{\partial \mathbf{h}} = \mathbf{u}^\top $$
注意输出形状:$\partial s / \partial \mathbf{u}$ 是 1 × n (标量对向量),所以是 $\mathbf{h}^\top$ 而不是 $\mathbf{h}$。
求以下雅可比,并标注形状:
$\mathbf{y} = A\mathbf{x}$,求 $\partial \mathbf{y}/\partial A$。
$f = \|\mathbf{x}\|_2^2 = \mathbf{x}^\top \mathbf{x}$,求 $\partial f/\partial \mathbf{x}$。
$\mathbf{y} = \text{softmax}(\mathbf{z})$,求 $\partial \mathbf{y}/\partial \mathbf{z}$。
提示:第 1 题答案是一个 3 维张量;第 3 题答案是 $\text{diag}(\mathbf{y}) - \mathbf{y}\mathbf{y}^\top$。
5.5 形状约定 (Shape Convention)
讲到这里,要面对 Lecture 3 最让人头疼的地方:对矩阵参数 $W$ 求导时,结果应该长什么样?
纯数学上:若标量 $s$ 关于 $W \in \mathbb{R}^{n \times m}$ 求导,雅可比应是 1 × nm 的"行向量"(因为输出是 1 维,输入有 $nm$ 个元素)。但 SGD 更新 $W^{\text{new}} = W^{\text{old}} - \alpha \nabla_W J$ 要求 $\nabla_W J$ 与 $W$ 形状相同 ,即 n × m 。
两个约定的冲突
约定 形状 优势 劣势
Jacobian form 1 × nm(拉直) 链式法则形状对应 难以塞回 SGD 更新
Shape convention n × m(同 W) SGD 可直接用 需手动对齐转置
CS224N 与几乎所有深度学习实现(PyTorch、TensorFlow)选择 shape convention 。规则:
梯度的 shape 与参数本身的 shape 一致。
所以:
$\partial J/\partial W$ → n × m
$\partial J/\partial \mathbf{b}$ → n × 1 (列向量)
$\partial J/\partial \mathbf{u}$ → n × 1
$\partial J/\partial \mathbf{x}$ → 5d × 1 (拼接的窗口)
工作流推荐:"用雅可比形式 compute,用 shape convention format "——先按数学正确推导,最后转置/重排让形状对齐参数。
第六章:手工推导神经网络梯度
本章把第五章的工具应用到第二章的 NER 网络。整个过程分 4 步:
把网络拆成原子操作 (章节 6.1);
对要求导的参数应用链式法则 (6.2);
把每一段写成第五章的已知雅可比 (6.2 后半 + 6.3);
用形状约定 校验并整理(6.4–6.6)。
6.1 拆分式:把网络写成原子操作
原网络:
$$ s = u^\top h, \quad h = f(Wx + b) $$
引入中间变量 $z$,拆成 4 个原子等式(每一行都对应一个第五章的"已知雅可比"):
这种拆分让"复合函数"变成一串单步函数——而每一单步都正好 对应第五章的基本雅可比。
6.2 链式法则三步走
我们目标:求 $\partial s / \partial b$(对偏置求导)。应用链式法则:
$$ \frac{\partial s}{\partial b} = \frac{\partial s}{\partial h} \cdot \frac{\partial h}{\partial z} \cdot \frac{\partial z}{\partial b} $$
每段单独写出来:
step 1
$s = u^\top h$ → $\dfrac{\partial s}{\partial h} = u^\top$ 1 × n
step 2
$h = f(z)$ → $\dfrac{\partial h}{\partial z} = \text{diag}(f'(z))$ n × n
step 3
$z = Wx + b$ → $\dfrac{\partial z}{\partial b} = I$ n × n
把三段乘起来:
$$ \frac{\partial s}{\partial b} = u^\top \cdot \text{diag}(f'(z)) \cdot I = u^\top \odot f'(z)^\top $$
(这里用了 diag 矩阵乘向量等价于 Hadamard 积 的恒等式。)
结果是 1 × n 。最后按 shape convention 转置成 n × 1 :
$$ \boxed{ \nabla_b s = u \odot f'(z) \in \mathbb{R}^{n} } $$
6.3 复用:上游误差信号 δ
现在考虑同时对 $W$ 求导:
$$ \frac{\partial s}{\partial W} = \frac{\partial s}{\partial h} \cdot \frac{\partial h}{\partial z} \cdot \frac{\partial z}{\partial W} $$
$$ \frac{\partial s}{\partial b} = \frac{\partial s}{\partial h} \cdot \frac{\partial h}{\partial z} \cdot \frac{\partial z}{\partial b} $$
前两段一模一样! 这正是"复用计算"的来源。我们给前两段一个名字 δ(delta,"误差信号"):
有了 δ,对任意上游参数的梯度都可以写成"δ 乘以本层的局部雅可比 "——这是 backprop 的核心套路。
6.4 对矩阵参数 W 求导
用 δ 的写法:
$$ \frac{\partial s}{\partial W} = \delta \cdot \frac{\partial z}{\partial W} $$
$\partial z / \partial W$ 看上去棘手(z 是向量,W 是矩阵),但单元素地看就简单了。考虑某个具体的 $W_{ij}$:
s
u₂
h₁
h₂
f(z₁)=
=f(z₂)
W₂₃
b₂
x₁
x₂
x₃
+1
图 6.1:W₂₃ 这条连接只贡献给 z₂(红色路径),与 z₁ 无关。因此 ∂zᵢ/∂Wⱼₖ 在 i ≠ j 时为零。
$W_{ij}$ 只出现在 $z_i = \sum_k W_{ik} x_k + b_i$ 这一行。因此:
$$ \frac{\partial z_i}{\partial W_{ij}} = \frac{\partial}{\partial W_{ij}}\sum_k W_{ik} x_k = x_j $$
其他 $z_l$($l \ne i$)都与 $W_{ij}$ 无关,偏导为 0。
把它整合回链式法则:
$$ \frac{\partial s}{\partial W_{ij}} = \sum_l \delta_l \cdot \frac{\partial z_l}{\partial W_{ij}} = \delta_i \cdot x_j $$
这正好是外积 (outer product) $\delta^\top x^\top$(按雅可比形式)或 $\delta x^\top$(按 shape convention)的元素 $(i,j)$。所以:
6.5 转置之谜:为什么是外积?
很多初学者在这里迷失:"为什么不是 $W \cdot x$ 或别的形式?"
用一个具体的小例子($n=3, d_x=4$):
$$ \delta = \begin{bmatrix} \delta_1 \\ \delta_2 \\ \delta_3 \end{bmatrix},\quad x^\top = [x_1, x_2, x_3, x_4] $$
$$ \delta x^\top = \begin{bmatrix} \delta_1 x_1 & \delta_1 x_2 & \delta_1 x_3 & \delta_1 x_4 \\ \delta_2 x_1 & \delta_2 x_2 & \delta_2 x_3 & \delta_2 x_4 \\ \delta_3 x_1 & \delta_3 x_2 & \delta_3 x_3 & \delta_3 x_4 \end{bmatrix} \in \mathbb{R}^{3 \times 4} $$
形状对齐:$\delta$ 与 $z$ 同形(n),$x^\top$ 与 $z = Wx$ 中"哪一列乘 x"对应(即每行用所有 x 加权)。
直觉理解
$W_{ij}$ 是
第 i 个隐藏单元用第 j 个输入特征的权重 。它的梯度 = (上游对第 i 个隐藏单元的灵敏度 $\delta_i$) × (该位置实际输入值 $x_j$)。
"
每个输入到每个输出 — 你想得到外积 " —— Manning 原话。
6.6 对输入 x 求导:词向量学习的关键
如果嵌入也是可学习参数(fine-tune 模式),那需要 $\partial s / \partial x$:
$$ \frac{\partial s}{\partial x} = \delta \cdot \frac{\partial z}{\partial x} = \delta \cdot W $$
按 shape convention:
$$ \boxed{\; \nabla_x s = W^\top \delta \in \mathbb{R}^{d_x \times 1} \;} $$
这里出现的 $W^\top$ 是 backprop 的标志性现象:正向传播用 W,反向传播用 $W^\top$ 。对于这个 NER 例子,$x$ 是 5 个词向量的拼接,所以 $\nabla_x s$ 也要切回 5 段,分别加到对应词的嵌入上。
梯度形状速查
参数 形状 梯度公式 (shape conv.)
$W \in \mathbb{R}^{n \times d_x}$ n × d_x $\delta\, x^\top$
$b \in \mathbb{R}^{n}$ n $\delta$
$u \in \mathbb{R}^{n}$ n $h$
$x \in \mathbb{R}^{d_x}$ d_x $W^\top \delta$
其中 $\delta = u \odot f'(z) \in \mathbb{R}^{n}$ 是上游误差信号(按 shape convention 列向量)。
第七章:反向传播与计算图
上一章我们靠手算导出了 4 个梯度。但深网络有几十层、几百个变量,逐个手算不现实。反向传播 (backpropagation) 把"手算"升级成沿计算图自动执行的算法 。
7.1 计算图:神经网络的中间表示
把网络改写为有向无环图 (DAG) :
源节点 (source node) :输入变量(x, W, b, u)
内部节点 (interior node) :操作(matmul, add, activation, dot)
边 (edge) :传递操作结果
·
matmul
+
add
f
tanh
·
dot
x
W
b
u
Wx
z
h
s
图 7.1:NER 网络的计算图。每个圆是一个算子;边携带中间结果。这就是 PyTorch 在 backward() 内部维护的数据结构。
7.2 前向传播与缓存
沿 DAG 按拓扑序 计算每个节点的输出值,并缓存 到内存中——反向时要用到这些激活值。
# 伪代码
class Node:
def forward(inputs):
out = compute(inputs)
self.cache = inputs # 必须保留!
return out
激活检查点 (gradient checkpointing)
缓存所有激活值会占大量显存。Chen et al. 2016 提出
梯度检查点 :只缓存少量节点,反向时重新前向算其余激活——以计算量换显存。这是训练大模型(如 70B LLaMA)的标配技巧。
7.3 单节点 backprop:上游 / 局部 / 下游
一个节点 $h = f(z)$ 的反向传播只做一件事 :
f
∂h/∂z (local)
z
h
∂s/∂z (下游)
∂s/∂h (上游)
[下游] = [上游] × [局部]
图 7.2:单节点 backprop 的"上游 × 局部 = 下游"心法。这是反向传播的"原子操作"。
形式化地写:
$$ \underbrace{\frac{\partial s}{\partial z}}_{\text{downstream}} = \underbrace{\frac{\partial s}{\partial h}}_{\text{upstream}} \cdot \underbrace{\frac{\partial h}{\partial z}}_{\text{local}} $$
这就是链式法则的本质 ——节点不需要知道整张图,只需要:
从输出方向接到上游梯度 $\partial s/\partial h$;
用自己缓存的输入 计算局部雅可比 $\partial h/\partial z$;
把两者相乘得到下游梯度 $\partial s/\partial z$,传给前一个节点。
7.4 多输入节点:多个局部梯度
如果一个节点有多个输入(如 matmul 节点同时接收 $W$ 和 $x$),那么有多个局部梯度 ,每个输入收到一份下游梯度:
×
∂z/∂W, ∂z/∂x
W
∂s/∂W = ∂s/∂z · ∂z/∂W
x
∂s/∂x = ∂s/∂z · ∂z/∂x
z
∂s/∂z (上游)
图 7.3:z = Wx 这个节点有两个输入,反向时各自得到一份下游梯度。
7.5 分支处的梯度求和
反过来,如果一个变量被多个 下游节点使用("分叉/扇出"),它的总梯度是所有路径的梯度之和 :
例:
$$ a = x + y,\quad b = \max(y, z),\quad f = ab $$
$y$ 同时被 $a$ 和 $b$ 用到,所以:
$$ \frac{\partial f}{\partial y} = \frac{\partial f}{\partial a}\frac{\partial a}{\partial y} + \frac{\partial f}{\partial b}\frac{\partial b}{\partial y} $$
为什么是求和?
直觉上:如果 $y$ 微小扰动 $\Delta y$,那么"通过 a 走的影响" 与 "通过 b 走的影响"是
独立叠加 的——这正是全微分定理在多变量链式法则中的体现。
7.6 算子直觉:+ 分发 / max 路由 / × 切换
Lecture 3 后半给出了几个非常直观的"算子身份",背下来对调试 backprop 很有帮助:
算子 反向行为 原因
+ (加法)分发 (distribute) :把上游梯度原样传给所有输入$\partial(a+b)/\partial a = 1$, $\partial(a+b)/\partial b = 1$
max 路由 (route) :把上游梯度全部给"获胜方",其余为 0$\partial \max(a,b)/\partial a = 1$ 当 $a > b$ 时
× (乘法)切换 (switch) :上游梯度乘以"另一个输入"$\partial(ab)/\partial a = b$, $\partial(ab)/\partial b = a$
copy/branch 求和 (sum) :所有下游梯度相加见 7.5
7.7 一次性完成整张图的反向
正确的 backprop 不应该对每个参数独立跑一遍 (那样会重复计算 δ),而是一次性 反向遍历整张图:
关键性质:反向的时间复杂度与前向的相同 。这是 backprop 的伟大之处——它把"指数级朴素方法"压缩到了"线性"。
对计算图中 $|E|$ 条边,前向恰好遍历每条边一次;反向也只遍历每条边一次(用本地雅可比一次性算所有梯度)。所以 $T_{\text{back}} = O(T_{\text{forward}})$。$\square$
7.8 数值实例:手工算到底
用 Lecture 3 经典例子:
$$ f(x,y,z) = (x+y) \cdot \max(y, z),\quad x=1, y=2, z=0 $$
前向 :
$a = x + y = 1 + 2 = 3$
$b = \max(y, z) = \max(2, 0) = 2$
$f = a \cdot b = 3 \cdot 2 = 6$
反向 (输出 $f$ 的梯度初始化为 1):
+
max
×
x
1
grad=2
y
2
grad=2+3=5
z
0
grad=0
a=3
b=2
grad=2
grad=3
f=6
grad=1
图 7.4:完整数值反向。每个节点旁同时标注前向值(黑)和反向梯度(蓝)。注意 y 收到了两条路径的贡献并求和。
逐步推导 :
×
$f = ab \Rightarrow \dfrac{\partial f}{\partial a} = b = 2,\;\dfrac{\partial f}{\partial b} = a = 3$("切换")。
+
$a = x + y$:把上游梯度 2 分发给 x 和 y。所以 $\dfrac{\partial f}{\partial x} = 2,\;\dfrac{\partial f}{\partial y}\bigg|_{\text{via}\,a} = 2$。
max
$b = \max(y, z)$,由于 $y > z$,胜者是 y:上游梯度 3 全给 y,z 得 0。所以 $\dfrac{\partial f}{\partial y}\bigg|_{\text{via}\,b} = 3,\;\dfrac{\partial f}{\partial z} = 0$。
分支求和
$y$ 被两条路径用到,总梯度 = $2 + 3 = 5$。
验证 :直接求偏导 $\partial f/\partial y$:
$f = (x+y)\max(y, z) = (x+y) \cdot y$(因为 $y > z$),
所以 $\partial f/\partial y = \max(y,z) + (x+y) = 2 + 3 = 5$ ✓。算法和手算结果一致。
继续这个例子:把 $z$ 改为 3(其他不变),重做一遍 backprop。预期 $\partial f/\partial y$ 会变成多少?
提示:现在 max 的胜者切换为 z;分支求和时只剩一条路径。
第八章:自动微分与工业实现
8.1 反向模式 AutoDiff 的本质
第七章的算法用工程术语叫反向模式自动微分 (reverse-mode automatic differentiation) 。它有两个孪生兄弟:
模式 遍历方向 代价 适合
Forward-mode AD 输入 → 输出 O(输入数) × forward 少输入、多输出
Reverse-mode AD 输出 → 输入 O(输出数) × forward 多输入、单输出(神经网络!)
Symbolic differentiation 符号级展开 可能指数爆炸 简单函数、解析解
Numerical (finite diff.) 差分 O(参数数) × forward 仅用于校验
神经网络损失 $J(\theta)$ 的输入维度(θ)有亿级,但输出是 1 个标量——反向模式恰好把"O(输入)"换成"O(输出)=O(1)",所以"反向 "才是深度学习的正确姿势。
8.2 PyTorch 是怎么算梯度的?
PyTorch 在每次前向时动态 构建计算图(dynamic graph / define-by-run),TensorFlow 1.x 用静态图(define-then-run)。两者数学等价。简化伪代码:
class ComputationalGraph:
def forward(inputs):
# 1. 沿拓扑序计算
for gate in self.graph.nodes_topologically_sorted():
gate.forward() # 缓存 inputs
return loss
def backward():
# 2. 反拓扑序,每个节点用链式法则
for gate in reversed(self.graph.nodes_topologically_sorted()):
gate.backward() # 应用本地雅可比
return inputs_gradients
每种节点(gate)只需要实现两个方法:
forward(inputs):算输出并缓存输入;
backward(grad_output):用 缓存的输入 和 本地雅可比公式 算梯度。
这是为什么 PyTorch 自定义算子要继承 torch.autograd.Function 并实现 forward 和 backward 两个静态方法。
import torch
class MyReLU(torch.autograd.Function):
@staticmethod
def forward(ctx, z):
ctx.save_for_backward(z) # 缓存
return z.clamp(min=0)
@staticmethod
def backward(ctx, grad_h):
(z,) = ctx.saved_tensors
local = (z > 0).float() # 局部雅可比 (对角)
return grad_h * local # 上游 × 局部 = 下游
这正是 Lecture 3 反复强调的
"
Each node type needs to know how to compute its output and how to compute the gradient wrt its inputs given the gradient wrt its output. " 你的自定义算子 = 实现 forward + 实现 [下游 = 上游 × 局部]。
8.3 数值梯度检查 (gradcheck)
你写了一个自定义 backward,怎么确认它正确?用有限差分 近似真实梯度:
对每个参数 $\theta_i$ 单独算一次数值梯度,与 backward 输出对比。如果两者相对误差 < 1e-6,basically 一致。
def gradcheck(f, params, eps=1e-4):
analytic = backward_grad(f, params)
for i in range(len(params)):
p = params.copy()
p[i] += eps; f_plus = f(p)
p[i] -= 2*eps; f_minus = f(p)
numeric = (f_plus - f_minus) / (2*eps)
assert abs(analytic[i] - numeric) / max(abs(numeric), 1e-8) < 1e-6
PyTorch 内置了 torch.autograd.gradcheck,做研究时强烈推荐写新算子时用一下。
为什么不直接用数值梯度训练?
对 GPT-3 这样的 175B 参数模型:
每个参数需要 2 次前向 → 总开销 = 350B × forward;
backprop 只需 1 次反向 = 1 × forward 的代价。
差距 11 个数量级。数值方法只配做
校验 。
8.4 backprop 不是黑盒:你必须懂的坑
Karpathy 写过一篇必读:"Yes you should understand backprop" 。他列举了几个仅靠 loss.backward() 无法发现的 bug:
Sigmoid 饱和 :当 $z$ 很大时 $\sigma'(z) \approx 0$,梯度直接消失。如果你的输出层 sigmoid 接 cross-entropy,PyTorch 的 BCEWithLogitsLoss 用 log-sum-exp 把这俩合二为一以保数值稳定——单独用 sigmoid + log 容易爆。
Dying ReLU :上文已述。
不可导的运算 :argmax、round、discrete sampling 处梯度为 0,要么用 Gumbel-Softmax,要么用 REINFORCE。
梯度爆炸 :RNN 沿时间反向乘很多次 W,如果 $\|W\| > 1$ 就爆。修复:gradient clipping。
错误的求和维度 :对 batch 维度求和 而非平均 ,会让 effective learning rate 与 batch size 耦合,难以迁移。
8.5 梯度爆炸与梯度消失预告
当网络变深,链式法则会把 $L$ 个雅可比连乘起来:
$$ \frac{\partial s}{\partial \theta^{(1)}} = \frac{\partial s}{\partial h^{(L)}} \cdot \frac{\partial h^{(L)}}{\partial h^{(L-1)}} \cdots \frac{\partial h^{(2)}}{\partial h^{(1)}} \cdot \frac{\partial h^{(1)}}{\partial \theta^{(1)}} $$
每个雅可比的奇异值若集中在 1 附近,梯度能稳定传到底;若 < 1,连乘后趋近 0(梯度消失 ,如深 sigmoid RNN);若 > 1,连乘后发散(梯度爆炸 ,如未 clip 的 LSTM)。
解决方案的演化(CS224N 后续讲会详讲):
初始化 :Xavier (Glorot)、He → 让前向激活方差保持稳定;
架构 :ResNet 的残差连接 → 加号路径直接传梯度;LSTM 的 cell-state → 加性更新;
归一化 :BatchNorm/LayerNorm → 控制每层激活分布;
优化器 :Adam/AdamW → 用二阶估计动态调整每个参数的学习率;
梯度裁剪 :$g \leftarrow g \cdot \min(1, \theta_{\max}/\|g\|)$。
第九章:综合实战与扩展阅读
9.1 从零实现 NER 窗口分类器(PyTorch)
把全部理论落地为代码。下面这段 100 行内能跑的 PyTorch 实现,对应 Assignment 2 风格。
import torch, torch.nn as nn, torch.optim as optim
class NERWindowClassifier(nn.Module):
def __init__(self, vocab_size, embed_dim=50, window_size=2, hidden=100):
super().__init__()
self.window = window_size
self.embed = nn.Embedding(vocab_size, embed_dim)
# x ∈ R^((2*window+1)*embed_dim) → h ∈ R^hidden → score ∈ R
self.W = nn.Linear((2*window_size+1)*embed_dim, hidden)
self.u = nn.Linear(hidden, 1, bias=False)
# 激活:现代选 GELU
self.f = nn.GELU()
def forward(self, token_ids): # token_ids: [batch, 2k+1]
emb = self.embed(token_ids) # [B, 2k+1, d]
flat = emb.view(emb.size(0), -1) # [B, (2k+1)d]
z = self.W(flat) # [B, hidden]
h = self.f(z) # [B, hidden]
s = self.u(h).squeeze(-1) # [B]
return s # logits (未 sigmoid)
# 训练循环
model = NERWindowClassifier(vocab_size=10000)
optimizer = optim.AdamW(model.parameters(), lr=1e-3)
criterion = nn.BCEWithLogitsLoss() # 内置数值稳定的 sigmoid+CE
for epoch in range(10):
for token_ids, labels in train_loader:
logits = model(token_ids)
loss = criterion(logits, labels.float())
optimizer.zero_grad()
loss.backward() # ← 整章第七章
optimizer.step() # ← SGD/Adam 更新
代码与教材的对应
self.embed ↔ 嵌入矩阵 L(章节 2.4)
self.W ↔ W、b(章节 2.5)
self.u ↔ 输出向量 u
self.f = nn.GELU() ↔ 现代激活(章节 3.3)
BCEWithLogitsLoss ↔ 数值稳定的交叉熵(章节 4.1)
loss.backward() ↔ 整个第七、八章
optimizer.step() ↔ 章节 4.3 的 SGD 更新
9.2 与 Assignment 2 的衔接
CS224N Winter 2026 的 Assignment 2 要求你手算梯度(不能调用 backward())来实现一个依存句法分析器 (dependency parser)。需要的全部数学武器就是本讲内容:
窗口拼接 → 章节 2.2
对 W、b、u、x(嵌入)求偏导 → 第六章公式
shape convention → 5.5
验证:用 gradcheck 对照数值梯度 → 8.3
建议工作流:
在草稿纸上把网络写成原子形式(6.1)。
对每个参数应用链式法则,标注每段的 shape。
把"前两段"抽象成 δ 复用(6.3)。
翻译成 NumPy 代码。
跑 gradcheck。误差 > 1e-6 → 回去检查转置 / 求和维度 / 形状对齐。
9.3 进一步阅读与论文
Rumelhart, Hinton, Williams (1986) : Learning representations by back-propagating errors. Nature. 必读·历史 — backprop 的奠基论文。
Karpathy (2016) : Yes you should understand backprop. Medium 博客。必读·实战
Baydin et al. (2017) : Automatic Differentiation in Machine Learning: a Survey. JMLR. 系统综述
Goodfellow, Bengio, Courville (2016) : Deep Learning , Chapter 6.5 (Back-Propagation and Other Differentiation Algorithms). deeplearningbook.org 教材级
Shazeer (2020) : GLU Variants Improve Transformer. arXiv:2002.05202. 现代激活
Hendrycks & Gimpel (2016) : Gaussian Error Linear Units (GELUs). arXiv:1606.08415. 现代激活
Chen et al. (2016) : Training Deep Nets with Sublinear Memory Cost. arXiv:1604.06174. 工程 — gradient checkpointing。
CS224N 课程笔记 : Matrix Calculus Notes 同期补充
Math 51 Textbook (Stanford) : 在线版 数学基础
Universal Approximation : Cybenko 1989, Hornik 1991. 理论基础
Telgarsky (2016) : Benefits of depth in neural networks. COLT. 深度的理论
本讲总结
三句话总览
神经网络 = 多个"线性 + 非线性"层堆叠 。最后一层之前的层做"特征学习",最后一层做线性分类(softmax/sigmoid)。
反向传播 = 沿计算图按反拓扑序应用链式法则 。核心心法:[下游] = [上游] × [局部] 。
形状约定让数学回到工程 。Jacobian 形式便于推导;shape convention 便于 SGD 实现。两者用转置桥接。
掌握这三点,你就拥有了阅读现代论文(注意力机制、LoRA、MoE、FlashAttention)的全部数学基础。接下来 Lecture 4 我们会把网络从前馈扩展到循环神经网络 (RNN) ,并在 Lecture 5 进入 Transformer ——届时 backprop 会沿时间方向应用 (BPTT),会引出新的挑战。
参考与致谢
本教材改编自 Stanford CS224N: NLP with Deep Learning, Winter 2026, Lecture 3 by Diyi Yang(80 页讲义),由 Claude Opus 4.7 (1M context) 整理扩写。所有图示采用 SVG 与 KaTeX 重绘,便于在浏览器中阅读与打印。引用本教材请按以下方式:
@misc{cs224n-2026-lecture03-textbook,
title = {Neural Network Foundations and Backpropagation: A Graduate-Level Textbook},
author = {Adapted from D. Yang, CS224N Winter 2026},
year = {2026},
note = {Companion material to Stanford CS224N Lecture 3}
}
反馈与勘误:欢迎在课程仓库提 Issue。