跳转至

03. 反向传播与神经网络基础

官方 PPT 来源第 3 讲官方 PPT

本讲对应 CS224n 第 3 讲 Neural Network Foundations。课堂目标非常明确:理解神经网络怎样通过反向传播训练,尤其是把复杂网络拆成计算图后,如何用链式法则高效得到所有参数的梯度。


本讲学习目标

学完这一讲,你应该能从零回答下面这些问题:

  1. 第 2 讲的词向量为什么需要放进下游任务里评估?
  2. NER 是什么,window classification 怎样把上下文变成输入向量?
  3. 普通线性 softmax classifier 和 neural classifier 的区别在哪里?
  4. 为什么神经网络必须有非线性函数?
  5. cross entropy loss 为什么等价于最大化正确类别概率?
  6. SGD 更新式里的每一项分别是什么意思?
  7. gradient 和 Jacobian 的区别是什么?
  8. 多变量 chain rule 为什么可以写成 Jacobian 相乘?
  9. \(s = u^\top h\)\(h=f(z)\)\(z=Wx+b\) 这种小网络,怎样手算梯度?
  10. 为什么对矩阵参数求导时要特别关心 shape?
  11. computation graph 怎样把神经网络拆成局部操作?
  12. backpropagation 怎样复用计算,避免对每个参数重复求导?
  13. automatic differentiation 和 manual gradient checking 各自解决什么问题?

PPT 脉络

部分 PPT 内容 本讲义对应章节
词向量评估回顾 intrinsic/extrinsic evaluation、analogy、similarity、NER 作为 extrinsic task 1
NER 与分类器 named entity recognition、window classification、supervised notation 2-4
神经分类器 embedding layer、hidden layer、non-linearities、cross entropy、SGD 5-9
矩阵求导 gradient、Jacobian、chain rule、activation Jacobian、shape convention 10-15
手算网络梯度 \(s=u^\top h\)\(h=f(z)\)\(z=Wx+b\)、upstream gradient、outer product 16-19
反向传播 computation graph、single node backprop、branches、node intuitions、efficiency 20-25
自动微分与检查 automatic differentiation、backprop implementations、numeric gradient checking 26-28

1. 这讲承接第 2 讲什么?

第 2 讲的核心是:把词从离散符号变成 dense vector。第 3 讲马上追问:

词向量学出来以后,怎样用于真实 NLP 任务?

PPT 先回顾词向量评估,因为“词向量看起来合理”不等于“能提升任务效果”。

1.1 Intrinsic evaluation

Intrinsic evaluation 是在某个中间任务上直接评估词向量本身。

PPT 给了两类例子:

  • word vector analogies:例如 man : woman :: king : ?
  • word similarity:比较词向量距离和人类相似度评分的相关性,例如 WordSim353。

这类评估的优点是快,能帮助理解词向量捕捉到了什么。缺点是:除非它和真实任务效果相关,否则不能保证它真的有用。

1.2 Extrinsic evaluation

Extrinsic evaluation 是把词向量放进真实任务系统里,看最终准确率是否提升。

PPT 用 NER 作为例子:

Chris Manning lives in Palo Alto.

这里模型需要识别:

  • Chris Manning 是人名。
  • Palo Alto 是地点。

如果替换一个词向量系统后,NER 准确率提升,而且其它部分不变,那么这个词向量系统就更有实际价值。


2. NER:这讲为什么选它做例子?

NER 是 named entity recognition,即命名实体识别。它要在文本中找到并分类名字类片段,例如:

  • person:人名。
  • organization:组织名。
  • location:地点名。
  • date:日期。

PPT 中的例子包括:

Last night, Paris Hilton wowed in a sequin gown.
Samuel Quinn was arrested in the Hilton Hotel in Paris in April 1989.

NER 的用途包括:

  • 在文档中追踪特定实体的出现。
  • 在问答任务中定位候选答案,因为答案常常是命名实体。
  • 把情感分析和被讨论实体关联起来。
  • 后续接 entity linking,把文本中的实体链接到 Wikidata 这类知识库。

选择 NER 的原因是:它既需要词义,也需要上下文。比如 Paris 可能是人名,也可能是地点;只看单词本身不够,必须看周围词。


3. Window Classification:把上下文变成一个向量

PPT 先讲一个简化版 NER:window classification。

目标不是一次性标完整个句子,而是对每个中心词单独分类:

看中心词附近一小段窗口,判断中心词是否属于某个类别。

例如判断 Paris 是否是 location,窗口长度为 2:

the museums in Paris are amazing to see .

Paris 为中心,取左右各两个词:

museums in Paris are amazing

每个词先查 embedding table 得到词向量:

x_museums, x_in, x_Paris, x_are, x_amazing

然后把这 5 个词向量拼接起来:

\[ x_{\text{window}} = \begin{bmatrix} x_{\text{museums}} \\ x_{\text{in}} \\ x_{\text{Paris}} \\ x_{\text{are}} \\ x_{\text{amazing}} \end{bmatrix} \]

