微积分 入门

读懂任何深度学习论文之前所需的最少微积分。4 个短小主题,涵盖「导数到底是什么」、 多变量情况下的偏导数、为什么链式法则是反向传播的核心、以及梯度如何把所有偏导数 打包成一支指向「上坡方向」的箭头。不讲积分 —— 训练一个百亿参数模型, 从头到尾用不到一次积分。

01

导数(Derivative)

一个数,表示函数此刻正在以多快的速度变化。

开车时,仪表盘显示 60 km/h。这个数既不是你跑了多远,也不是你开了多久 —— 而是在这一瞬间,「距离随时间变化」的速率。导数就是把这个想法形式化: 对任意函数 f(x),它的导数 f'(x) 告诉你, 当 x 移动时,f 正在以多快的速度变化。

几何上,导数就是 切线的斜率 —— 在某个点上「擦着」曲线、 和曲线走向一致的那条直线。如果曲线在 x = 3 处陡峭上升, 那里切线的斜率就大,f'(3) 是一个比较大的正数; 如果曲线刚到顶,准备下来,切线是水平的,f' 等于 0; 如果在下坠,斜率就是负的。

三种记号意思完全一样,ML 论文里都混着用:

  • f'(x) —— 拉格朗日的撇号记号,简洁。
  • df/dx —— 莱布尼茨的比例记号,读作「f 关于 x 的变化率」。
  • d/dx [f(x)] —— 想强调「正在求导」这个动作时用。

具体例子。取 f(x) = x²。图像是一条抛物线,左边下降、右边上升。 它的导数是 f'(x) = 2x。在 x = 3 处, 斜率是 6 —— 挺陡的;在 x = 0 处斜率是 0 —— 抛物线刚好到底;在 x = −1 处斜率是 −2 —— 正在下降。 一个公式同时给出了曲线上每一点的「上升速率」。

-2-1012x = -2斜率 f'(x) = 2 · (-2) = -4
1 / 5
f(x) = x²,因此 f'(x) = 2x。点在曲线上移动,切线随之旋转 —— 切线的斜率,就是导数。

一套简短的求导规则,足以应付 ML 论文里几乎所有情形:

  d/dx [c]         = 0              (常数不变)
  d/dx [x]         = 1
  d/dx [x^n]       = n · x^(n−1)     (幂法则)
  d/dx [eˣ]        = eˣ              (e 的指数,导数还是它自己)
  d/dx [ln x]      = 1/x
  d/dx [f + g]     = f' + g'         (线性)
  d/dx [c · f]     = c · f'

这些和 ML 有什么关系?因为训练模型本质上就是「找谷底」。 这里的「谷」是损失函数 —— 每一组参数对应一个数,越小越好。 导数告诉你,站在当前位置,「下坡方向」在哪边。梯度下降就是顺着负导数方向 走一小步,然后重复。重复上百万次以后,你就站在谷底附近 —— 一个能拟合数据的模型。

在 Transformer 里:每一个可学习参数(有几十亿个),都对应一个 「损失对该参数的导数」。优化器把这个导数读成「这个权重往上微调一点, 损失会上升(或下降)多少」。然后它把每个权重朝着「降低损失」的方向各推一下。 这一推,乘以学习率,就是一次训练步。后面三节会一步步搭出 Transformer 真正用来在大规模上算出这些导数的机器。

02

偏导数(Partial Derivative)

多变量函数的导数 —— 一次只动一个变量,其他全部按住不动。

第 1 节的导数,假设函数只有一个输入:输入 x、输出 f(x)。 真实的模型一次有几百万个输入 —— 权重、偏置、上一层传过来的激活。 我们需要一个能处理「多变量」函数的导数概念。这就是 偏导数

从两变量函数开始:f(x, y)。把它想成一片地形 —— 每对经纬度 (x, y) 都有一个高程 f(x, y)。 站在某点 (x₀, y₀),现在能问两个问题: 「若我只往东走一小步(只动 x),高度变化得多快?」 以及「只往北走呢(只动 y)?」 这两个答案,就是这一点的两个 偏导数

记号上,用花体的 (读作「偏」或「del」)代替第 1 节那种直立的 d, 提示读者「其他变量正按住不动」:

  • ∂f/∂x —— 让 y 不动,只动 x 时的变化率。
  • ∂f/∂y —— 让 x 不动,只动 y 时的变化率。

算偏导很机械:把其他所有变量当常数,再套第 1 节的单变量规则。 以 f(x, y) = x² + 3xy + y³ 为例:

  ∂f/∂x   (把 y 当常数)
        = d/dx [x²] + d/dx [3xy] + d/dx [y³]
        = 2x        + 3y         + 0
        = 2x + 3y

  ∂f/∂y   (把 x 当常数)
        = d/dy [x²] + d/dy [3xy] + d/dy [y³]
        = 0         + 3x         + 3y²
        = 3x + 3y²

