58-机器学习与大模型开发数学教程-5-5 牛顿法与拟牛顿法(BFGS、L-BFGS)

一句话版:牛顿法 用二阶曲率(Hessian)给出"最会拐弯"的下降方向,局部二次收敛、步子少拟牛顿法 用数据驱动的低成本曲率近似 (BFGS/L-BFGS),不需要显式 Hessian ,却常有超线性收敛

类比:在山谷里下山------梯度法 像没有方向盘的车,牛顿法 像有高精度方向盘的车,拟牛顿像学会了如何"凭历史轨迹修方向盘"。


1. 从二次近似出发:为什么二阶会快?

在点 xkx_kxk 处对目标函数 fff 做二次泰勒近似:

mk(p)  =  f(xk)+∇f(xk)⊤p+12p⊤Hkp,Hk≈∇2f(xk). m_k(p) \;=\; f(x_k) + \nabla f(x_k)^\top p + \tfrac12 p^\top H_k p, \quad H_k \approx \nabla^2 f(x_k). mk(p)=f(xk)+∇f(xk)⊤p+21p⊤Hkp,Hk≈∇2f(xk).

令模型最小化的一阶条件得到牛顿方向 pkp_kpk:

Hk pk=−∇f(xk). H_k\,p_k = -\nabla f(x_k). Hkpk=−∇f(xk).

再做一次线搜索(或信赖域)更新:

xk+1=xk+αk pk. x_{k+1} = x_k + \alpha_k\,p_k. xk+1=xk+αkpk.

  • 若 HkH_kHk 用真 Hessian 且在解附近 KaTeX parse error: Undefined control sequence: \* at position 5: H(x^\̲*̲)\succ 0,二次收敛:误差平方级缩小。
  • 直觉:把弯曲"看懂"之后,一拐就到,不再"之"字形。

2. 线搜索 vs 信赖域:两种操控方式

  • 线搜索 :选方向 pkp_kpk 后,找满足Armijo-Wolfe 条件的步长 αk\alpha_kαk,既降函数值,又不过冲。
  • 信赖域 :限制 ∥p∥≤Δ\|p\|\le \Delta∥p∥≤Δ,在"小球"里最小化 mk(p)m_k(p)mk(p)。若模型可信,扩大 Δ\DeltaΔ;否则收缩

实践建议:凸问题常用线搜索 ;非凸和数值敏感时,信赖域更稳(如 LM/牛顿-CG)。


3. 标准牛顿法的代价与"折中方案"

  • 代价 :构造 Hessian O(n2)O(n^2)O(n2),解线性方程 O(n3)O(n^3)O(n3)。当 nnn 很大(深度网络),不可行

  • 替代

    • 只做 Hessian-vector(HVP) :用 Pearlmutter 技巧在 O(n)O(n)O(n) 成本算 HvHvHv;内层用 CG 解 Hp=−gHp=-gHp=−g(牛顿-CG)。
    • 拟牛顿 :学习到一个好的逆 Hessian 近似来乘梯度,避免求解线性系统。

4. 拟牛顿思想:用"位移-梯度差"学习曲率

sk=xk+1−xk,yk=∇f(xk+1)−∇f(xk). s_k = x_{k+1}-x_k,\qquad y_k = \nabla f(x_{k+1}) - \nabla f(x_k). sk=xk+1−xk,yk=∇f(xk+1)−∇f(xk).

我们希望更新一个对称正定 矩阵(或其逆)使之满足割线方程

Bk+1sk=yk(或  Hk+1yk=sk), B_{k+1}s_k = y_k \quad (\text{或}\; H_{k+1}y_k = s_k), Bk+1sk=yk(或Hk+1yk=sk),

并"尽量靠近上一步"。

4.1 BFGS(更新 Hessian 近似的逆)

最常用的是 BFGS 的逆式更新 (直接维护 Hk≈(∇2f)−1H_k\approx (\nabla^2 f)^{-1}Hk≈(∇2f)−1):

