☰
机器学习优化算法剖析:梯度下降法与牛顿法手推公式及工程应用
2026/10/16 4:13:45 网站建设 项目流程

机器学习优化:梯度下降法与牛顿法手推公式详解

做算法这行,尤其是刚入门机器学习那阵子,绝大多数人第一个被劝退的坎儿就是优化算法。看代码倒还好,调个库跑个模型似乎也不难,但一旦要认真看论文、看源码,或者自己手搓一个模型出来,满屏的梯度、海森矩阵、迭代公式扑面而来,瞬间就懵了。我当年也是这样,前前后后折腾了大半个月,才勉强把梯度下降法和牛顿法从“知道名字”变成了“能自己推、能讲清楚为什么”。

这篇文章就把我踩过的坑和琢磨透的东西一次讲明白。我会从最朴素的直觉出发,把梯度下降法和牛顿法的每一步推导都手写出来,不跳步、不藏公式,同时把“为什么这么设计”“什么时候用哪个”“实际工程里有哪些大坑”也一并说清楚。不管你是刚接触机器学习的初学者,还是在复习数学基础准备面试的开发者,这篇文章应该都能帮你把这两个算法真正装进脑子里。

1. 从优化问题开始:我们到底在做什么

先理清一个基本问题:机器学习里的“训练”,本质上是在解一个优化问题。我们有一个模型,模型带一堆参数,我们用数据去算一个损失函数——这个函数的值越小,说明模型在训练集上表现得越好。训练的过程,就是不断调整参数,让损失函数的值尽可能小。

写成数学语言就是:

min θ L(θ)

其中 θ 是模型的参数向量,L(θ) 是损失函数。比如线性回归的损失函数是均方误差,逻辑回归是交叉熵,神经网络是各种自定义的 loss。不管损失函数长什么样,我们手上的工具其实就那么几类:一类是只用到一阶导数(梯度)的方法,梯度下降法是代表;另一类是用到二阶导数(海森矩阵)的方法,牛顿法是代表。

1.1 为什么不能直接求解析解

有人可能会问:求最小值,不就是令导数等于零吗?为什么还要搞迭代算法?

确实,如果损失函数是凸函数且形式简单,比如线性回归的均方误差,我们可以直接通过正规方程求出解析解。但现实中的模型没这么温柔。神经网络的损失函数是一个极度复杂的非线性函数,参数动辄百万千万级别,解析解根本不存在,或者即使存在也根本算不出来。所以我们必须依赖迭代法——从一个初始参数出发,一步一步地更新参数,让损失函数逐步下降,最终逼近最小值。

这种迭代优化的思路,就像你站在一座山上,要摸黑走到山谷的最低点。你看不到整座山的地形图,只能通过脚底感受当下的坡度,然后朝最陡的下坡方向迈一步,再感受、再迈步,慢慢接近谷底。梯度下降法就是这么干的,而牛顿法更厉害一点,它不光感受坡度,还能感受到坡度的变化趋势,迈步的时候把前方的地形曲率也算进去。

1.2 泰勒展开:所有优化算法的共同语言

无论梯度下降还是牛顿法,它们的推导都逃不开一个数学工具——泰勒展开。泰勒展开告诉我们,一个光滑的函数在某个点附近,可以用它的导数信息来逼近。

对于一元函数 f(x),在点 x_k 处做二阶泰勒展开:

f(x) ≈ f(x_k) + f'(x_k)(x - x_k) + (1/2) f''(x_k)(x - x_k)^2

对于多元函数 f(x),其中 x 是 n 维向量,在点 x_k 处做二阶泰勒展开:

f(x) ≈ f(x_k) + ∇f(x_k)^T (x - x_k) + (1/2) (x - x_k)^T H(x_k) (x - x_k)

这里 ∇f(x_k) 是梯度向量,H(x_k) 是海森矩阵,也就是二阶偏导数组成的矩阵。