∂f/∂x = 2x + 3y 读作:「无论你站在地表的哪一点, 如果你只朝 x 方向用力推,高度上升的速度就是这个值。」 代入一个具体点,比如 (x, y) = (1, 2),得到 8

f(x, y)xyy = y₀ 处的切片尚未选切片f(x, y) = x² + 3xy + y³ —— 一片曲面,用等高线图表示。
1 / 3
把一个变量按住,曲面就变成一条曲线。偏导数就是这条曲线的斜率。

「切片」就是它的直觉图像。把 y 钉在 y₀, 相当于从那张曲面上切下一条曲线 —— 横截面在 y = y₀ 处。 在这条 2D 截面上,只剩 x 一个变量,而那一点切线的斜率, 恰好就是 ∂f/∂x。 偏导数本质就是普通导数,「偏」字只是在记录「你切的是哪一刀」。

推广到 D 个变量,什么都不用改。函数 f(x₁, x₂, …, x_D) D 个偏导数,一个变量对应一个;每一个都靠「按住其他 D − 1 个变量」来求。 机械难度没增加,只是账本变长了。

一个小却关键的细节:∂f/∂x 本身也是所有变量的函数 —— 换一个 y,「x 方向的斜率」通常也会变。 某一点的偏导数是一个数,但「偏导数函数」依旧是和 f 同维度的多变量函数。

在 Transformer 里:损失是一个标量 —— 每条训练样本一个数,在批次上平均一下。 模型有几十亿个参数 w₁, w₂, …, w_n。训练真正需要的, 是「损失对每个参数」的偏导:对每个 i∂L/∂wᵢ。 每一个偏导都在回答「我把这个权重往上推一丁点,损失会上升还是下降?」 下一节的链式法则,就是让这「几十亿个偏导一次性算出来」这件事真的可行的关键。

03

链式法则(Chain Rule)

函数套函数,把链条上每一节的「变化率」相乘。

想象三个咬合在一起的齿轮。齿轮 A 转 1°,齿轮 B 转 2°,齿轮 C 转 5°。 你转齿轮 A,齿轮 C 转得有多快?简单 —— 把比例相乘: A 每转 1°,C 转 2 · 5 = 10 度。这就是链式法则。 当变化沿着一连串函数传播时,总的变化率就是链条上「每一节局部变化率」的乘积。

形式上:若 y = f(g(x)) —— 即把 x 喂给 g, 再把结果喂给 f —— 那么:

dy/dx = f'(g(x)) · g'(x)

或者用更直观的莱布尼茨记号,引入辅助变量 u = g(x):

dy/dx = dy/du · du/dx

莱布尼茨形式几乎像「分数约分」一样自然 —— 事实上,它就是这么自然: 导数确实通过乘法来组合。三个函数串起来?dy/dx = dy/du · du/dv · dv/dxN 个串起来?乘 N 项。配方永远不变。

具体例子。设 y = sin(x²)。令 u = x²,则 y = sin(u):

  dy/du  =  cos(u)         (sin 的导数)
  du/dx  =  2x             (x² 的导数)
  dy/dx  =  dy/du · du/dx
         =  cos(u) · 2x
         =  cos(x²) · 2x   (把 u 代回去)

代入 x = 1:cos(1) · 2 ≈ 0.54 · 2 ≈ 1.08。 这就是 sin(x²)x = 1 处的斜率。 全程只用了两个单变量导数相乘 —— 没动用过任何比第 1 节更深的微积分。

x= 1u = x²= 1y = sin(u)= 0.84du/dx2x = 2dy/ducos(u) ≈ 0.54dy/dx = du/dx · dy/du = 2 · 0.54 ≈ 1.08三个变量:x、然后 u = x²、然后 y = sin(u)。每个箭头携带一个局部变化率。
1 / 4
y = sin(x²)。两个局部变化率相乘,得到 x = 1 处的总斜率。

多变量版本,就是支撑整个深度学习的那个链式法则。若 L 依赖 y、 而 y 依赖 w,即便 y 是个向量 甚至整层的激活,同样的乘法依然适用 —— 只是把普通导数换成偏导,并对中间变量求和。 骨架不变:从输入到输出的路径上,把局部导数相乘起来。

这恰好就是 反向传播(backpropagation) 的本质。 现代模型是个很深的复合函数:输入 → 第 1 层 → 第 2 层 → … → 第 N 层 → 损失。 要得到第 5 层里某权重 w∂L/∂w, 把从 L 沿着第 N、N−1、… 一路反推到第 5 层、最后落在 w 上的 那条链上的导数,统统乘起来。整套算法里没用到任何比第 1 节更深的微积分 —— 只是把链式法则用了几十亿遍。