ρk=1yk⊤sk,Hk+1=(I−ρkskyk⊤) Hk (I−ρkyksk⊤)  +  ρk sksk⊤. \rho_k = \frac{1}{y_k^\top s_k},\quad H_{k+1} = \big(I - \rho_k s_k y_k^\top\big)\, H_k \,\big(I - \rho_k y_k s_k^\top\big) \;+\; \rho_k\, s_k s_k^\top. ρk=yk⊤sk1,Hk+1=(I−ρkskyk⊤)Hk(I−ρkyksk⊤)+ρksksk⊤.

  • 正定性 :若 曲率条件 yk⊤sk>0y_k^\top s_k>0yk⊤sk>0(强 Wolfe 线搜索通常保证),则 Hk+1≻0H_{k+1}\succ0Hk+1≻0。
  • 方向 :pk=−Hk∇f(xk)p_k = - H_k \nabla f(x_k)pk=−Hk∇f(xk)。
  • 收敛 :在适当条件下可达超线性(比一阶快、比牛顿略慢)。

4.2 L-BFGS(有限记忆)

当 nnn 大时,存 HkH_kHk 需要 O(n2)O(n^2)O(n2) 内存。L-BFGS 仅保存最近 mmm 对 (si,yi)(s_i,y_i)(si,yi)(如 m=10∼50m=10\sim 50m=10∼50),通过两遍递推计算方向:

  • 后向遍历 :计算系数 αi=ρisi⊤q,  q←q−αiyi\alpha_i = \rho_i s_i^\top q, \; q\leftarrow q-\alpha_i y_iαi=ρisi⊤q,q←q−αiyi。
  • 初始缩放 :H0(k)=sk−1⊤yk−1yk−1⊤yk−1IH_0^{(k)} = \frac{s_{k-1}^\top y_{k-1}}{y_{k-1}^\top y_{k-1}} IH0(k)=yk−1⊤yk−1sk−1⊤yk−1I。
  • 前向遍历 :r←H0(k)q;  βi=ρiyi⊤r;  r←r+si(αi−βi)r \leftarrow H_0^{(k)} q; \; \beta_i = \rho_i y_i^\top r; \; r\leftarrow r + s_i(\alpha_i-\beta_i)r←H0(k)q;βi=ρiyi⊤r;r←r+si(αi−βi)。
  • 方向 :pk=−rp_k = -rpk=−r。
    内存 O(mn)O(mn)O(mn)、时间 O(mn)O(mn)O(mn),适合大维度。

5. 三种方法一览:方向如何"会拐弯"?

当前点与梯度
梯度下降
牛顿法
BFGS/L-BFGS
方向: 负梯度
方向: 解 Hessian 方程
方向: 逆H近似乘梯度
线搜索
线搜索或信赖域
线搜索

说明 :三者都做线搜索(或信赖域),差别在于方向 如何构造:是否利用曲率


6. 数学性质(简要而够用)

  • 牛顿法

    • 若 fff 二次可微、解点邻域 KaTeX parse error: Undefined control sequence: \* at position 5: H(x^\̲*̲)\succ \mu I、Hessian 李普希茨连续,则局部二次收敛
    • 非凸时 HHH 可不定 → 需修正 (如加 λI\lambda IλI 或信赖域)。
  • BFGS / L-BFGS

    • 满足强 Wolfe + yk⊤sk>0y_k^\top s_k>0yk⊤sk>0 → Hk≻0H_k\succ0Hk≻0 且方向下降;
    • 对光滑强凸问题常见超线性收敛
    • L-BFGS 记忆有限,收敛率略受 mmm 影响,但对大规模问题更实用。

7. 逻辑回归上的示例(凸问题的得心应手)

逻辑回归的负对数似然

f(w)=1n∑i=1nlog⁡(1+exp⁡(−yiw⊤xi))+λ2∥w∥2 f(w)=\frac{1}{n}\sum_{i=1}^n \log\big(1+\exp(-y_i w^\top x_i)\big) + \frac{\lambda}{2}\|w\|^2 f(w)=n1i=1∑nlog(1+exp(−yiw⊤xi))+2λ∥w∥2

