微积分 入门
读懂任何深度学习论文之前所需的最少微积分。4 个短小主题,涵盖「导数到底是什么」、 多变量情况下的偏导数、为什么链式法则是反向传播的核心、以及梯度如何把所有偏导数 打包成一支指向「上坡方向」的箭头。不讲积分 —— 训练一个百亿参数模型, 从头到尾用不到一次积分。
导数(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 —— 正在下降。 一个公式同时给出了曲线上每一点的「上升速率」。
一套简短的求导规则,足以应付 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 真正用来在大规模上算出这些导数的机器。
偏导数(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。
「切片」就是它的直觉图像。把 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ᵢ。 每一个偏导都在回答「我把这个权重往上推一丁点,损失会上升还是下降?」 下一节的链式法则,就是让这「几十亿个偏导一次性算出来」这件事真的可行的关键。
链式法则(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/dx。N 个串起来?乘 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 节更深的微积分。
多变量版本,就是支撑整个深度学习的那个链式法则。若 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。 一次前向 + 一次反向 = 一步训练。链式法则不是花边小知识 —— 从机制上说,它就是深度学习之所以可行的全部理由。
梯度(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,即 学习率)控制每一步走多大。 太小,训练慢得受不了;太大,会越过去甚至发散。 选一个合适的 η,把这条更新重复几百万次, 参数就会落在「损失的一个极小值」附近。
三个值得记住的实用事实:
- 在一个 极小点(或任意「平地」)处,梯度是零向量
(0, 0, …, 0)—— 没有任何方向上坡,因为你已经在底部。优化器判断「收敛」的方式之一, 就是看梯度的大小是不是已经趋于 0。 - 梯度始终 与
f的等高线垂直 —— 等高线就是「f保持不变」的那些线。沿着等高线走的人既不上山也不下山; 而梯度指的是「上山方向」,所以它必然以 90° 切过这些线。 - 对于「D 个输入 → D 个输出」的函数(而不是只有一个标量输出), 梯度的自然推广是一个叫 雅可比矩阵(Jacobian) 的矩阵 —— 每个输出一行,每个输入一列。梯度恰好就是「输出是一个标量」时的特例, 雅可比就退化成单单一行。
在 Transformer 里:训练归结成反复做两件事。正向: 从一批样本算出损失 L。反向:算出 ∇L —— 一个长达几十亿项的向量, 每个参数一项。然后做一步 w ← w − η · ∇L(通常会包装成 Adam、Lion 之类,但骨架完全一样)。 所有有意思的深度学习话题 —— 动量、权重衰减、学习率调度、梯度裁剪、混合精度训练 —— 都不过是这一条更新规则的修饰。梯度是中心对象:线性代数(第一篇 primer) 给了它生存的空间;概率(第二篇)给了它要逼近的损失; 链式法则(上一节)给了真正能算出它的算法。