跳转至

讲解稿 · 第 03 章第 05 节:优化

口播时长:约 15–20 分钟|建议语速:中速,公式处停顿两拍|字数:约 4300 字


导语(约 80 秒)

先给你一个画面。

你站在一座起雾的山坡上,目标是走到最低点。你看不见整座山,也没有地图。你只能用脚感受当前位置往哪个方向下降最快,然后迈一步,再感受一次。步子太小,半天走不到;步子太大,可能直接跨过谷底,在两边来回震荡。

训练机器学习模型时,计算机面对的就是这座山。山的高度是损失函数,你站的位置是模型参数,向下迈步就是更新参数。区别只在于:真实神经网络的“山坡”不是二维或三维,而可能有几百万甚至几十亿个方向。

比如损失函数

\[ f(x, y) = (x-3)^2 + (y+1)^2 \]

把当前位置取成 \((0, 0)\),损失是 \(10\);最低点在 \((3, -1)\),损失是 \(0\)。优化要回答的不是“最低点长什么样”,而是:只利用当前位置的局部信息,怎样一步步走到那里?

这正是这一节要解决的问题。前面四节我们学会了求导、求梯度、做泰勒近似,今天我们把它们用到极致。


复习昨日 & 今日路线(约 50 秒)

上一节讲了函数逼近:用泰勒级数把复杂函数换成多项式。今天你会看到,一阶泰勒给出梯度下降,二阶泰勒给出牛顿法。这不是巧合,是同一套工具的不同用途。

再回引 Ch02 §04 的核心框架:线性映射可以由矩阵表示。今天我们会把它兑现到微积分里——导数描述局部线性变化,梯度和 Jacobian 就是这种线性映射的矩阵表达。

下一节将进入概率与统计的世界:那是从确定性的微积分跨入不确定性的第一步。


核心概念一:临界点只是候选人,不是获奖者(约 3 分钟)

1. 直觉

在一维曲线上,谷底和山顶通常有一个共同特征:切线水平,瞬时斜率为零。

因此,找极值的第一步不是直接找最低点,而是先找导数的零点。不过这里容易踩坑:斜率为零,只说明这一刻变平了,不保证函数随后掉头。

2. 定义与定理边界