光滑强凸

  • 牛顿法 :Hessian 是带权特征协方差矩阵 X⊤WX+λIX^\top W X + \lambda IX⊤WX+λI,可做牛顿-CG(只需 HVP)。
  • BFGS / L-BFGS :无需 Hessian/HVP,通常迭代次数更少、收敛更稳,工业界常用作"批量学习器"的基线优化器。

8. 深度学习与大模型:为何不直接用牛顿?

  • 维度太大(百万-十亿参数),构造/分解 Hessian 不现实

  • 损失非凸 ,Hessian 不定;需要大量数值保护。

  • 取而代之:

    • 一阶自适应(AdamW、RMSProp)------5-6 节详述;
    • 近似二阶预条件(K-FAC、Shampoo、AdaHessian)------以块结构或 Fisher 近似学习曲率;
    • 牛顿-CG + HVP 在某些大模型微调/二阶方法研究中仍有用武之地。

9. 代码骨架(可直接改造使用)

9.1 线搜索(Armijo-Wolfe 简化版)

python 复制代码
def line_search(f, g, x, p, c1=1e-4, c2=0.9, alpha0=1.0):
    # f: 值函数, g: 梯度函数
    alpha, phi0, dphi0 = alpha0, f(x), g(x) @ p
    while True:
        x_new = x + alpha * p
        if f(x_new) > phi0 + c1 * alpha * dphi0:  # Armijo
            alpha *= 0.5
        elif g(x_new) @ p < c2 * dphi0:           # Wolfe 曲率
            alpha *= 1.1
        else:
            return alpha

9.2 BFGS(逆式,保持 H≻0H\succ0H≻0)

python 复制代码
import numpy as np

def bfgs(f, grad, x0, maxit=200, tol=1e-6):
    x = x0.copy()
    n = len(x)
    H = np.eye(n)
    g = grad(x)
    for _ in range(maxit):
        if np.linalg.norm(g) < tol: break
        p = - H @ g
        alpha = line_search(f, grad, x, p)
        s = alpha * p
        x_new = x + s
        g_new = grad(x_new)
        y = g_new - g

        ys = y @ s
        if ys <= 1e-10:     # 保护:若曲率不正,做阻尼或重置
            H = np.eye(n); x, g = x_new, g_new; continue

        rho = 1.0 / ys
        I = np.eye(n)
        V = I - rho * np.outer(s, y)
        H = V @ H @ V.T + rho * np.outer(s, s)

        x, g = x_new, g_new
    return x

9.3 L-BFGS(两遍递推,有限记忆)

python 复制代码
from collections import deque

def lbfgs(f, grad, x0, m=20, maxit=200, tol=1e-6):
    x = x0.copy()
    S, Y, RHO = deque(maxlen=m), deque(maxlen=m), deque(maxlen=m)
    g = grad(x)
    for _ in range(maxit):
        if np.linalg.norm(g) < tol: break

        # 后向
        q = g.copy()
        alpha = []
        for s, y, rho in reversed(list(zip(S, Y, RHO))):
            a = rho * (s @ q); alpha.append(a); q -= a * y
        # 初始缩放
        if len(S) > 0:
            sy = S[-1] @ Y[-1]; yy = Y[-1] @ Y[-1]
            gamma = sy / yy
        else:
            gamma = 1.0
        r = gamma * q
        # 前向
        for (s, y, rho), a in zip(list(zip(S, Y, RHO)), reversed(alpha)):
            b = rho * (y @ r); r += s * (a - b)

        p = -r
        alpha_ls = line_search(f, grad, x, p)
        s = alpha_ls * p
        x_new = x + s
        g_new = grad(x_new)
        y = g_new - g
        ys = y @ s
        if ys > 1e-10:
            rho = 1.0 / ys
            S.append(s); Y.append(y); RHO.append(rho)
        else:
            S.clear(); Y.clear(); RHO.clear()  # 曲率差,重置记忆
        x, g = x_new, g_new
    return x

工程提示:实际项目中把 line_search 换成库实现(如强 Wolfe)、加入 早停最大步长 会更稳。