掌握了这个工具,接下来的所有推导就顺理成章了。梯度下降法可以看作只保留了泰勒展开的一阶项,牛顿法则把二阶项也考虑进来了。差别就在这一项上,但这一项带来的效果天差地别。

2. 梯度下降法手推全过程

梯度下降法是最基础、最常用的优化算法,没有之一。理论上讲,只要你有一阶导数信息,你就能用梯度下降法去优化任何一个可微函数。它的核心思想简单到令人发指:既然梯度是函数增长最快的方向,那梯度的反方向就是函数下降最快的方向,沿着反方向走就能让函数值下降。

2.1 朴素版本:一元函数的梯度下降推导

先从最简单的情况入手。设我们有一个一元函数 f(x),在点 x_k 处做一阶泰勒展开:

f(x) ≈ f(x_k) + f'(x_k)(x - x_k)

我们希望找到一个新点 x_{k+1},使得 f(x_{k+1}) < f(x_k)。从泰勒展开式来看,要让 f(x) 比 f(x_k) 小,只需要让 f'(x_k)(x - x_k) 这一项尽可能小(为负且绝对值大)。

但这里有个问题:如果不对 (x - x_k) 的距离加以限制,我们可以让 x 离 x_k 无限远,一阶泰勒展开早就失真了。泰勒展开只在 x_k 附近的一个小邻域内才近似成立。所以我们要限制每次迈步的步长——这就是学习率的由来。

令 Δx = x - x_k,那么要最小化的是 f'(x_k) · Δx,同时 |Δx| 被限制在一个很小的范围 η 内。显然,当 Δx 与 f'(x_k) 方向相反时,乘积最小。也就是:

Δx = -η · f'(x_k)

于是新的迭代公式:

x_{k+1} = x_k - η · f'(x_k)

这个式子看着简单,但整个过程推出每一步都是有说道的。学习率 η 就是步长,控制了每次更新走多远。方向是负梯度方向,步长是 η,两者缺一不可。

2.2 多元情况的推导与矩阵形式

实际机器学习中,参数几乎永远是向量,所以必须升级到多元版本。设损失函数 L(θ),参数 θ ∈ R^n。在 θ_k 处做一阶泰勒展开:

L(θ) ≈ L(θ_k) + ∇L(θ_k)^T (θ - θ_k)

同样的逻辑,我们希望选择一个 Δθ = θ - θ_k,在步长受限的条件下让 L(θ_k) + ∇L(θ_k)^T Δθ 最小。由柯西-施瓦茨不等式:

∇L(θ_k)^T Δθ ≥ -|∇L(θ_k)| · |Δθ|

等式成立的条件是 Δθ 与 ∇L(θ_k) 方向相反。因此:

Δθ = -η · ∇L(θ_k)

最终迭代公式:

θ_{k+1} = θ_k - η · ∇L(θ_k)

这就是我们在所有深度学习框架里反复见到的那个更新公式。无论是 PyTorch 里的optimizer.step()还是手写线性回归时用到的更新语句,本质都是这一行。

2.3 学习率的影响:从数学公式看为什么步长不能太大

关于学习率,我一直建议大家不要只把它当超参来调,而是要从公式层面理解它为什么会带来那些问题。

看上面的迭代公式,如果 η 太小,每一步都走得很谨慎,需要迭代很多次才能收敛,训练速度非常慢。如果 η 太大,会出现什么情况?

逐项分析迭代公式。假设当前点 x_k 在最小值点 x_min 右侧,梯度为正,更新后 x_{k+1} = x_k - η · g_k。如果 η 很大,x_{k+1} 可能一下子冲到最小值的左侧很远的地方。此时在新的点,梯度为负,更新后又冲回右侧。结果就是参数在最小值附近来回震荡,甚至每一步都越弹越远——这就是“发散”。

从数学上看,对于一个二次函数 f(x) = (1/2) a x^2,梯度是 ax,迭代公式是 x_{k+1} = (1 - ηa) x_k。要让这个序列收敛,必须满足 |1 - ηa| < 1,即 0 < η < 2/a。如果 ηa > 2,|1 - ηa| > 1,参数就会每一步放大,直接爆炸。

