3、局部微调算法基础:梯度下降法、牛顿法、拟牛顿法的原理与对比

聊到局部微调,绕不开的就是那几样最基础的优化算法。说实话,我刚开始做 NLP 模型微调的时候,总觉得这些算法是数学课本里的东西,跟实际工程离得远。后来被几个 loss 不收敛的 bug 折磨过之后,才老老实实回头补课。

今天我们就掰开揉碎,把梯度下降、牛顿法、拟牛顿法这三兄弟讲清楚。你想想看,它们本质上都是在干同一件事——找一个能让损失函数最小的参数点。但怎么找,每一步往哪走、走多大,差别就大了。

3.1 梯度下降法:最朴素的“下山”策略

梯度下降法,说白了就是沿着最陡的方向往下滑。你站在一座山上,闭着眼睛,用脚探一探哪个方向最陡,就往那个方向迈一步。这就是梯度下降的直觉。

数学上,我们计算损失函数 L(θ) 对参数 θ 的梯度 ∇L(θ),然后沿着梯度的反方向更新参数:

θ_{t+1} = θ_t - η · ∇L(θ_t)

这里的 η 是学习率,也就是步长。我见过太多新手把学习率设得太大,结果 loss 直接飞了;设得太小,又半天不收敛。嗯,这里要注意,学习率是梯度下降法里最敏感的超参数。

我的经验: 做 BERT 微调时,我习惯先用 2e-5 到 5e-5 这个范围试。如果 loss 震荡厉害,就降一个数量级。如果 loss 下降太慢,就稍微提一点,但别超过 1e-4。

3.1.1 三种变体

实际工程里,我们很少用最原始的梯度下降。根据每次更新用的数据量,分成三种:

变体 每次用多少数据 更新频率 收敛稳定性
批量梯度下降 (BGD) 全部训练数据 慢(每 epoch 一次) 稳定,但容易陷入局部最优
随机梯度下降 (SGD) 1 个样本 快(每样本一次) 震荡大,但有机会跳出局部最优
小批量梯度下降 (MBGD) 一个小批量(如 32/64) 适中 折中方案,工程中最常用

我个人习惯用 Mini-batch SGD,batch size 设在 32 或 64。为什么?因为 GPU 的并行计算能力能充分利用起来,而且每个 batch 的梯度噪声刚好起到一点正则化作用。

我曾经踩过的坑: 有一次做序列标注任务,我把 batch size 设成了 1,结果 loss 曲线像心电图一样上下乱跳。后来改成 32,立马稳了。batch size 太小,梯度方差太大,模型根本学不进去。

3.2 牛顿法:用二阶信息“抄近路”

梯度下降法只用了一阶导数信息,也就是梯度。它只知道哪个方向是下坡,但不知道这个坡的曲率——也就是坡是越来越陡还是越来越缓。

牛顿法就不一样了。它不光看梯度,还看海森矩阵(Hessian Matrix),也就是二阶导数。这相当于你不仅知道哪边是下坡,还知道这个坡的弯曲程度,从而能一步跨到谷底附近。

更新公式长这样:

θ_{t+1} = θ_t - [H(θ_t)]⁻¹ · ∇L(θ_t)

其中 H(θ_t) 是海森矩阵,[H(θ_t)]⁻¹ 是它的逆矩阵。

你想想看,梯度下降法是一阶的,牛顿法是二阶的。按理说二阶方法应该更厉害才对。但为什么实际工程里,大家还是用梯度下降居多?

原因很简单——海森矩阵太大了。假设你的模型有 1 亿个参数(这在今天的大模型里算小的),海森矩阵就是 1 亿 × 1 亿的矩阵,光存储就需要 4 × 10¹⁶ 字节,也就是 40 PB。这谁扛得住?

核心矛盾: 牛顿法收敛快(二次收敛),但计算海森矩阵及其逆矩阵的代价太高,在大规模深度学习场景下基本不可行。

3.3 拟牛顿法:折中的智慧

既然直接算海森矩阵不现实,那能不能近似它?这就是拟牛顿法的思路。