10. 何时用谁?一张"拍板"图

中小维度/批量
中大维度
超大/非凸深网
可得或HVP可得

识别问题规模与性质
维度与数据
牛顿/牛顿CG
LBFGS
一阶自适应
Hessian易得?
牛顿CG/信赖域
线搜索 强Wolfe
动量/正则/调度

说明能用二阶就用二阶 ;HVP 易得时用牛顿-CG ;否则就用L-BFGS ;深网以一阶为主。


11. 常见坑与排错

  1. 牛顿方向不是下降方向:Hessian 不定。

    • 对策:修正 Hessian (加 λI\lambda IλI 或做信赖域),或只在 CG 中允许负曲率并截断。
  2. BFGS 失去正定性 :y⊤s≤0y^\top s \le 0y⊤s≤0。

    • 对策:用强 Wolfe 线搜索;或阻尼 BFGS (Powell damping);或重置 H=IH=IH=I。
  3. L-BFGS 不稳定:历史对不代表当前曲率。

    • 对策:缩小记忆 mmm、清空历史、重置缩放、加强正则。
  4. 线搜索过慢:函数评估贵。

    • 对策:拟牛顿 + 简化线搜索(如 Armijo-backtracking),设置最小步长与最大回溯次数。
  5. 数值溢出(logistic/softmax)

    • 对策:稳定实现(logsumexp)、特征缩放/标准化。

12. 练习(含提示)

  1. 推导牛顿方向 :从二次模型一阶条件出发,得到 Hp=−gH p=-gHp=−g。
  2. BFGS 逆式:在最小 Frobenius 范数变化、满足割线与对称约束下,推得逆式更新公式。
  3. 超线性收敛:阅读并复现"强 Wolfe + 曲率条件"下 BFGS 的超线性收敛证明要点。
  4. 牛顿-CG:实现仅依赖 HVP 的 CG 内循环,比较与 L-BFGS 在逻辑回归上的迭代次数与时间。
  5. 阻尼 BFGS :实现 Powell 阻尼,比较 y⊤s≤0y^\top s \le 0y⊤s≤0 情况下的稳定性。
  6. 非凸玩具:在 Rosenbrock 函数上比较 GD, NAG, BFGS, L-BFGS 的收敛轨迹(画等高线)。

13. 小结

  • 牛顿法:曲率感知、局部二次收敛,但要"付二阶账"。
  • BFGS/L-BFGS用历史学曲率,既保持下降与正定,又避免显式 Hessian,工程上性价比极高。
  • 实战口诀能二阶就二阶;难二阶用 L-BFGS;深非凸靠一阶,但别忘了预条件与良好线搜索。
相关推荐
NAGNIP1 分钟前
一文搞懂深度学习中的通用逼近定理!
人工智能·算法·面试
冬奇Lab1 小时前
一天一个开源项目(第36篇):EverMemOS - 跨 LLM 与平台的长时记忆 OS,让 Agent 会记忆更会推理
人工智能·开源·资讯
冬奇Lab1 小时前
OpenClaw 源码深度解析(一):Gateway——为什么需要一个"中枢"
人工智能·开源·源码阅读
AngelPP5 小时前
OpenClaw 架构深度解析:如何把 AI 助手搬到你的个人设备上
人工智能
宅小年5 小时前
Claude Code 换成了Kimi K2.5后,我再也回不去了
人工智能·ai编程·claude
九狼5 小时前
Flutter URL Scheme 跨平台跳转
人工智能·flutter·github
ZFSS5 小时前
Kimi Chat Completion API 申请及使用
前端·人工智能
天翼云开发者社区7 小时前
春节复工福利就位!天翼云息壤2500万Tokens免费送,全品类大模型一键畅玩!
人工智能·算力服务·息壤
知识浅谈7 小时前
教你如何用 Gemini 将课本图片一键转为精美 PPT
人工智能
Ray Liang7 小时前
被低估的量化版模型,小身材也能干大事
人工智能·ai·ai助手·mindx