如果每个词向量维度是 \(d\),窗口中有 5 个词,那么:

\[ x_{\text{window}} \in \mathbb{R}^{5d} \]

这就是神经分类器的输入。

3.1 为什么要拼接窗口?

因为中心词的类别常常由上下文决定。

例如:

Paris Hilton

这里 Paris 更像人名的一部分。

in Paris

这里 Paris 更像地点。

窗口向量把局部上下文带给分类器,让模型不只根据中心词自己做判断。

3.2 如何对整句做分类?

对一句话中的每个词,都把它作为中心词取窗口,再运行分类器。

例如句子有 9 个词,就得到 9 个 window inputs,分别判断每个中心词的标签。

PPT 这里为了讲清数学,先用 binary logistic classifier:判断中心词是不是 location。实际系统通常会用 multi-class softmax,一次预测多个类别。


4. 监督学习符号

PPT 接着统一分类任务的符号。

训练集由 \(N\) 个样本组成:

\[ \{(x_i, y_i)\}_{i=1}^{N} \]

其中:

  • \(x_i\):输入,可以是词、词向量、句子、文档等。
  • \(y_i\):标签,是 \(C\) 个类别中的一个。

在 NER window classification 里:

  • \(x_i\) 是某个中心词的窗口拼接向量。
  • \(y_i\) 可以是 location / not location,也可以是 PER、ORG、LOC、DATE 等多类标签。

监督学习的基本形式就是:

给定带标签样本
学习参数 theta
让模型在新输入上预测正确标签

5. 普通分类器和神经分类器有什么不同?

PPT 对比了传统 softmax classifier 和 neural classifier。

5.1 普通 softmax classifier

普通统计/机器学习分类器通常学习一个线性分类器:

score = W x

参数主要是 \(W\)。如果输入 \(x\) 是稀疏符号特征,那么模型依赖人工设计的特征。它的决策边界是线性的,表达能力有限。

5.2 神经分类器

神经分类器的关键差别是:

  1. 它学习分类权重。
  2. 它也学习输入表示,也就是 distributed word representations。
  3. 它可以通过多层网络反复重表示和组合输入。

PPT 用 embedding layer 表达这个过程:

\[ x = L e \]

其中:

  • \(e\) 是 one-hot word vector。
  • \(L\) 是 embedding matrix。
  • \(x\) 是查表得到的 dense word vector。

这意味着词向量不是固定符号,而是训练中会移动的参数。模型可以把词移动到更容易分类的位置。


6. 本讲的核心小网络

PPT 用一个非常重要的小网络贯穿手算梯度:

\[ z = W x + b \]
\[ h = f(z) \]
\[ s = u^\top h \]

对 binary location classifier,再把 score 送入 sigmoid:

\[ \hat{y} = J_t(\theta) = \sigma(s) = \frac{1}{1 + e^{-s}} \]

注意这里 PPT 图中把 \(J_t(\theta)\) 标成模型预测的类别概率。后面讲 cross entropy 时,会把训练目标写成最小化正确类别的 negative log probability。

6.1 每个变量是什么意思?

符号 含义 形状
\(x\) window input,5 个词向量拼接 \(\mathbb{R}^{5d}\)
\(W\) 输入到隐藏层的权重矩阵 \(\mathbb{R}^{H \times 5d}\)
\(b\) hidden layer bias \(\mathbb{R}^{H}\)
\(z\) affine transform 后的 pre-activation \(\mathbb{R}^{H}\)
\(f\) element-wise non-linear function 逐元素作用
\(h\) hidden representation \(\mathbb{R}^{H}\)
\(u\) hidden layer 到 score 的权重 \(\mathbb{R}^{H}\)
\(s\) 标量 score \(\mathbb{R}\)
\(\hat{y}\) 中心词属于 location 的概率 \([0,1]\)

如果 \(\hat{y}\) 越接近 1,模型越相信中心词是 location。

6.2 前向传播的直觉

可以把这个网络理解成三步:

  1. \(z = Wx+b\):把窗口信息线性混合。
  2. \(h=f(z)\):通过非线性函数生成隐藏表示。
  3. \(s=u^\top h\):把隐藏表示压成一个标量分数。

sigmoid 再把分数变成概率。


7. 非线性函数:为什么它们必不可少?

PPT 强调:没有非线性,多层神经网络不会比一层线性变换更强。

假设网络只有线性变换:

\[ h = W_1 x \]
\[ s = W_2 h \]

那么:

\[ s = W_2 W_1 x \]

令:

\[ W = W_2 W_1 \]

就得到:

\[ s = W x \]

这说明两层线性网络可以折叠成一层线性网络。堆更多层也一样,只要中间没有非线性,本质仍然是一个线性变换。

非线性函数的作用是打断这种折叠,让网络能够近似复杂函数,学到非线性决策边界。


8. PPT 中出现的非线性函数

8.1 Logistic / Sigmoid

\[ \sigma(z) = \frac{1}{1 + e^{-z}} \]