backprop 这个名字里的「反向」,是指它走链条的方向正向(输入 → 损失)是算预测值和损失值。反向(损失 → 输入)是算梯度。 链式法则两个方向都成立;而当「损失是一个标量,参数有上百万」时, 反向算梯度便宜得多。

在 Transformer 里:Transformer 里的每一步运算 —— 矩阵乘法、softmax、 LayerNorm、GELU、残差加 —— 都会在前向过程中把自己「登记」到一张计算图上。 反向开始时,框架沿着这张图从损失走回输入,在每个节点上把局部梯度相乘。 每个权重就这样,通过链式法则把乘积「结算」成自己的 ∂L/∂w。 一次前向 + 一次反向 = 一步训练。链式法则不是花边小知识 —— 从机制上说,它就是深度学习之所以可行的全部理由

04

梯度(Gradient)

把所有偏导数打包成一个向量,这个向量指向「上坡最快」的方向。

多变量函数有很多偏导数 —— 一个输入变量对应一个。一项项分开列没问题, 但更有用的做法,是把它们打包成一个向量。这个向量就是 梯度, 记作 ∇f(读作「del f」或「grad f」):

∇f = (∂f/∂x₁, ∂f/∂x₂, …, ∂f/∂x_D)

梯度和输入活在同一个空间里。两变量函数的梯度是二维的;D 变量函数的梯度是 D 维的。 在某一点上的梯度,比如 ∇f(1, 2), 就是把那个点的数字代进去后得到的向量。

以第 2 节里的 f(x, y) = x² + 3xy + y³ 为例:

  ∂f/∂x   =  2x + 3y
  ∂f/∂y   =  3x + 3y²

  ∇f       =  ( 2x + 3y,  3x + 3y² )

  ∇f(1, 2) =  ( 2·1 + 3·2,  3·1 + 3·4 )
           =  ( 8, 15 )

在点 (1, 2),梯度是二维向量 (8, 15)。 这个向量有两个性质很特殊,都对 ML 至关重要:

  • 方向。∇f 指向「f 上升最快」的方向。 想象在一片光滑地形上放一颗弹珠,然后把重力反过来:它会沿着哪条方向往上滚 —— 那就是梯度的方向。
  • 大小。范数 ‖∇f‖ 告诉你, 沿那个最佳方向上升的速度有多快。 梯度短,意味着地形几乎是平的;梯度长,意味着这里很陡。

「上升最快的方向」这个性质,正是梯度为什么能驱动 ML 的核心理由。 损失函数是一片输入维度高达几十亿的地形;我们想找一个低点。 我们先算梯度 —— 它指向「上升最快」的方向 —— 再朝反方向走。 这就是 梯度下降:

w ← w − η · ∇f(w)

这一行公式值得逐字读 —— 它是所有曾经训练过的神经网络背后的核心算法。 当前参数向量 w 朝着「梯度的反方向」走。 标量 η(eta,即 学习率)控制每一步走多大。 太小,训练慢得受不了;太大,会越过去甚至发散。 选一个合适的 η,把这条更新重复几百万次, 参数就会落在「损失的一个极小值」附近。

f(x, y) = x² + y² · 等高线∇f站在 (1.5, 0.8) 处。∇f = (2x, 2y) = (3.0, 1.6) 指向上坡,远离原点。
1 / 4
梯度指向远离极小点的方向;梯度下降让标记朝相反方向走。

三个值得记住的实用事实:

  • 在一个 极小点(或任意「平地」)处,梯度是零向量 (0, 0, …, 0) —— 没有任何方向上坡,因为你已经在底部。优化器判断「收敛」的方式之一, 就是看梯度的大小是不是已经趋于 0。
  • 梯度始终 f 的等高线垂直 —— 等高线就是「f 保持不变」的那些线。沿着等高线走的人既不上山也不下山; 而梯度指的是「上山方向」,所以它必然以 90° 切过这些线。
  • 对于「D 个输入 → D 个输出」的函数(而不是只有一个标量输出), 梯度的自然推广是一个叫 雅可比矩阵(Jacobian) 的矩阵 —— 每个输出一行,每个输入一列。梯度恰好就是「输出是一个标量」时的特例, 雅可比就退化成单单一行。

在 Transformer 里:训练归结成反复做两件事。正向: 从一批样本算出损失 L。反向:算出 ∇L —— 一个长达几十亿项的向量, 每个参数一项。然后做一步 w ← w − η · ∇L(通常会包装成 Adam、Lion 之类,但骨架完全一样)。 所有有意思的深度学习话题 —— 动量、权重衰减、学习率调度、梯度裁剪、混合精度训练 —— 都不过是这一条更新规则的修饰。梯度是中心对象:线性代数(第一篇 primer) 给了它生存的空间;概率(第二篇)给了它要逼近的损失; 链式法则(上一节)给了真正能算出它的算法。