\(f'(c) = 0\) 时,\(c\) 称为函数 \(f\) 的临界点 (critical point)。例如对

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

有

\[ f'(x) = 3x^2 - 3 = 3(x-1)(x+1) \]

所以临界点是 \(x = -1\) 和 \(x = 1\)。

这是定义:它规定“临界点”这个名字怎样使用,不需要从其他结论推出。

接着使用二阶导数检验定理。若 \(f'(c) = 0\),那么:

  • \(f''(c) > 0\),则 \(c\) 是局部极小点;
  • \(f''(c) < 0\),则 \(c\) 是局部极大点;
  • \(f''(c) = 0\),检验没有结论。

这三条是从导数理论推出的定理,不是独立定义,更不是公理。

3. 例子

继续算刚才的函数:

\[ f''(x) = 6x \]

在 \(x = -1\) 处,

\[ f''(-1) = -6 < 0,\qquad f(-1) = 2 \]

所以 \(x = -1\) 是局部极大点。

在 \(x = 1\) 处,

\[ f''(1) = 6 > 0,\qquad f(1) = -2 \]

所以 \(x = 1\) 是局部极小点。

黑板动作:画一条先升、后降、再升的三次曲线;在 \((-1, 2)\) 画水平切线并标“极大”,在 \((1, -2)\) 画水平切线并标“极小”。

4. 反例

取一个纯净反例:

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

它在 \(x = 0\) 满足 \(g'(0) = 0\),但 \(0\) 左右函数都保持递增,没有从降变升,也没有从升变降。因此 \(x = 0\) 不是极大点或极小点,而是水平拐点。再算

\[ g''(0) = 0 \]

这不表示“既是极大又是极小”,只表示二阶导数检验失效。

5. 一句话总结

导数为零负责筛选候选点,二阶导数负责判断局部形状。

学生常问

  • 问:不可导的点会不会也是极值点?
    答:会,例如 \(|x|\) 在 \(x = 0\) 取得极小值,但导数不存在,所以完整搜索还要检查不可导点和区间端点。
  • 问:\(f''(c) = 0\) 后怎么办?
    答:看一阶导数左右符号、更高阶导数,或直接比较附近函数值。

⏸️ 暂停思考 · 第一次

对 \(h(x) = x^4\),有 \(h'(0) = 0\)、\(h''(0) = 0\)。二阶检验失效,但 \(h(x) \geq 0 = h(0)\)。所以 \(x = 0\) 是什么点?

答案:全局极小点。请留意,“检验失效”不等于“没有极值”。


核心概念二:凸性决定局部低谷是否可信(约 3 分钟)

1. 直觉

把函数图像想成一只碗。若碗里没有暗坑,小球停下的位置就是整体最低处;若地形高低起伏,小球可能停在某个局部低谷,却错过更低的位置。

2. 定义与推论边界

函数 \(f\) 是凸函数 (convex function),是指对任意 \(x, y\) 和 \(t \in [0, 1]\),

\[ f(tx + (1-t)y) \leq t f(x) + (1-t) f(y) \]

例如取 \(f(x) = x^2\)、\(x = 0\)、\(y = 2\)、\(t = \frac12\):

\[ f(1) = 1 \leq \frac12 f(0) + \frac12 f(2) = 2 \]

几何上,这表示图像上两点之间的弦不落到图像下方。这是凸性的定义。

若二次可导且整个区间内 \(f''(x) \geq 0\),则 \(f\) 为凸函数;凸函数的每个局部极小点都是全局极小点。这些是由定义推出的判据与定理。

但凸函数不保证极小点唯一。例如常数函数 \(f(x) = 0\) 是凸函数,每个点都是全局极小点。只有再叠加严格凸等条件,才可保证至多一个极小点。

3. 例子

看二次函数

\[ f(x) = (x-3)^2 + 1 \]

它的一阶、二阶导数分别是

\[ f'(x) = 2(x-3),\qquad f''(x) = 2 > 0 \]

所以它严格凸。令 \(f'(x) = 0\) 得 \(x = 3\),并且

\[ f(3) = 1 \]

由于凸性,这不只是局部极小值,也是全局极小值。

4. 反例

取

\[ q(x) = (x^2 - 1)^2 \]

它不是凸函数,因为

\[ q''(0) = -4 < 0 \]

在 \(x = -1\) 和 \(x = 1\) 都有全局低谷,中间 \(x = 0\) 还有局部高点。这个反例只破坏凸性,不涉及不可导或边界问题。

5. 一句话总结

凸性保证局部极小可信;严格凸性进一步排除多个极小点。

学生常问

  • 问:看到 \(f''(c) > 0\),能否断言函数全局凸?
    答:不能;单点二阶导数只描述单点附近,凸性要求整个定义域满足条件。
  • 问:神经网络损失通常凸吗?
    答:通常不凸,因此实际训练只能依靠算法、初始化和经验寻找足够好的解。

核心概念三:梯度是局部线性映射,也是最陡上升方向(约 4 分钟)

1. 直觉

在二维山坡上,斜率不再只有一个。向东、向北、向东北走,高度变化都可能不同。我们需要一个对象,把所有方向的局部变化一次装进去,这就是梯度。

2. 定义,以及与 Ch02 的连接

对标量函数 \(f: \mathbb{R}^n \to \mathbb{R}\),梯度定义为

\[ \nabla f(\mathbf{x}) = \begin{pmatrix} \frac{\partial f}{\partial x_1}\\ \vdots\\ \frac{\partial f}{\partial x_n} \end{pmatrix} \]

\(\nabla f\) 读作“\(f\) 的梯度”,它由所有偏导数组成。数字例:若

\[ f(x, y) = x^2 + 3y^2 \]

则

\[ \nabla f(x, y) = \begin{pmatrix} 2x\\ 6y \end{pmatrix}, \qquad \nabla f(1, 2) = \begin{pmatrix} 2\\ 12 \end{pmatrix} \]

现在兑现 Ch02 §04 的“线性映射 = 矩阵”框架。微分把很小的输入变化 \(d\mathbf{x}\) 映射成输出变化 \(df\):

\[ df = J_f(\mathbf{x})\,d\mathbf{x} = \nabla f(\mathbf{x})^\top d\mathbf{x} \]

这里 \(J_f\) 是 \(f\) 的 Jacobian 行向量,形状为 \(1 \times n\);梯度默认是列向量,形状为 \(n \times 1\),所以

\[ J_f = \nabla f^\top \]

例如在 \((1, 2)\) 处取 \(d\mathbf{x} = (0.01, -0.02)^\top\):

\[ df \approx \begin{pmatrix} 2 & 12 \end{pmatrix} \begin{pmatrix} 0.01 \\ -0.02 \end{pmatrix} = 0.02 - 0.24 = -0.22 \]

这就是一次 \(1 \times 2\) 乘 \(2 \times 1\) 的矩阵乘法。单变量时更简单:

\[ dy = f'(x)\,dx \]

其中 \(f'(x)\) 是 \(1 \times 1\) 矩阵。于是“导数是斜率”和“导数是局部线性映射”并不冲突:前者是一维直觉,后者是能推广到高维的表达。Ch02 矩阵乘法框架今天终于在微积分里落地。

3. 例子:梯度下降

由于梯度指向最陡上升方向,要下降就取反方向:

\[ \mathbf{x}_{k+1} = \mathbf{x}_k - \eta\, \nabla f(\mathbf{x}_k) \]

\(\eta\) 是学习率 (learning rate),表示每次沿负梯度走多远。数字例:再考虑

\[ f(x, y) = (x-3)^2 + (y+1)^2 \]

有

\[ \nabla f(x, y) = \begin{pmatrix} 2(x-3)\\ 2(y+1) \end{pmatrix} \]

从 \(\mathbf{x}_0 = (0, 0)^\top\) 出发,取 \(\eta = 0.1\):

Step 1:算梯度

\[ \nabla f(\mathbf{x}_0) = (-6, 2)^\top \]

Step 2:更新参数

\[ \mathbf{x}_1 = \begin{pmatrix} 0 \\ 0 \end{pmatrix} - 0.1 \begin{pmatrix} -6 \\ 2 \end{pmatrix} = \begin{pmatrix} 0.6 \\ -0.2 \end{pmatrix} \]

Step 3:验算损失

\[ f(0, 0) = 10,\qquad f(0.6, -0.2) = 2.4^2 + 0.8^2 = 6.4 \]

损失从 \(10\) 降到 \(6.4\),方向正确 ✓。

黑板动作:画同心椭圆等高线,在 \((0, 0)\) 画梯度箭头,再画反向更新箭头指向 \((3, -1)\)。

4. 反例

学习率不是越大越快。对 \(r(x) = x^2\),更新式为

\[ x_{k+1} = x_k - 2\eta\, x_k = (1 - 2\eta)\, x_k \]

取 \(x_0 = 1\)、\(\eta = 1.1\):

\[ x_1 = -1.2,\qquad x_2 = 1.44 \]

绝对值越来越大,算法发散。函数仍然光滑、严格凸,唯一改变的是学习率过大,因此这是一个纯净反例。

5. 一句话总结

梯度把局部变化写成线性映射;负梯度给出局部最陡下降方向。

PyTorch autograd 形状卡

import torch

params = torch.tensor([0.0, 0.0], requires_grad=True)  # shape: [2]
target = torch.tensor([3.0, -1.0])                     # shape: [2]

loss = ((params - target) ** 2).sum()                  # shape: []
loss.backward()

print(params.grad)        # tensor([-6.,  2.]), shape: [2]

with torch.no_grad():
    params -= 0.1 * params.grad                        # [2] - scalar*[2] -> [2]
    params.grad.zero_()

loss 必须是标量,shape 是 [];params.grad 与 params 同形,都是 [2]。数学上 Jacobian 是 \(1 \times 2\) 行向量,而 PyTorch 对标量损失返回与参数同形的梯度 [2]。这是 Ch02 矩阵乘法在深度学习里的真实运行形态。

学生常问

  • 问:梯度是行向量还是列向量?
    答:本书默认梯度为列向量;标量函数的 Jacobian 是它的转置。
  • 问:一次更新后损失一定下降吗?
    答:不一定;学习率过大、函数不光滑或数值误差都可能使损失上升。

⏸️ 暂停思考 · 第二次

若当前位置梯度是 \((4, -2)^\top\),学习率 \(\eta = 0.25\),梯度下降的位移是多少?

位移是 \(-\eta \nabla f = (-1, 0.5)^\top\)。别把“梯度方向”和“下降方向”写成同一个方向。


核心概念四:牛顿法利用曲率,约束优化改变可走方向(约 3 分钟)

1. 直觉

梯度下降只问“坡往哪边下”,牛顿法还问“坡有多弯”。如果知道曲率,就能直接跳到二次近似的底部。

约束优化则反过来:不是我们走得不好,而是我们必须踩在一条曲线或曲面上,比如“在预算 \(x + y = 10\) 内找最高产出”。在这种情况下,我们需要拉格朗日乘子。

2. 定义与公式

牛顿法 (Newton's method) 用切线求零点:

\[ x_{n+1} = x_n - \frac{f(x_n)}{f'(x_n)} \]

用于优化(找极小)时换成

\[ x_{n+1} = x_n - \frac{f'(x_n)}{f''(x_n)} \]

多维版本依赖 Hessian \(H\):

\[ \mathbf{x}_{n+1} = \mathbf{x}_n - H^{-1}(\mathbf{x}_n)\, \nabla f(\mathbf{x}_n) \]

拉格朗日乘子法解约束优化:

\[ \min f(x, y)\quad \text{s.t.}\quad g(x, y) = c \]

定义拉格朗日函数:

\[ \mathcal{L}(x, y, \lambda) = f(x, y) - \lambda(g(x, y) - c) \]

最优点处三个偏导都为零:

\[ \frac{\partial \mathcal{L}}{\partial x} = 0,\quad \frac{\partial \mathcal{L}}{\partial y} = 0,\quad \frac{\partial \mathcal{L}}{\partial \lambda} = 0 \]

几何上,最优处 \(f\) 和 \(g\) 的梯度必须平行——否则沿约束曲面还能继续让 \(f\) 变化。

3. 例子:求 \(\sqrt{5}\)

取 \(f(x) = x^2 - 5\),\(f'(x) = 2x\)。从 \(x_0 = 2\) 开始:

Step 1:

\[ x_1 = 2 - \frac{2^2 - 5}{2 \cdot 2} = 2 - \frac{-1}{4} = 2.25 \]

Step 2:

\[ x_2 = 2.25 - \frac{2.25^2 - 5}{2 \cdot 2.25} = 2.25 - \frac{0.0625}{4.5} \approx 2.2361 \]

Step 3:验算:

\[ 2.2361^2 = 5.0001\ldots \]

已经精确到小数点后四位 ✓。这就是二次收敛——每走一步,正确的十进制位数大约翻一倍。

4. 例子:拉格朗日乘子手算

约束 \(x^2 + y^2 = 1\),求 \(f(x, y) = x^2 y\) 的极大。构造

\[ \mathcal{L} = x^2 y - \lambda(x^2 + y^2 - 1) \]

求偏导:

\[ 2xy - 2\lambda x = 0,\quad x^2 - 2\lambda y = 0,\quad x^2 + y^2 = 1 \]

由第一个方程(假设 \(x \neq 0\))得 \(\lambda = y\);代入第二个得 \(x^2 = 2y^2\);再结合约束 \(2y^2 + y^2 = 1\),解得

\[ y = \frac{1}{\sqrt{3}},\qquad f = \frac{2}{3\sqrt{3}} \]

黑板动作:画单位圆,画函数梯度、约束梯度在极值点处的平行关系。

5. 反例(纯净)

牛顿法在初值过远或 \(f'(x)\) 过小时会翻车。取

\[ h(x) = x^{1/3},\qquad h(0) = 0 \]

它满足 \(h(0) = 0\),但 \(h'(0)\) 不存在。直接套用牛顿公式会除以零或得到不收敛的迭代。这个反例只破坏导数存在性,不掺杂数值误差。

6. 一句话总结

牛顿法靠曲率(Hessian)走大步;拉格朗日乘子把约束翻译成梯度平行条件。

⏸️ 暂停思考 · 第三次

牛顿法求 \(x^2 - 2 = 0\) 的根,从 \(x_0 = 1.5\) 出发。下一步 \(x_1\) 是多少?手算一下。

答案:\(x_1 = 1.5 - (1.5^2 - 2)/(2 \cdot 1.5) = 1.5 - 0.25/3 \approx 1.4167\)。真实 \(\sqrt{2} \approx 1.4142\),一步就接近到 0.002。


一个走完全程的例子(约 2.5 分钟)

好,咱们用一个例子把今天的概念全串起来。

问题:用梯度下降找 \(f(x, y) = (x-3)^2 + (y+1)^2\) 的最小值,最低点在 \((3, -1)\),最小值是 \(0\)。

第一步,画等高线。这是同心圆,圆心在 \((3, -1)\)。我们从 \(\mathbf{x}_0 = (0, 0)^\top\) 出发,这位于最大圆上。

第二步,初始化。取学习率 \(\eta = 0.1\),损失 \(f(0, 0) = 10\)。

第三步,迭代 5 步(用更新式 \(\mathbf{x}_{k+1} = \mathbf{x}_k - \eta\, \nabla f(\mathbf{x}_k)\)):

步 位置 损失
0 (0.0000, 0.0000) 10.0000
1 (0.6000, -0.2000) 6.4000
2 (1.0800, -0.3600) 4.0960
3 (1.4640, -0.4880) 2.6214
4 (1.7712, -0.5904) 1.6777
5 (2.0170, -0.6723) 1.0737

每步损失都下降 ✓,参数稳定向 \((3, -1)\) 收敛。

第四步,验证凸性。$$ f''(x, y) = \begin{pmatrix} 2 & 0 \ 0 & 2 \end{pmatrix} $$,特征值都是 \(2 > 0\),严格凸,所以找到的极小一定是全局极小,没有“骗人低谷”。

第五步,连到牛顿法。如果换成牛顿法多走一步:

\[ \mathbf{x}_1 = \mathbf{x}_0 - H^{-1} \nabla f(\mathbf{x}_0) = \begin{pmatrix} 0 \\ 0 \end{pmatrix} - \begin{pmatrix} 0.5 & 0 \\ 0 & 0.5 \end{pmatrix} \begin{pmatrix} -6 \\ 2 \end{pmatrix} = \begin{pmatrix} 3 \\ -1 \end{pmatrix} \]

一步就跳到精确最小点。这就是曲率带来的加速。

整套动作串起来:从一阶梯度(梯度下降)→ 二阶梯度(牛顿法)→ 二阶导数矩阵(Hessian)决定凸性。


易错点回头看(约 80 秒)

三条最常翻车的地方。

  • 很多人以为:导数为零的点就是极值点。其实是:仅是临界点,可能是极大、极小或拐点。因为二阶导数还控制着附近是上升还是下降。数字反例:\(x^3\) 在 \(x = 0\) 处 \(f'(0) = 0\),但那是水平拐点。

  • 很多人以为:凸函数必有唯一全局极小。其实是:凸函数保证局部极小即全局极小,但常数函数每个点都是极小。因为“局部极小即全局”只排除“骗人低谷”,不排除“平坦底部”。要把“唯一”也写进来,必须额外要求严格凸(\(f'' > 0\))。

  • 很多人以为:学习率越大收敛越快。其实是:过大会发散,过小会停滞。因为更新幅度 = 学习率 × 梯度,梯度大时小步也走得远。数字反例:\(r(x) = x^2\),\(\eta = 1.1\) 时 \(x_k\) 绝对值越滚越大,参数震荡发散。


与下节的钩子(约 30 秒)

今天我们讲了优化的主力算法:一阶梯度下降、二阶牛顿法,以及带约束的拉格朗日乘子。所有这些都假设我们能精确算出梯度。

但是在深度学习里,模型有几百万参数,损失函数是几百层复合,单个梯度算起来代价不菲。下一节——概率与统计——我们会看到不确定性怎样进入优化:噪声怎样影响梯度、随机梯度下降 (SGD) 为什么要“随机”、Adam 又怎样在每个参数上自适应地调整学习率。带着这些连接,咱们下节见。