☰
多元泰勒公式如何驱动AI优化:从梯度下降到海森矩阵
2026/10/17 4:39:30 网站建设 项目流程

1. 从一元到多元:AI优化为什么绕不开泰勒公式

1.1 一个直观场景:梯度下降的方向从哪来

做机器学习的朋友应该都有过这种经历:第一次接触梯度下降时,PPT上画了一个碗状的损失曲面,然后告诉你“沿着负梯度方向走,就能走到最低点”。我当时的第一反应是:凭什么?为什么负梯度方向就一定是下降方向?往左走一点、往右偏一点,不也是能下降吗?这个“负梯度”到底是怎么从函数本身算出来的?

答案是泰勒公式。严格说,是多元函数的一阶泰勒展开。

设当前模型参数为 (w_0) ,损失函数为 (L(w)) ,我们想知道参数变成 (w_0 + \delta) 之后损失大约是多少。一阶多元泰勒展开给出的近似是:

[ L(w_0 + \delta) \approx L(w_0) + \nabla L(w_0)^T \delta ]

也就是说,损失的变化量大致是梯度向量和参数变化量的点积。想让损失下降,就要让 (\nabla L(w_0)^T \delta < 0)。如果取 (\delta = -\eta \nabla L(w_0)),那么变化量就是 (-\eta ||\nabla L(w_0)||^2),只要学习率 (\eta > 0),这个值就一定小于零。

换句话说,梯度下降根本不是“一拍脑袋想出来的经验法则”,它是多元泰勒公式在一阶截断后推导出的必然选择。你一旦接受了这个视角,后面理解牛顿法、动量法、Adam这些优化算法时会顺很多,因为它们都是在同一个泰勒展开框架下做了不同阶数的截断和修正。

1.2 一元泰勒公式:把复杂函数“拆”成多项式

在进入多元版本之前,先把一元情况理清楚。一个足够光滑的一元函数 (f(x)),在点 (x_0) 附近可以展开为:

[ f(x) = f(x_0) + f'(x_0)(x - x_0) + \frac{f''(x_0)}{2!}(x - x_0)^2 + \cdots + \frac{f^{(n)}(x_0)}{n!}(x - x_0)^n + R_n(x) ]

这个式子的核心思想是:任何足够光滑的函数,在某个点附近都能用一个多项式去逼近,系数由函数在该点的各阶导数决定。

我这里强调“附近”两个字,是因为泰勒展开本质上是一个局部性质。离展开点越远,多项式逼近的效果通常越差,除非函数本身就是多项式。余项 (R_n(x)) 就是用来兜底的,它刻画了截断到第 (n) 阶之后剩余的那部分误差。

刚才说的这些,很多教材里都有。但真正到了AI场景,你会发现一个常见误区:很多人只记得“展开”,不记得“在展开点附近”这个前提条件。后文专门有一节讲这个坑,这里先埋个伏笔。

从一元到多元,困难的地方不是定理本身,而是符号系统和计算复杂度。一元函数只需要一个导数,多元函数会冒出一个梯度向量和一个海森矩阵,记号一下子变得繁琐。但只要你抓住“函数在点附近的多项式近似”这个核心思想,多元展开不过是把“导数”换成“梯度”,把“二阶导数”换成“海森矩阵”而已。

2. 多元泰勒公式的完整展开:偏导数、梯度和海森矩阵

2.1 多元函数展开的整体框架

设 (f: \mathbb{R}^n \to \mathbb{R}) 是一个多元实值函数,在点 (\mathbf{x}0 = (x{10}, x_{20}, \dots, x_{n0})) 的某个邻域内有足够的连续偏导数。那么 (f) 在 (\mathbf{x}_0) 附近的二阶泰勒展开,也就是深度学习里最常用的形式,可以写成:

[ f(\mathbf{x}) \approx f(\mathbf{x}0) + \sum{i=1}^n \frac{\partial f(\mathbf{x}0)}{\partial x_i} (x_i - x{i0}) + \frac{1}{2} \sum_{i=1}^n \sum_{j=1}^n \frac{\partial^2 f(\mathbf{x}0)}{\partial x_i \partial x_j} (x_i - x{i0})(x_j - x_{j0}) ]

初次看到这个式子的人,往往会觉得“变量一多就乱了”。我的建议是别急着背求和号,先看它的结构:

  • 第零项 (f(\mathbf{x}_0)):函数在当前点的取值,常数项。
  • 第一项(线性项):一阶偏导数乘以对应的位移,本质是梯度向量与位移向量的内积。
  • 第二项(二次项):所有二阶偏导数乘以两两位移的乘积,本质是位移向量的二次型。

这个结构可以说非常整齐:零阶项是标量,一阶项是向量内积,二阶项是矩阵二次型。每一次升阶,信息量都从一个数变成一个向量再变成一个矩阵,恰好对应了梯度向量和海森矩阵的引入。

2.2 二阶展开的矩阵形式

上面那个求和符号版本,虽然严谨,但不好用。把它写成矩阵形式,理解效率能提升一个档次:

[ f(\mathbf{x}) \approx f(\mathbf{x}_0) + \nabla f(\mathbf{x}_0)^T (\mathbf{x} - \mathbf{x}_0) + \frac{1}{2} (\mathbf{x} - \mathbf{x}_0)^T \mathbf{H}_f(\mathbf{x}_0) (\mathbf{x} - \mathbf{x}_0) ]

其中:

[ \nabla f(\mathbf{x}0) = \left[ \frac{\partial f}{\partial x_1}, \frac{\partial f}{\partial x_2}, \dots, \frac{\partial f}{\partial x_n} \right]^T \Bigg|{\mathbf{x} = \mathbf{x}_0} ]

是梯度向量,而:

[ \mathbf{H}_f(\mathbf{x}0) = \begin{bmatrix} \frac{\partial^2 f}{\partial x_1^2} & \frac{\partial^2 f}{\partial x_1 \partial x_2} & \cdots & \frac{\partial^2 f}{\partial x_1 \partial x_n} \ \frac{\partial^2 f}{\partial x_2 \partial x_1} & \frac{\partial^2 f}{\partial x_2^2} & \cdots & \frac{\partial^2 f}{\partial x_2 \partial x_n} \ \vdots & \vdots & \ddots & \vdots \ \frac{\partial^2 f}{\partial x_n \partial x_1} & \frac{\partial^2 f}{\partial x_n \partial x_2} & \cdots & \frac{\partial^2 f}{\partial x_n^2} \end{bmatrix} \Bigg|{\mathbf{x} = \mathbf{x}_0} ]

是海森矩阵(Hessian Matrix)。

这个写法最大的好处是:形式上一元和多元完全统一。一元时海森矩阵退化成一个常数 (f''(x_0)),二次项退化成一个数乘上 ((x-x_0)^2)。你只需要记住“一元是多元的特例”,而不是把两套公式分开背。

2.3 为什么二阶项是矩阵而不是一个数

我见过不少初学者在这里卡住:明明是一长串的和,怎么突然变成一个矩阵了?为什么要用二次型表示?

回忆线性代数里的一个基本事实:对于一个对称矩阵 (\mathbf{A}),表达式 (\mathbf{u}^T \mathbf{A} \mathbf{u}) 是一个二次型,展开后就是:

[ \sum_{i,j} a_{ij} u_i u_j ]

这个结构天然适合表示“两个方向的位移相乘再求和”。而多元函数的二阶泰勒项,恰恰就是所有二阶偏导数乘以两个位移分量的乘积之和。

具体来说,(\mathbf{A}_{ij}) 对应 (\partial^2 f / \partial x_i \partial x_j),(\mathbf{u}i) 对应 ((x_i - x{i0}))。只要海森矩阵是实对称的(Clairaut定理保证:当二阶偏导数连续时,混合偏导数与求导顺序无关),二次型就完全描述了二阶展开的全部信息。

从AI的角度看,这个矩阵还提供了一个额外的解读:它的特征值和特征向量刻划了损失曲面在某点的“弯曲程度”和“弯曲方向”。最大特征值对应的特征向量方向,是曲面弯曲最剧烈的方向;最小特征值对应的方向,是曲面相对平坦的方向。这一点在分析优化收敛性和设计二阶优化算法时会反复用到。

3. 三大AI应用落点:梯度、牛顿法与损失曲面

3.1 梯度下降就是一阶泰勒展开的贪心策略

前面说了,一阶泰勒展开给了我们损失变化量的近似公式。那么问题是:在展开点附近,怎么选一个方向走,让损失下降最快?

把一阶项写成夹角形式:

[ \nabla L(\mathbf{x}_0)^T \delta = ||\nabla L(\mathbf{x}_0)|| \cdot ||\delta|| \cdot \cos\theta ]

其中 (\theta) 是梯度向量和位移向量的夹角。固定步长 (||\delta||),要让这个内积最小,就要让 (\cos\theta = -1),也就是 (\delta) 和梯度方向完全相反。

这就是为什么梯度下降的更新规则是 (\mathbf{x} \leftarrow \mathbf{x} - \eta \nabla L(\mathbf{x})),而不是其他方向。

但这里面有个隐藏的问题:梯度下降只用了一阶信息,所以它默认损失函数是近似线性的。当真正的高阶项(尤其是二阶曲率项)比较大时,沿着负梯度方向走很快一段就会“偏离航线”,表现为损失下降不稳定、来回震荡,甚至发散。这也是为什么后面发展出了带动量的方法:动量相当于把过去几步的方向信息叠加起来,在某种程度上补偿了二阶曲率带来的偏差,但不是真正利用二阶信息。

3.2 牛顿法:借用二阶泰勒展开找极值

如果你不满足于一阶近似,想把曲率信息也用上,做法就变成了:对 (f(\mathbf{x})) 在 (\mathbf{x}_0) 附近做二阶泰勒展开,然后直接对这个二次近似函数求极小值点。

二阶近似的极值条件是对 (\delta) 求导并令其为零:

[ \frac{\partial}{\partial \delta} \left[ f(\mathbf{x}_0) + \nabla f(\mathbf{x}_0)^T \delta + \frac{1}{2} \delta^T \mathbf{H}_f(\mathbf{x}_0) \delta \right] = 0 ]

整理后得到:

[ \mathbf{H}_f(\mathbf{x}_0) \delta = -\nabla f(\mathbf{x}_0) ]

解出:

[ \delta = -\mathbf{H}_f(\mathbf{x}_0)^{-1} \nabla f(\mathbf{x}_0) ]

这就是牛顿法的更新方向。跟梯度下降相比,它多了海森矩阵的逆作为“预条件”。直观理解:梯度下降在所有方向上用同一个学习率,而牛顿法会让曲率大的方向走得慢一点、曲率小的方向走得快一点,相当于根据几何形状调整了每一步的方向和步长。

我当年第一次把牛顿法用在手写逻辑回归上时,最大的感触是:迭代次数真的少。同样的收敛精度,梯度下降可能要走几千步,牛顿法往往几十步就到位了。代价也很明显:需要计算海森矩阵并求逆,当参数维度到几千甚至上万时,这一步的计算量和内存开销会呈平方级增长,在深度学习的参数规模下几乎不可承受。

3.3 凸性与海森矩阵:为什么二阶信息有时比一阶更高效

还有一个值得专门说清楚的点:海森矩阵不仅用于牛顿法,还用来判断函数的凸性。

一个二次可微的多元函数 (f) 在凸集上是凸函数,当且仅当它的海森矩阵在整个定义域上都是半正定的。换句话说,海森矩阵是正定时,局部极小值就是全局极小值;海森矩阵有负特征值时,说明函数在该方向上是凹的,当前驻点可能是个鞍点。

这一点在深度学习中尤其扎心。神经网络损失函数的海森矩阵几乎不可能是正定的,反而会在参数空间里出现大量鞍点。早期有个经典研究观点认为,高维损失曲面上鞍点的数量远超局部极小值,而梯度下降在鞍点附近会表现出“假性停滞”——梯度很小但并不是极小值。后来的人用随机扰动、动量和自适应学习率去缓解这个问题,本质上都是在跟“海森矩阵有负特征值”这个几何事实做对抗。

所以我的建议是:分析优化算法的时候,脑子里始终带着多元泰勒公式的这层几何图像。一阶信息只告诉你“哪里陡”,二阶信息才告诉你“怎么弯”。停在陡坡上还是停在谷底,这两者的区分必须靠二阶信息。

4. 动手计算:二元函数的泰勒展开与可视化

4.1 手算一个具体例子

理论讲再多,不如亲手算一个。取一个简单的二元函数:

[ f(x, y) = x^2 + 3y^2 + 2xy + e^{x-y} ]

在点 (\mathbf{x}_0 = (0, 0)) 处做二阶泰勒展开。

第一步,算函数值和一阶偏导数:

[ f(0,0) = 0 + 0 + 0 + e^0 = 1 ]

[ \frac{\partial f}{\partial x} = 2x + 2y + e^{x-y}, \quad \frac{\partial f}{\partial x}\bigg|_{(0,0)} = 0 + 0 + 1 = 1 ]

[ \frac{\partial f}{\partial y} = 6y + 2x - e^{x-y}, \quad \frac{\partial f}{\partial y}\bigg|_{(0,0)} = 0 + 0 - 1 = -1 ]

所以梯度向量是 ((1, -1)^T)。

第二步,算二阶偏导数:

[ \frac{\partial^2 f}{\partial x^2} = 2 + e^{x-y}, \quad \frac{\partial^2 f}{\partial x^2}\bigg|_{(0,0)} = 3 ]

[ \frac{\partial^2 f}{\partial y^2} = 6 + e^{x-y}, \quad \frac{\partial^2 f}{\partial y^2}\bigg|_{(0,0)} = 7 ]

[ \frac{\partial^2 f}{\partial x \partial y} = 2 - e^{x-y}, \quad \frac{\partial^2 f}{\partial x \partial y}\bigg|_{(0,0)} = 1 ]

注意这里混合偏导数只要算一个。

于是海森矩阵是:

[ \mathbf{H} = \begin{bmatrix} 3 & 1 \ 1 & 7 \end{bmatrix} ]

第三步,把所有结果代进二阶泰勒展开的矩阵形式:

[ f(x, y) \approx 1 + (1, -1) \begin{pmatrix} x \ y \end{pmatrix} + \frac{1}{2} (x, y) \begin{bmatrix} 3 & 1 \ 1 & 7 \end{bmatrix} \begin{pmatrix} x \ y \end{pmatrix} ]

展开整理:

[ f(x, y) \approx 1 + x - y + \frac{3}{2}x^2 + xy + \frac{7}{2}y^2 ]

这里特别想提醒一点:如果用手算,很多人会把混合偏导数项写成 (2xy) 或 (xy) 弄混。因为二次型展开后那个 (\frac{1}{2}) 会把对称矩阵的非对角元素各分一半,最终系数恰恰等于海森矩阵非对角元素本身。所以 ((x, y) \mathbf{H} (x, y)^T) 展开后混合项系数就是 (2 \cdot 1 = 2),再乘外面的 (\frac{1}{2}),最终是 (xy)。

4.2 近似精度对比

算完展开式之后,我们来验证一下近似效果。取几个距离原点不同的点,对比真实值和近似值:

点 ((x, y))真实值 (f(x,y))二阶展开近似值误差
((0.1, -0.1))(0.01 + 0.03 - 0.02 + e^{0.2}) ≈ 1.2434(1 + 0.1 + 0.1 + 0.015 - 0.01 + 0.035) ≈ 1.24000.0034
((0.5, -0.2))(0.25 + 0.12 - 0.2 + e^{0.7}) ≈ 2.3634(1 + 0.5 + 0.2 + 0.375 - 0.1 + 0.14) ≈ 2.11500.2484
((1.0, 0.5))(1 + 0.75 + 1 + e^{0.5}) ≈ 4.3987(1 + 1 - 0.5 + 1.5 + 0.5 + 0.875) ≈ 4.37500.0237

仔细观察上面这张表,你会发现误差的变化并不完全和“距离远”成正比。((1.0, 0.5)) 离原点比 ((0.5, -0.2)) 更远,但误差反而更小。原因是 (e^{x-y}) 这个非线性项在 (x-y) 方向上的高阶导数占比不同。这说明泰勒展开的误差不仅取决于离展开点的距离,还取决于函数本身的高阶项强度。

这个现象在AI场景里同样存在。损失曲面在参数空间的有些方向非常“平缓”,二阶近似很准;但有些方向上高阶非线性极强,二阶近似很快就失效。所以不能简单地说“展开点附近就够用”,必须结合具体函数的性质。

4.3 Python可视化:观察近似效果

我之前为了给学生讲清楚这件事,专门写了个小脚本,把原函数和二阶近似同时画出来。代码很简单,基本思路是生成网格点,分别计算两个函数值,然后叠加显示。

import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D def f_original(x, y): return x**2 + 3*y**2 + 2*x*y + np.exp(x - y) def f_approx(x, y): return 1 + x - y + 1.5*x**2 + x*y + 3.5*y**2 x = np.linspace(-1, 1, 50) y = np.linspace(-1, 1, 50) X, Y = np.meshgrid(x, y) Z_orig = f_original(X, Y) Z_approx = f_approx(X, Y) fig = plt.figure(figsize=(10, 5)) ax1 = fig.add_subplot(121, projection='3d') ax1.plot_surface(X, Y, Z_orig, cmap='viridis', alpha=0.8) ax1.set_title('Original Function') ax2 = fig.add_subplot(122, projection='3d') ax2.plot_surface(X, Y, Z_approx, cmap='plasma', alpha=0.8) ax2.set_title('Second-order Taylor Approximation') plt.tight_layout() plt.show()

从可视化结果能看得特别清楚:在原点附近,两个曲面几乎重合;往外走,尤其是沿着 (y) 轴方向和 (x-y) 方向走,差异开始变大。如果你把误差函数 ((Z_orig - Z_approx)^2) 画出来,会更明显地看到误差的“方向性”。

这种方向性误差在神经网络的可解释性研究里也很有价值。比如一些工作通过损失曲面的二阶近似来分析对抗样本的存在性——对抗扰动往往沿着海森矩阵最大特征值对应的方向,因为这个方向上的高阶非线性最强,一阶甚至二阶近似都很难覆盖。

5. 实践中的坑与经验:展开到几阶、怎么处理余项

5.1 余项的含义与常见形式

前面的展开式写的是“近似等于”,严格写法其实应该带上余项。一元函数的Lagrange余项是:

[ R_n(x) = \frac{f^{(n+1)}(\xi)}{(n+1)!}(x - x_0)^{n+1} ]

其中 (\xi) 介于 (x) 和 (x_0) 之间。多元情况下余项更复杂,常见的是积分型余项或Lagrange型余项。从实用角度,你不需要背全所有余项形式,但一定要理解三件事:

第一,余项的存在说明泰勒公式不是一个精确等式,它能控制的只是余项的数量级。如果展开到第 (k) 阶,余项通常是 (||\mathbf{x} - \mathbf{x}_0||^{k+1}) 这个量级。所以展开阶数越高,在展开点附近逼近越精细,但不代表全局都能用。

第二,余项系数和导数有关。如果函数的 (k+1) 阶偏导数很大,即使点离展开点很近,误差也可能很大。这就是为什么有些函数特别“难优化”——高阶导数太大,导致一阶梯度下降的步长很难选。

第三,在AI框架里,自动微分计算的是精确导数,不是泰勒展开的数值近似。但很多理论分析(比如学习率上限的推导、收敛率证明)其实是基于泰勒展开的。你看到论文里写着“assuming the loss is locally quadratic”,意思就是假设二阶泰勒展开足够精确。

5.2 展开点附近的局部有效性:一个亲测的教训

有一段时间我在调一个很小的神经网络做回归任务,损失函数总是某个区间上收敛很好、另一个区间上震荡明显。做了各种调参,效果都不理想。

后来我把损失函数沿某个固定方向切片画出来,才意识到问题所在:那个震荡区间上,损失曲面有一个幅度很大的高阶非线性成分,二阶泰勒展开在离当前参数点不远的地方就完全失效了。梯度下降在这个区域里走一步,实际下降量和一阶近似估计的下降量差得很远,于是步长被迫缩小,否则就发散。

这个例子给我的教训是:看到损失震荡,先别急着调学习率,可以做一个“泰勒近似有效性检测”。具体做法是:取当前参数 (\theta),沿负梯度方向迈一个小步 (\epsilon),分别计算真实损失值 (L(\theta - \epsilon \nabla L)) 和一阶近似值 (L(\theta) - \epsilon ||\nabla L||^2),如果两者差距过大,说明当前区域内高阶项很强,要么减小步长,要么考虑在近似失效的方向上引入二阶修正。

我在实际项目中把这一步写成了调试脚本,每当训练表现异常时就跑一次,定位问题非常快。这个技巧比单纯看loss曲线要有效得多。

5.3 深度学习里为什么二阶方法难用

既然牛顿法看起来这么美,为什么现在主流的深度学习优化器仍然是Adam、SGD为主,而不是牛顿法?

核心原因是参数规模。一个现代神经网络动辄上亿参数,海森矩阵的规模是参数量的平方。存一个 (10^8 \times 10^8) 的矩阵,内存直接爆掉,这是现实约束。更别提求逆的复杂度了。

所以后来的研究方向分成了几条路:

一是拟牛顿法,比如L-BFGS,通过历史上有限次梯度信息近似海森矩阵的逆,避免显式构造矩阵。但即便这样,在超大模型和分布式训练场景下仍然不够方便。

二是对角近似和Kronecker分解方法,把海森矩阵近似成对角矩阵或块对角矩阵。Adam本质上可以看作海森矩阵对角近似的一种粗糙实现——它用梯度二阶矩估计来归一化每个参数的学习率,相当于只保留了海森矩阵对角线方向的信息。

三是使用“不动点迭代”和“二次模型”的思想来设计新型优化器,比如一些基于梯度裁剪策略的方法,本质上也是在控制泰勒展开高阶项带来的风险。

我的个人观点是:不管能不能用二阶方法,你都要懂二阶展开。因为几乎所有优化算法的收敛性分析都建立在泰勒展开之上,你懂了这套语言,看论文、调参数、debug训练过程都会顺手很多。

还有一个容易踩的坑:自动微分的二阶导数计算。很多人以为PyTorch或TensorFlow里torch.autograd.grad只能算一阶梯度,其实通过高阶自动微分是可以算海森向量积的。这个技巧在分析损失曲面局部几何时非常有用,我常用它来验证某个点附近是极小值还是鞍点。

import torch def hessian_vector_product(loss_fn, params, v): """计算海森向量积 H·v(无需显式构造海森矩阵)""" grads = torch.autograd.grad(loss_fn(params), params, create_graph=True) flat_grads = torch.cat([g.reshape(-1) for g in grads]) flat_v = torch.cat([x.reshape(-1) for x in v]) grad_v = torch.sum(flat_grads * flat_v) hvp = torch.autograd.grad(grad_v, params, retain_graph=True) return hvp

这段代码的思路是:先对损失函数求一阶梯度时打开create_graph=True,让梯度图保留下来,然后再对“梯度与向量的点积”求一次梯度,得到的就是海森向量积。整个过程只需要两次反向传播,内存代价可控,不需要显式构造海森矩阵。

我在分析一个模拟项目的损失曲面时,就靠这个函数判断过好几个参数点。有个点看起来loss很低,梯度也接近零,但海森向量积的特征值显示它是个鞍点,继续训练还有下降空间。当时如果只盯着一阶梯度,很可能就提前停了。

5.4 展开下一步:从理解到应用

坦白说,泰勒公式本身并不难,难的是把它变成一个随时能调用的思维工具。我现在看一篇优化论文,第一反应就会去找它基于几阶泰勒展开做了假设,是在哪个展开点附近做分析,余项被忽略的代价是什么。这个习惯一旦养成,很多论文里的“魔法”都会变得透明。

如果你也想培养这个习惯,可以从一个最简单的练习开始:对你手头正在训练的一个小模型,固定一个参数点,计算它的梯度、海森向量积,再画一下沿某个方向切开的损失面,对照一下二阶泰勒近似的预测。做一次,对“损失曲面到底长什么样”的感觉会完全不一样。

多元泰勒公式在AI里不是一道被考完就忘的数学题,它是整个优化理论的地基。地基稳不稳,直接决定了你后面看问题的高度。我在实际踩过那些坑之后,回头再读梯度下降和Adam的论文,才发现很多设计细节从一开始就写在这一行行的展开式里了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询