所以学习率本质上受限于损失函数在当前位置的曲率(或者说二阶导)。曲率越大,允许的步长就越小。这也自然引出我们为什么要研究牛顿法——它把曲率信息显式地考虑进来了。

3. 牛顿法:把曲率信息加进来

如果说梯度下降法只用了函数的一阶信息,那牛顿法就是用了一阶加二阶信息。它的想法很直接:既然泰勒展开里有二阶项能更准确地描述函数在局部的形状,为什么不让它参与决定步长和方向?

3.1 从二阶泰勒展开推导牛顿法

继续用一元函数开始。对 f(x) 在 x_k 处做二阶泰勒展开:

f(x) ≈ f(x_k) + f'(x_k)(x - x_k) + (1/2) f''(x_k)(x - x_k)^2

这是一个关于 (x - x_k) 的二次函数。我们希望找到让这个近似函数最小的点。对右边关于 x 求导并令其等于零:

f'(x_k) + f''(x_k)(x - x_k) = 0

解这个方程:

x - x_k = -f'(x_k) / f''(x_k)

于是得到牛顿法的迭代公式:

x_{k+1} = x_k - f'(x_k) / f''(x_k)

注意这里的推导过程和梯度下降法有本质区别。梯度下降法是人为设定一个步长 η,然后沿着负梯度方向走。牛顿法则没有显式设置学习率,它直接通过二阶信息算出了一个“最优步长”—— f'(x_k) / f''(x_k) 这个比值同时包含了方向和步长信息。

对于二次函数来说,这个公式一步就能跳到最小值点,因为二阶泰勒展开就是原函数本身,没有近似误差。

3.2 多元牛顿法:向量与矩阵形式

把牛顿法推广到多元函数。损失函数 L(θ) 在 θ_k 处的二阶泰勒展开:

L(θ) ≈ L(θ_k) + ∇L(θ_k)^T (θ - θ_k) + (1/2)(θ - θ_k)^T H_k (θ - θ_k)

其中 H_k 是海森矩阵,第 i 行第 j 列的元素是 ∂²L/∂θ_i ∂θ_j 在 θ_k 处的取值。对这个近似函数关于 θ 求梯度并令其为零:

∇L(θ_k) + H_k (θ - θ_k) = 0

移项得到:

θ - θ_k = -H_k^{-1} ∇L(θ_k)

最后牛顿法迭代公式:

θ_{k+1} = θ_k - H_k^{-1} ∇L(θ_k)

每一步都要计算海森矩阵的逆 H_k^{-1},这就是牛顿法在实际工程中最大的痛点。假设模型有 1000 万个参数,海森矩阵就是 1000 万 × 1000 万的矩阵,光存储就需要 400 TB(按 float32 计算),这显然不现实。即便参数少一些,求逆的复杂度是 O(n³),参数稍微多一点就完全不可接受。

3.3 为什么牛顿法收敛更快:线性收敛与二次收敛对比

这里用一个直观的比喻。梯度下降法就像是一个近视眼,只知道眼前几米的路是上坡还是下坡,只能凭感觉迈出固定大小的一步。牛顿法则像是一个能看到远方地形的人,它知道前方是平缓还是陡峭,会根据地形的弯曲程度调整步伐:地形平缓就迈大步,地形陡峭就迈小步。

用数学语言来比较。对梯度下降法,在最小值点附近,误差 e_k = x_k - x_min 的收敛行为近似为:

e_{k+1} ≈ (1 - ηf''(x_min))e_k

这是线性收敛,误差每一次乘一个常数因子。而对牛顿法,可以证明:

e_{k+1} ≈ (f'''(x_min) / (2f''(x_min))) e_k^2

误差的平方出现在右边,这是二次收敛。如果当前误差是 0.1,下一次大约变成 0.01,再下一次变成 0.0001,每步缩小的位数翻倍。而线性收敛的话,每次可能只从 0.1 变成 0.05 或 0.08,慢得肉眼可见。

看完收敛性分析,你就明白为什么在某些问题上,牛顿法只需要几步就到精度,而梯度下降法要跑几百上千轮。

4. 从推导到工程:两种算法的选型、改进与常见问题

公式推完了,接下来是更接地气的部分。你在实际训练模型的时候,直接用原始牛顿法的场景其实极少,倒不是因为原理不行,而是计算代价压不住。但理解它的原理,去看那些更高级的优化器时——比如 L-BFGS、Adam、RMSProp——你会发现它们都是在牛顿法和梯度下降法之间做折中设计出来的。

4.1 工程实现避坑:学习率、海森矩阵与数值稳定性

先说梯度下降法在工程实现中最常见的几个坑。

学习率的选择永远是第一个坑。我之前训练一个简单的逻辑回归模型,初始学习率设为 0.5,结果损失值在训练日志里跳来跳去,完全收敛不了。后来把学习率降到 0.01,模型很快就稳定收敛了。当时我就想,要是早点从二次函数的收敛条件去理解学习率,就不会这样盲目去试了。一个实用的做法是开始训练时用一个较小的学习率(比如 0.001),观察前几个 epoch 的损失变化,如果损失在稳步下降,可以尝试逐步调大;如果损失震荡,就调小一个数量级再试。

梯度下降法还有一个需要注意的地方是特征缩放。因为这个方法对每个维度用的是同一个学习率 η,如果特征之间的量纲差距悬殊,比如一个特征范围是 [0, 1],另一个范围是 [0, 100000],那么梯度在各个维度上的量级会完全不同。此时同一个学习率会导致某些维度更新太慢,另一些维度又可能震荡甚至发散。这就是为什么要做标准化或归一化——把各个维度的尺度统一之后,梯度下降才能真正高效工作。

再看牛顿法。原始牛顿法在工程中的第一个问题是海森矩阵可能不是正定的。在非凸函数上,海森矩阵可能是负定或半负定的,这时候 H_k^{-1} ∇L(θ_k) 的方向不仅不是下降方向,反而可能指向上升方向。实际表现是迭代一步之后,损失函数反而变大了。解决方案是加一个正则化项,把海森矩阵修正为 H_k + λI,其中 λ 是一个正数,这样保证矩阵正定,迭代也就稳定了——这个思想后来也被应用到 Levenberg-Marquardt 算法中。

第二个问题是当海森矩阵接近奇异时,求逆的结果会非常大,导致参数更新一步就飞出去。这个在数值优化里,通常可以通过加正则化或使用伪逆来缓解。

4.2 变体算法速览:从 SGD 到 Adam,牛顿的遗产

说实话,现在深度学习中标准的优化器已经不是朴素的梯度下降法了,而是各种变体。但如果你把公式拿过来对比,会发现它们都在解决同一个问题——如何让梯度下降既快又稳。

随机梯度下降(SGD)解决了全量梯度计算代价过高的问题。原来每次迭代要用全部训练样本算梯度,现在每次只随机抽一个小批量(mini-batch)去估计梯度。梯度带上了噪声,但期望方向仍然是对的,而且计算速度快到能迭代更多轮次,整体反而更快收敛。

带动量的 SGD 则在公式里加了一个“惯性项”:

v_{k+1} = βv_k + (1-β)∇L(θ_k) θ_{k+1} = θ_k - ηv_{k+1}

这等于把历史梯度信息累积起来,方向一致时加速,方向不一致时减速,有效抑制了梯度下降在峡谷地形中的震荡——这个思路也借鉴了牛顿法中“利用更多历史/局部信息来修正方向”的理念。

RMSProp 和 Adam 则更进一步,它们用梯度的平方滑动平均来给每个参数自适应地调整学习率。核心思想是对梯度大的维度自动放慢步伐,对梯度小的维度自动加快步伐。这其实是对牛顿法“用二阶信息调整步长”的一种廉价近似——不是真的计算海森矩阵,而是用历史梯度平方来模拟曲率。理解了牛顿法的原理之后,你再看 Adam 的公式就不会觉得它是凭空冒出来的了。

4.3 实战心得:我如何选择优化器并调试收敛

根据我的个人经验,在传统机器学习模型上,比如逻辑回归、线性回归,如果特征维度不高(几千以内)而且训练样本量适中,用 L-BFGS 这类拟牛顿法效果非常惊艳。它比梯度下降法收敛快得多,而且不需要手动调学习率,省心省力。在深度学习中,Adam 基本是默认首选,因为它在大多数任务上都能以较少的调参成本达到不错的收敛效果。

但 Adam 也有它的毛病,在训练后期容易在最优解附近来回波动,难以精细收敛。这个时候有一个常用的技巧:在训练后期切换到带动量的 SGD,或者把 Adam 的学习率衰减到很小的值,让模型在局部区域精细打磨。这个“先 Adam 后 SGD”的策略,在很多比赛中都能有效提升最终精度,是业界常見的做法。

另外关于牛顿法的经验,有一次我需要求解一个规模不算大的非线性最小二乘问题,参数大概只有几百个,用梯度下降法怎么调都感觉很慢。后来改用牛顿法,几下就收敛了。当时海森矩阵的计算是通过数值差分近似得到的,虽然每一步计算有点贵,但总迭代次数少了两个数量级,整体耗时反而大幅下降。所以不要一听牛顿法就说“太贵用不起”——在小规模问题上它依然是一个极其有力的工具。

4.4 常见问题速查表

为了方便实际操作中快速排查问题,我把常见现象、原因和应对方案整理成了下面这张表。

现象可能原因排查方向与解决方案
损失震荡不下降学习率过大;数据未归一化调低学习率;对特征做标准化处理
损失下降极慢学习率过小;梯度消失调大学习率;检查模型初始化和激活函数
训练后期损失波动,无法精调Adam 类优化器步长过粗衰减学习率;切换到带动量的 SGD 精调
牛顿法迭代时损失上升海森矩阵非正定给海森矩阵加正则项 λI,保证正定性
牛顿法更新一步就爆炸海森矩阵接近奇异,逆矩阵过大加更大的正则化系数;改用伪逆或拟牛顿法
梯度下降收敛到鞍点停滞一阶信息在鞍点处为零使用带动量的 SGD 打破对称性;或改用二阶优化方法

这个表中包含的问题,都是我在实际工作中真正遇到并解决过的。尤其是牛顿法在海森矩阵非正定时损失上升这个问题,最初我甚至怀疑是自己的梯度算错了,后来排查了很久才发现是矩阵正定性出了问题。如果你也在做类似的事情,不妨对照排查,能节省很多时间。

5. 写在最后的一点体会

从梯度下降到牛顿法,本质上是在追问同一个问题:我们如何找到一个点,让函数值不断下降直到最小?梯度下降说“只信脚下的坡度,每次小心地走一截”,牛顿法说“多看几步,把地形弯曲也考虑进去,一步就能跨到位”。两种策略各有代价和适用范围,而现代优化算法的历史,很大程度上就是在这两种思路之间不断折中、改进和融合。

我个人在实际操作中的体会是:先把这两套公式从零推一遍,比读十篇博客都有用。推导的过程中你会自然地明白每一个符号的含义,明白为什么学习率不能太大、为什么海森矩阵要正定、为什么二阶收敛快但计算代价高。之后再去看 Adam、RMSProp、L-BFGS 这些进阶算法,你会发现自己几乎不需要死记硬背,公式的来龙去脉自动就清晰了。

如果这篇文章对你的学习有一点帮助,那我的目的就达到了。至少下次当你再看到某个优化算法的公式时,可以想起——这些看似复杂的符号背后,其实就是一个朴素的问题:下一步该往哪儿走,走多远。

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

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

立即咨询