输出范围是 \((0,1)\),所以常用来表示概率。PPT 的 binary NER 图就用 sigmoid 把 \(s\) 变成类别概率。

8.2 Tanh

PPT 说明 tanh 可以看成经过缩放和平移的 sigmoid:

\[ \tanh(z) = 2\sigma(2z)-1 \]

tanh 输出范围是 \((-1,1)\)

8.3 ReLU

\[ \text{ReLU}(z) = \max(z, 0) \]

PPT 说,在深层网络中常先尝试 ReLU,因为它训练快,梯度回传通常较好。

但 ReLU 有负半轴的 dead zone:当输入小于 0 时,输出为 0,局部梯度也为 0。这会让某些神经元很难再更新。

8.4 Leaky ReLU / Parametric ReLU

PPT 把它们作为 ReLU dead zone 的缓解方案。核心思想是:负半轴不要完全变成 0,而是保留一个小斜率。

8.5 GELU、SiLU、Swish、GLU、SwiGLU

PPT 还列出现代网络中常见的激活或门控变体。

GELU:

\[ \text{GELU}(x) = x \cdot P(X \le x), \quad X \sim \mathcal{N}(0,1) \]

近似写法:

\[ \text{GELU}(x) \approx x \cdot \sigma(1.702x) \]

SiLU:

\[ \text{SiLU}(x) = x \cdot \sigma(x) \]

Swish:

\[ \text{Swish}(x) = x \cdot \sigma(\beta x) \]

GLU 使用 gate / switch:

\[ \text{GLU}(x) = (xV+v) \otimes \sigma(xW+b) \]

SwiGLU 使用 Swish 风格的门控:

\[ \text{SwiGLU}(x) = (xV+c) \otimes \text{Swish}_{\beta}(xW+b) \]

其中 \(\otimes\) 表示逐元素乘法。PPT 提到 SwiGLU 常用于 Llama3、Qwen3 等模型。

本讲不会深入这些激活函数的工程细节;它们在这里的作用是说明:非线性函数是神经网络表达能力的关键部件。


9. Cross Entropy Loss:分类训练到底在优化什么?

PPT 说,之前我们把目标说成:

最大化正确类别的概率

等价地,也可以说成:

最小化正确类别概率的负对数

cross entropy 就是这种分类损失的标准写法。

9.1 交叉熵公式

设真实分布是 \(p\),模型预测分布是 \(q\),共有 \(C\) 个类别。交叉熵为:

\[ H(p,q) = - \sum_{c=1}^{C} p(c)\log q(c) \]

在分类任务中,真实标签通常是 one-hot 分布。假设正确类别是 \(y_i\),那么:

\[ p(y_i)=1 \]

其它类别:

\[ p(c)=0,\quad c \ne y_i \]

因此求和里只剩正确类别那一项:

\[ H(p,q) = - \log q(y_i) \]

PPT 写成:

\[ -\log p(y_i \mid x_i) \]

这里的 \(p(y_i \mid x_i)\) 表示模型给正确类别的概率。

9.2 为什么正确类别概率越高,loss 越低?

如果模型给正确类别的概率是 0.9:

\[ -\log(0.9) \]

很小。

如果概率是 0.01:

\[ -\log(0.01) \]

很大。

所以 cross entropy 会强烈惩罚“把正确答案概率给得很低”的模型。


10. SGD:参数怎样被更新?

PPT 回顾 stochastic gradient descent。更新式是:

\[ \theta^{new} = \theta^{old} - \alpha \nabla_{\theta}J(\theta) \]

对单个参数 \(\theta_j\)

\[ \theta_j^{new} = \theta_j^{old} - \alpha \frac{\partial J(\theta)} {\partial \theta_j^{old}} \]

其中:

  • \(\theta\):模型所有参数。
  • \(J(\theta)\):loss / objective。
  • \(\nabla_{\theta}J(\theta)\):loss 对参数的梯度。
  • \(\alpha\):step size 或 learning rate。

PPT 特别提醒:在深度学习中,\(\theta\) 不只是分类器权重,也包括数据表示,例如 word vectors。

这点很重要。训练 NER 时,更新的不只是 \(W,b,u\),窗口里的词向量也可以被更新。

10.1 为什么沿负梯度方向更新?

梯度指向函数上升最快的方向。我们要最小化 loss,所以往梯度的反方向走:

参数新值 = 参数旧值 - 学习率 * 梯度

如果某个参数增大会让 loss 增大,那么梯度为正,SGD 会减小它。

如果某个参数增大会让 loss 减小,那么梯度为负,SGD 会增大它。

10.2 核心问题:梯度怎么来?

PPT 问:

How can we compute \(\nabla_{\theta}J(\theta)\)?

给出两条路:

  1. 手算。
  2. 用 backpropagation 算法。

这讲先手算,是为了让你理解 backprop 到底在自动化什么。


11. Gradient:标量输出对输入的变化率

先从一维函数开始。

PPT 例子:

\[ f(x)=x^3 \]

导数是:

\[ \frac{df}{dx} = 3x^2 \]

导数回答的问题是:

如果输入 \(x\) 轻微变化,输出 \(f(x)\) 会变化多少?

\(x=1\) 时,斜率约为 3。

\(x=4\) 时,斜率约为 48。

所以同样改变一点输入,在不同位置会造成不同大小的输出变化。

11.1 多输入标量函数的 gradient

如果函数有 1 个输出、\(n\) 个输入:

\[ f: \mathbb{R}^{n} \to \mathbb{R} \]

那么 gradient 是所有偏导数组成的向量:

\[ \nabla_x f = \begin{bmatrix} \frac{\partial f}{\partial x_1} \\ \frac{\partial f}{\partial x_2} \\ \vdots \\ \frac{\partial f}{\partial x_n} \end{bmatrix} \]

它告诉我们:每个输入维度变化一点,会怎样影响这个标量输出。

在神经网络训练中,loss 是标量,所以我们最终要的就是:

loss 对每个参数的 gradient

12. Jacobian:向量输出对向量输入的导数

如果函数有 \(m\) 个输出、\(n\) 个输入:

\[ f: \mathbb{R}^{n} \to \mathbb{R}^{m} \]

那么它的导数不是一个向量,而是一个 \(m \times n\) 的矩阵:

\[ J = \begin{bmatrix} \frac{\partial f_1}{\partial x_1} & \frac{\partial f_1}{\partial x_2} & \cdots & \frac{\partial f_1}{\partial x_n} \\ \frac{\partial f_2}{\partial x_1} & \frac{\partial f_2}{\partial x_2} & \cdots & \frac{\partial f_2}{\partial x_n} \\ \vdots & \vdots & \ddots & \vdots \\ \frac{\partial f_m}{\partial x_1} & \frac{\partial f_m}{\partial x_2} & \cdots & \frac{\partial f_m}{\partial x_n} \end{bmatrix} \]

这就是 Jacobian。

12.1 Gradient 和 Jacobian 的区别

情况 名称 形状
1 个输出,\(n\) 个输入 gradient \(n \times 1\) 或按约定写成同形向量
\(m\) 个输出,\(n\) 个输入 Jacobian \(m \times n\)

神经网络里很多中间变量是向量,例如 \(z\)\(h\)。所以理解 Jacobian 是理解多变量 chain rule 的基础。


13. Chain Rule:反向传播的数学核心

一元函数复合时,链式法则是:

\[ \frac{d}{dx}g(f(x)) = g'(f(x))f'(x) \]

多变量情况下,PPT 的结论是:

multiply Jacobians

如果:

\[ x \to z \to h \to s \]

那么:

\[ \frac{\partial s}{\partial x} = \frac{\partial s}{\partial h} \frac{\partial h}{\partial z} \frac{\partial z}{\partial x} \]

这就是本讲后面所有推导的骨架。


14. Element-wise Activation 的 Jacobian

神经网络常用逐元素激活函数:

\[ h = f(z) \]

也就是:

\[ h_i = f(z_i) \]

\(i\) 个输出只依赖第 \(i\) 个输入,不依赖其它输入。因此 Jacobian 是对角矩阵:

\[ \frac{\partial h}{\partial z} = \text{diag}(f'(z)) \]

展开看就是:

\[ \begin{bmatrix} f'(z_1) & 0 & \cdots & 0 \\ 0 & f'(z_2) & \cdots & 0 \\ \vdots & \vdots & \ddots & \vdots \\ 0 & 0 & \cdots & f'(z_H) \end{bmatrix} \]

这件事后面会让公式变成逐元素乘法。


15. 先记住三个局部 Jacobian

对小网络:

\[ s = u^\top h \]
\[ h = f(z) \]
\[ z = Wx+b \]

PPT 在推导中用到这些局部结果。

15.1 对 \(u^\top h\) 求导

\[ \frac{\partial}{\partial u}(u^\top h) = h^\top \]

如果最终按 shape convention 写成和 \(u\) 同形的梯度,通常会写成:

\[ \nabla_u s = h \]

15.2 对 \(f(z)\) 求导

\[ \frac{\partial}{\partial z}f(z) = \text{diag}(f'(z)) \]

15.3 对 \(Wx+b\) 关于 \(b\) 求导

\[ \frac{\partial}{\partial b}(Wx+b) = I \]

因为 \(b\) 的第 \(i\) 个分量只加到 \(z_i\) 上。


16. 手算小网络:先拆成简单变量

PPT 告诉我们,推导时第一步是:

Break up equations into simple pieces

不要一口气对 \(s=u^\top f(Wx+b)\) 求导。先定义:

\[ z = Wx+b \]
\[ h = f(z) \]
\[ s = u^\top h \]

然后持续追踪每个变量的维度:

符号 形状
\(x\) \(D \times 1\),其中 \(D=5d\)
\(W\) \(H \times D\)
\(b\) \(H \times 1\)
\(z\) \(H \times 1\)
\(h\) \(H \times 1\)
\(u\) \(H \times 1\)
\(s\) 标量

PPT 特别强调:carefully define variables and keep track of dimensionality。

中文说就是:不要让 shape 在脑子里漂着。每一步都要知道对象是标量、向量还是矩阵。


17. 例子一:求 \(\partial s / \partial b\)

从链式法则开始:

\[ \frac{\partial s}{\partial b} = \frac{\partial s}{\partial h} \frac{\partial h}{\partial z} \frac{\partial z}{\partial b} \]

逐项代入:

\[ \frac{\partial s}{\partial h} = u^\top \]
\[ \frac{\partial h}{\partial z} = \text{diag}(f'(z)) \]
\[ \frac{\partial z}{\partial b} = I \]

所以:

\[ \frac{\partial s}{\partial b} = u^\top \text{diag}(f'(z)) I \]

因为乘以 \(I\) 不改变结果:

\[ \frac{\partial s}{\partial b} = u^\top \text{diag}(f'(z)) \]

PPT 进一步把它写成 Hadamard product:

\[ \frac{\partial s}{\partial b} = u^\top \odot f'(z) \]

其中 \(\odot\) 表示逐元素乘法。

17.1 Upstream gradient \(\delta\)

PPT 把重复出现的中间量记为:

\[ \delta = \frac{\partial s}{\partial h} \frac{\partial h}{\partial z} = u^\top \odot f'(z) \]

它叫 upstream gradient 或 error signal。

直觉上,\(\delta_i\) 表示:

\(z_i\) 改变一点,会对最终 score \(s\) 造成多大影响?

如果按列向量 shape convention 书写,也可以把它写成:

\[ \delta_{\text{col}} = u \odot f'(z) \]

然后:

\[ \nabla_b s = \delta_{\text{col}} \]

18. 例子二:求 \(\partial s / \partial W\)

接着求 \(W\) 的梯度。

链式法则:

\[ \frac{\partial s}{\partial W} = \delta \frac{\partial z}{\partial W} \]

PPT 的关键直觉是:

delta 会出现在答案里
另一个项应该和 x 有关,因为 z = Wx + b

最终答案按 shape convention 写成和 \(W\) 一样的矩阵:

\[ \nabla_W s = \delta_{\text{col}} x^\top \]

如果使用 PPT 中偏 Jacobian row-vector 的记法,会看到类似:

\[ \frac{\partial s}{\partial W} = \delta^\top x^\top \]

不要被转置吓到。核心结构是:

W 的梯度 = error signal 和 local input signal 的外积

也就是:

\[ \text{hidden error} \times \text{input signal} \]

18.1 为什么是外积?

写出第 \(i\) 个 hidden pre-activation:

\[ z_i = \sum_{j=1}^{D} W_{ij}x_j + b_i \]

单个权重 \(W_{ij}\) 只影响 \(z_i\),且:

\[ \frac{\partial z_i}{\partial W_{ij}} = x_j \]

如果 \(z_i\) 对 score 的影响是 \(\delta_i\),那么:

\[ \frac{\partial s}{\partial W_{ij}} = \delta_i x_j \]

所有 \(i,j\) 组合起来就是矩阵:

\[ \nabla_W s = \begin{bmatrix} \delta_1 x_1 & \delta_1 x_2 & \cdots & \delta_1 x_D \\ \delta_2 x_1 & \delta_2 x_2 & \cdots & \delta_2 x_D \\ \vdots & \vdots & \ddots & \vdots \\ \delta_H x_1 & \delta_H x_2 & \cdots & \delta_H x_D \end{bmatrix} \]

这正是:

\[ \delta_{\text{col}}x^\top \]

19. Shape Convention:为什么 PPT 一直提醒转置?

数学上,Jacobian 经常把导数写成一行或一个展开后的矩阵。但在实现 SGD 时,我们希望:

参数的梯度形状 = 参数自己的形状

PPT 称之为 shape convention。

例如:

  • \(b\)\(H \times 1\),所以 \(\nabla_b s\) 也应该是 \(H \times 1\)
  • \(W\)\(H \times D\),所以 \(\nabla_W s\) 也应该是 \(H \times D\)

这能让更新式直接写成:

\[ W^{new} = W^{old} - \alpha \nabla_W J \]

如果梯度不是 \(H \times D\),这个减法就没法直接做。

19.1 两种做题策略

PPT 给出两种做法:

  1. 先用 Jacobian form 推导,最后 reshape 或 transpose 成 shape convention。
  2. 一开始就遵守 shape convention,用维度检查决定什么时候转置。

初学建议是:先写清每个变量 shape,然后随时问自己:

我最后得到的梯度,能不能直接和参数相减?

如果不能,shape 大概率错了。


20. 从手算到 Backpropagation

PPT 说:

We have almost shown you backpropagation.

因为手算过程已经包含了 backprop 的本质:

  1. 使用多变量 / 矩阵 chain rule。
  2. 复用高层已经算出的导数。

例如 \(\delta\) 同时用于:

\[ \nabla_b s = \delta_{\text{col}} \]

和:

\[ \nabla_W s = \delta_{\text{col}}x^\top \]

如果每个参数都从头单独求导,会重复计算大量相同链条。backprop 的核心就是把这些可复用的中间梯度保存下来,从输出往输入一次性传播。


21. Computation Graph:软件如何表示神经网络?

PPT 说,软件把神经网络方程表示成 graph:

  • source nodes:输入节点。
  • interior nodes:操作节点。
  • edges:传递操作结果。

例如:

x, W, b -> matmul/add -> z -> activation -> h -> dot -> s

前向传播时,数据沿边从输入流向输出。

21.1 Forward Propagation

Forward pass 做两件事:

  1. 按顺序计算每个节点的值。
  2. 保存中间值,供 backward pass 使用。

比如为了反向传播 ReLU,需要知道前向时 \(z\) 哪些位置大于 0。

为了计算 \(\nabla_W s\),需要保存输入 \(x\) 和 upstream gradient \(\delta\)


22. Single Node Backprop:一个节点怎样回传梯度?

PPT 对单个节点给出三个概念:

  • upstream gradient:从后面传来的梯度。
  • local gradient:当前节点输出对输入的局部导数。
  • downstream gradient:继续往前传给输入的梯度。

核心公式:

\[ \text{downstream gradient} = \text{upstream gradient} \times \text{local gradient} \]

这就是 chain rule 在计算图节点上的版本。

22.1 一个输入的节点

如果:

\[ y = f(x) \]

后面传来:

\[ \frac{\partial J}{\partial y} \]

当前节点知道:

\[ \frac{\partial y}{\partial x} \]

于是传给 \(x\)

\[ \frac{\partial J}{\partial x} = \frac{\partial J}{\partial y} \frac{\partial y}{\partial x} \]

22.2 多个输入的节点

如果:

\[ y = f(x_1, x_2, \ldots, x_n) \]

那么每个输入都有自己的 local gradient:

\[ \frac{\partial y}{\partial x_1}, \frac{\partial y}{\partial x_2}, \ldots, \frac{\partial y}{\partial x_n} \]

节点需要分别把 downstream gradient 传给每个输入。


23. 分支处:为什么梯度要相加?

PPT 明确给出结论:

Gradients sum at outward branches.

如果一个变量 \(x\) 同时走向两条路径:

x -> a -> J
x -> b -> J

那么 \(x\) 对最终 loss 的总影响,是两条路径影响之和:

\[ \frac{\partial J}{\partial x} = \frac{\partial J}{\partial a} \frac{\partial a}{\partial x} + \frac{\partial J}{\partial b} \frac{\partial b}{\partial x} \]

这在神经网络中非常常见。例如同一个参数被多个样本、多个位置、多个分支共享时,最终梯度会累加所有使用路径的贡献。


24. PPT 给出的节点直觉

PPT 用几个简单操作解释不同节点怎样处理 upstream gradient。

24.1 加法节点

加法会把 upstream gradient 分发给每个加数。

如果:

\[ z = x + y \]

那么:

\[ \frac{\partial z}{\partial x}=1 \]
\[ \frac{\partial z}{\partial y}=1 \]

所以传给 \(x\)\(y\) 的梯度都等于 upstream gradient。

PPT 的直觉:+ distributes the upstream gradient。

24.2 Max 节点

max 会把 upstream gradient 路由给前向传播中取到最大值的输入。

如果:

\[ z = \max(x,y) \]

并且前向时 \(x>y\),那么梯度主要传给 \(x\),不给 \(y\)

PPT 的直觉:max routes the upstream gradient。

24.3 乘法节点

乘法会交换另一个输入作为 local gradient。

如果:

\[ z = xy \]

那么:

\[ \frac{\partial z}{\partial x}=y \]
\[ \frac{\partial z}{\partial y}=x \]

所以传给 \(x\) 的梯度会乘以 \(y\),传给 \(y\) 的梯度会乘以 \(x\)

PPT 的直觉:* switches the upstream gradient。


25. 为什么 Backprop 高效?

PPT 对比了错误和正确的做法。

错误做法:

先算某个参数的梯度
再独立算另一个参数的梯度
每次都重复经过同一批中间节点

正确做法:

一次 backward pass 中同时算出所有梯度

这和前面手算时复用 \(\delta\) 是同一个思想。

25.1 一般计算图中的 Backprop

PPT 给出一般流程。

Forward pass:

  1. 按 topological sort order 访问节点。
  2. 每个节点根据前驱节点计算自己的值。

Backward pass:

  1. 把最终标量输出的梯度初始化为 1。
  2. 按反向顺序访问节点。
  3. 对每个节点,用后继节点传回的梯度计算它自己的梯度。

如果实现正确,forward pass 和 backward pass 的大 \(O\) 复杂度相同。

这就是 backprop 强大的地方:不是“对每个参数单独跑一次模型”,而是“沿计算图反向扫一遍,所有参数的梯度都拿到”。


26. Automatic Differentiation:现代框架自动做了什么?

PPT 说,梯度计算可以从 forward propagation 的 symbolic expression 自动推断出来。

每种节点类型需要知道两件事:

  1. 怎样计算自己的输出。
  2. 给定输出梯度时,怎样计算输入梯度。

现代深度学习框架,例如 TensorFlow、PyTorch,会为你执行 backpropagation。

但 PPT 也提醒:框架主要把反向传播流程自动化,具体层或节点的 local derivative 仍然需要由实现者正确写出或由框架内置。

26.1 为什么还要学这些细节?

因为框架会算梯度,不代表你不需要理解梯度。

你仍然需要判断:

  • loss 是否定义正确。
  • 参数有没有参与计算图。
  • 梯度是否断掉。
  • shape 是否合理。
  • 激活函数是否造成梯度消失或死亡区域。
  • 新写的 layer / loss 是否有正确的 local derivative。

PPT 最后也强调:backpropagation 并不总是开箱即用,理解底层机制对 debug 和改进模型很重要。


27. Manual Gradient Checking:用数值梯度检查实现

PPT 最后介绍 numeric gradient。

对一维函数:

\[ f'(x) \approx \frac{f(x+h)-f(x-h)}{2h} \]

其中 \(h\) 很小,PPT 给的量级是:

\[ h \approx 10^{-4} \]

这叫 centered difference。

27.1 它为什么有用?

如果你手写了某个 layer 的 backward,可以用 numeric gradient 得到一个近似梯度,再和你的 backprop 梯度比较。

如果两者差别很大,说明实现可能有 bug。

27.2 它为什么很慢?

因为每检查一个参数,都要重新计算函数值。

如果模型有一百万个参数,数值梯度需要大量重复前向计算。因此它适合检查小模块,不适合正式训练。

27.3 它检查的是什么?

它检查的是:

梯度实现是否正确

不是:

模型是否会泛化
超参数是否最好
任务设计是否正确

28. 本讲的完整知识链

把 PPT 的内容连起来,可以得到下面这条链:

词向量
-> window input
-> neural classifier
-> score / probability
-> cross entropy loss
-> SGD 更新参数
-> 需要梯度
-> 用 chain rule 计算梯度
-> 用 computation graph 组织计算
-> 用 backpropagation 高效复用中间梯度
-> 用 automatic differentiation 自动执行
-> 用 gradient checking 检查实现

这条链是后面所有深度 NLP 模型的基础。RNN、Transformer、pretraining、fine-tuning 都离不开它。


29. 初学者最容易混的点

29.1 \(s\)\(\hat{y}\)、loss 不是同一个东西

在本讲小网络中:

\[ s = u^\top h \]

是未归一化 score。

\[ \hat{y} = \sigma(s) \]

是模型预测的概率。

cross entropy / negative log probability 才是训练要最小化的 loss。

29.2 Gradient 不是“参数更新量”

gradient 是 loss 对参数的敏感度。真正的更新量还要乘以学习率并取负方向:

\[ -\alpha \nabla_{\theta}J(\theta) \]

29.3 Jacobian 不是总要显式算出来

理解 Jacobian 是为了理解 chain rule 和 shape。实际框架里通常不会显式构造巨大的 Jacobian,而是通过局部规则和向量化操作高效算出结果。

29.4 反向传播不是另一个优化算法

SGD 是优化算法,负责决定参数往哪里走。

Backpropagation 是求梯度的算法,负责告诉 SGD 当前梯度是多少。

两者配合起来才完成训练。

29.5 Automatic differentiation 不是“自动理解模型”

autodiff 只会按照你写出的计算图求导。如果 loss 写错、标签错、模型结构错,它仍然会忠实地对错误目标求导。


30. 板书级总结

本讲最应该背下来的公式和规则如下。

30.1 NER window classifier

\[ x_{\text{window}} \in \mathbb{R}^{5d} \]
\[ z = Wx+b \]
\[ h=f(z) \]
\[ s=u^\top h \]
\[ \hat{y} = \sigma(s) = \frac{1}{1+e^{-s}} \]

30.2 Cross entropy

\[ H(p,q) = - \sum_{c=1}^{C}p(c)\log q(c) \]

one-hot target 下:

\[ H(p,q) = - \log p(y_i \mid x_i) \]

30.3 SGD

\[ \theta^{new} = \theta^{old} - \alpha\nabla_{\theta}J(\theta) \]

30.4 Chain rule

\[ \frac{\partial s}{\partial x} = \frac{\partial s}{\partial h} \frac{\partial h}{\partial z} \frac{\partial z}{\partial x} \]

30.5 Activation Jacobian

\[ \frac{\partial f(z)}{\partial z} = \text{diag}(f'(z)) \]

30.6 Upstream gradient

\[ \delta_{\text{col}} = u \odot f'(z) \]

30.7 参数梯度

\[ \nabla_b s = \delta_{\text{col}} \]
\[ \nabla_W s = \delta_{\text{col}}x^\top \]

30.8 单节点反向传播

\[ \text{downstream gradient} = \text{upstream gradient} \times \text{local gradient} \]

30.9 数值梯度检查

\[ f'(x) \approx \frac{f(x+h)-f(x-h)}{2h} \]

31. 自学路线

按下面顺序学这讲,会比直接啃矩阵求导轻松很多。

第一步:先把任务跑通

只看 NER window classification:

句子
-> 每个词查词向量
-> 拼接窗口
-> 神经分类器
-> 判断中心词类别

先理解输入输出,不要急着求导。

第二步:弄清前向传播

把下面三行反复写熟:

\[ z = Wx+b \]
\[ h=f(z) \]
\[ s=u^\top h \]

每写一行,都标出 shape。

第三步:理解 loss 和 SGD

明确:

模型输出概率
cross entropy 变成 loss
SGD 根据 gradient 更新参数

第四步:学 Jacobian 和 chain rule

先接受:

多变量 chain rule = Jacobian 相乘

再看:

\[ \frac{\partial s}{\partial b} = \frac{\partial s}{\partial h} \frac{\partial h}{\partial z} \frac{\partial z}{\partial b} \]

第五步:把 \(\delta\) 当成 error signal

记住:

\[ \delta_{\text{col}} = u \odot f'(z) \]

然后:

\[ \nabla_b s = \delta_{\text{col}} \]
\[ \nabla_W s = \delta_{\text{col}}x^\top \]

第六步:最后上升到 computation graph

当你理解单个小网络怎么求导后,再看计算图会很自然:

每个节点只会一件事:
拿 upstream gradient
乘 local gradient
把 downstream gradient 传回去

32. 自测题

  1. intrinsic evaluation 和 extrinsic evaluation 的区别是什么?
  2. 为什么 NER 中 Paris 不能只靠单词本身判断类别?
  3. window size 为 2、词向量维度为 \(d\) 时,拼接输入 \(x\) 的维度是多少?
  4. 神经分类器相比普通 softmax classifier,多学习了什么?
  5. 为什么没有非线性时,多层网络仍然等价于一层线性变换?
  6. sigmoid、tanh、ReLU 的输出范围或核心特点分别是什么?
  7. cross entropy 在 one-hot 标签下为什么只剩 \(-\log p(y_i \mid x_i)\)
  8. SGD 更新式中 \(\alpha\) 表示什么?
  9. gradient 和 Jacobian 的形状有什么区别?
  10. 为什么 \(h=f(z)\) 的 Jacobian 是对角矩阵?
  11. \(z=Wx+b\),为什么 \(\nabla_W s\) 会包含输入 \(x\)
  12. 什么是 upstream gradient?
  13. 为什么分支处的梯度要相加?
  14. 加法、max、乘法节点分别怎样处理 upstream gradient?
  15. automatic differentiation 和 manual gradient checking 的作用分别是什么?

33. 自测题参考答案

  1. Intrinsic evaluation 评估词向量在中间任务上的表现,例如类比和相似度;extrinsic evaluation 把词向量放进真实下游任务,看最终任务指标是否提升。
  2. 因为 Paris 可能是人名的一部分,也可能是地点;上下文决定它在当前句子中的实体类别。
  3. 窗口包含中心词和左右各两个词,共 5 个词,所以输入维度是 \(5d\)
  4. 它不仅学习分类器权重,也学习 distributed word representations,并通过 hidden layers 重表示输入。
  5. 多个线性变换相乘仍然是线性变换,例如 \(W_2W_1x = Wx\)。没有非线性时,多层可以折叠成一层。
  6. Sigmoid 输出 \((0,1)\),常用于概率;tanh 输出 \((-1,1)\);ReLU 是 \(\max(z,0)\),训练快但有负半轴 dead zone。
  7. one-hot 标签中只有正确类别的 \(p(c)=1\),其它类别为 0,所以求和只剩正确类别那一项。
  8. \(\alpha\) 是 step size 或 learning rate,控制每次沿负梯度方向走多大一步。
  9. 标量函数对向量输入的导数是 gradient;向量函数对向量输入的导数是 Jacobian 矩阵。
  10. 因为 element-wise activation 的 \(h_i\) 只依赖 \(z_i\),不依赖其它 \(z_j\),所以非对角项为 0。
  11. 因为 \(z_i=\sum_j W_{ij}x_j+b_i\),所以 \(\partial z_i / \partial W_{ij}=x_j\)。每个权重的梯度都带有对应输入分量。
  12. upstream gradient 是从后面节点传来的梯度,表示当前节点输出变化对最终目标的影响。
  13. 同一个变量通过多条路径影响最终 loss,总影响等于每条路径影响之和。
  14. 加法把梯度分给每个加数;max 把梯度路由给前向时取最大值的输入;乘法把另一个输入作为 local gradient 乘到 upstream gradient 上。
  15. Automatic differentiation 根据计算图自动执行 backprop;manual gradient checking 用数值梯度近似检查手写或自定义层的梯度实现是否正确。