拟牛顿法不直接计算海森矩阵,而是通过每次迭代的梯度变化,逐步构建一个海森矩阵的近似。最常用的两种是:

  • DFP 算法:由 Davidon、Fletcher、Powell 提出,直接近似海森矩阵的逆。
  • BFGS 算法:由 Broyden、Fletcher、Goldfarb、Shanno 提出,目前最流行的拟牛顿法。
  • L-BFGS:Limited-memory BFGS,只保存最近几次迭代的信息,内存开销小,适合大规模问题。

BFGS 的更新公式看起来有点复杂,但核心思想很简单:用一阶梯度信息去逼近二阶曲率信息。

# 伪代码示意:BFGS 更新步骤
1. 计算当前梯度 g_t
2. 计算方向 d_t = -H_t · g_t   (H_t 是海森逆的近似)
3. 线搜索确定步长 α_t
4. 更新参数 θ_{t+1} = θ_t + α_t · d_t
5. 计算新梯度 g_{t+1}
6. 根据梯度变化更新 H_{t+1}

我在做 CRF 序列标注模型的训练时,就常用 L-BFGS。因为 CRF 的损失函数是凸的,L-BFGS 收敛得又快又稳,比 SGD 省心多了。

选型建议: 如果你的损失函数是凸的,或者模型参数规模在百万级以内,优先考虑 L-BFGS。如果是深度神经网络,参数上亿,那就老老实实用 SGD 或 Adam。

3.4 三种方法的对比

我把它们放在一起对比,这样更直观:

维度 梯度下降法 牛顿法 拟牛顿法 (BFGS/L-BFGS)
导数阶数 一阶 二阶 近似二阶
收敛速度 线性收敛 二次收敛 超线性收敛
每次迭代计算量 O(n) O(n³) O(n²) 或 O(n)
内存开销 O(n) O(n²) O(n²) 或 O(m·n)
适用场景 大规模深度学习 小规模、凸优化 中等规模、凸优化
对超参数敏感度 高(学习率) 低 低

注意看那个 O(n³),这就是牛顿法在大模型上“死”的原因。n 是参数数量,三次方的复杂度,参数一多直接爆炸。

3.5 知识结构图

下面这张图把三种方法的关系和适用场景梳理清楚了:

局部微调算法知识结构 局部微调优化算法 梯度下降法 牛顿法 拟牛顿法 BGD SGD MBGD 海森矩阵求逆 DFP BFGS L-BFGS 关键对比总结 • 梯度下降:一阶,O(n) 计算,适合大规模深度学习,对学习率敏感 • 牛顿法:二阶,O(n³) 计算,收敛最快但内存爆炸,小规模凸优化可用 • 拟牛顿法:近似二阶,O(n²) 或 O(m·n) 计算,折中方案,L-BFGS 最实用 • 选型口诀:大模型用 SGD/Adam,中模型用 L-BFGS,小模型用牛顿法

3.6 实际工程中的选型建议

说了这么多理论,最后给点实在的。你在实际项目中到底该用哪个?

  • 大模型微调(BERT/GPT 等):用 AdamW 或 SGD with Momentum。别碰牛顿法,也别碰 BFGS,参数太多了,内存扛不住。
  • 传统机器学习模型(CRF/SVM 等):用 L-BFGS。收敛快,不用调学习率,省心。
  • 小规模凸优化问题:可以考虑牛顿法,但要做好海森矩阵正则化,防止奇异。
  • 非凸问题:梯度下降法配合动量或 Adam 是主流。拟牛顿法在非凸问题上效果一般。
我曾经犯过的错: 刚入行时,我在一个百万参数的任务上用了 BFGS,结果每次迭代都要算 O(n²) 的矩阵,训练一天都没跑完一个 epoch。后来换成 L-BFGS,只保存最近 10 次迭代的信息,内存降了两个数量级,速度也上来了。记住,工程上“够用”比“最优”更重要。

好了,局部微调的基础算法就聊到这儿。梯度下降法、牛顿法、拟牛顿法,这三兄弟各有各的脾气。理解它们的本质区别,你就能在项目中做出更聪明的选择。