为什么 ML 离不开它
机器学习的训练过程,从最朴素的线性回归到 GPT,本质上都是同一件事:
定义一个表示「模型预测有多错」的数 ,然后调整参数让它变小。
「调整参数让它变小」听起来抽象——但只要你能算出 对每个参数的导数,你就知道每个参数往哪个方向动会让 变小。整个深度学习的训练循环就两步:
- 算损失 对所有参数的梯度 。
- 把参数往 方向挪一小步。
这就是梯度下降。理解它需要的微积分,只是导数和链式法则两件事的几何化。这篇我们就把它们画出来。
导数:一点的斜率
导数 就是函数 在 这个点的切线的斜率。
定义式:
公式里的「极限」「无穷小」让人头大,但几何上就是一句话:把切线斜率代数化。
拖动下面的圆点到曲线的不同位置——观察切线(虚线)的倾斜方向如何变化,以及右侧显示的导数值如何从正变负:
函数
f(x) = 0.3·(x³ − 2x)
位置
x = -1.30
f(x) = 0.12
导数(斜率)
f′(x) = 0.92
正 → 这里在上升
几个值得留意的事实:
- 导数 大于 0 → 函数在这里上升(往右走 增加)。
- 导数 小于 0 → 函数在这里下降。
- 导数 等于 0 → 切线水平 → 极大值、极小值或者鞍点。
最后一条是优化的起点:光滑函数的最小值必然满足 。但反过来不成立—— 的地方也可能是极大值或鞍点。在深度学习里,高维 loss landscape 中 的地方绝大多数是鞍点(某些方向上升、某些方向下降),而不是真正的最小值。好消息是:实践中梯度下降几乎总能找到足够好的低点——虽然不一定是全局最小。
还有一个微妙之处:ML 里广泛使用的 ReLU 激活函数在 处不可导(有折角)。实际中我们用次梯度(取左右导数之间的任意值,通常取 0)来绕过这个问题——PyTorch 默认就是这么做的。
梯度:上山最陡的方向
ML 里的损失函数不是 1 维的,而是依赖几千万、几亿个参数。一个 元函数 没有「斜率」这种单一的数——但它有 个偏导数:
几何意义:想象你站在一片高维山坡上。「假装其他参数全冻住,只动 」——等于用一把刀沿平行于 轴的方向把山坡切开,你看到的那条 2D 切面的斜率,就是偏导数 。
把所有方向的偏导数打包成一个向量,就是梯度:
这就是这篇最关键的一句话:
梯度 指向 在当前位置上升最快的方向,大小是上升的速率。
所以反过来, 就是下降最快的方向——这就是「下山最陡的路」。
为什么是「最陡」?沿任意方向 (单位向量)走一小步, 的变化率是 (上一篇讲的点积)。点积在两个向量方向一致时最大,所以 时上升最快, 时下降最快。
还有一个免费的几何事实:梯度永远垂直于等高线。等高线上 不变(),而 正好意味着 和等高线的切方向垂直。这一点在下面的动图里直接能看到——注意轨迹的每一步都垂直于它穿过的等高线。
梯度下降:下山的算法
知道了「最陡的下山方向」,算法就只剩一行:
是学习率(learning rate)——一步迈多大。
但「迈多大」是个真问题。试试下面的交互:点击画布换起点,拖动学习率滑块。特别试试这几个值:
- 把 调到很小(~0.02):轨迹像蚂蚁爬,一万步可能还没到谷底。
- 把 调到适中(~0.1):轨迹平滑收敛到中心。
- 把 调大(>0.5):轨迹开始在山谷两侧来回弹射,甚至越冲越远——这就是训练中常见的发散(divergence)。
损失函数
L(x, y) = 0.3·x² + 1.5·y²
椭圆形山谷,最小值在原点(紫色圆心)
当前轨迹
收敛到最小值
L(最终) = 0.000
✦ 点击画布换起点
✦ 拖动滑块改学习率
✦ 路径每次重新跑 30 步
还有一个现象比学习率更深刻——注意上面这个椭圆形山谷:轨迹经常是 Z 字形的。
为什么?因为梯度永远垂直于等高线。当等高线是被拉长的椭圆时,它的法线(垂直方向)大部分时候并不指向椭圆的中心(极小值点),而是指向山谷的对岸。这就迫使梯度下降像一个醉汉一样,在两壁之间来回撞击(Z 字形),而不是沿着谷底直奔终点。
数学上,这对应损失函数的 Hessian 矩阵条件数很大(最大特征值 / 最小特征值 ≫ 1)——不同方向的曲率差异极大。这是普通梯度下降的根本病灶,也是后来 Momentum、Adam 等优化器要解决的核心问题——优化器篇会详细展开。
链式法则:把误差传回去
神经网络是函数的层层嵌套。一个三层网络的输出大概长这样:
想算 对最深一层参数 的偏导,得穿过 和 才能到 。链式法则就是这件事的数学:
代数上是乘法链。几何上有一个更直观的读法:每个偏导数是那一层的局部拉伸率——如果 把 方向的变化放大了 2 倍, 又放大了 3 倍,那么端到端的放大倍数就是 。链式法则就是「逐层拉伸率的连乘」。
把它读作「敏感度的传递」也可以: 抖一下 → 抖一下 → 跟着抖 → 跟着抖 → 跟着抖。每一段的「抖动放大倍数」就是局部偏导,乘起来就是端到端的敏感度。
这就是**反向传播(backpropagation)**的全部秘密——从输出端往输入端,把局部导数一段一段乘回去。PyTorch、JAX 这些框架的核心就是:自动帮你把这条乘法链算出来。
一个实际后果:如果每层的局部导数都小于 1(比如 sigmoid 的最大导数是 0.25),连乘几十层之后梯度会指数衰减到接近零——深层参数几乎收不到学习信号。这叫梯度消失,是早期深度网络训不动的主要原因。ReLU(导数 = 0 或 1)和残差连接(让梯度有一条"直通车")就是为了解决这个问题。