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;深非凸靠一阶,但别忘了预条件与良好线搜索。
相关推荐
HZjiangzi2 小时前
文物古董如何实现高保真三维数字化?思看科技3DeVOK MT彩色扫描+智能贴图方案权威解析
人工智能·科技·制造·三维扫描仪
junziruruo2 小时前
三叉预测头Trident prediction head(RGBT目标跟踪以MTNET为例)
人工智能·计算机视觉·目标跟踪
光羽隹衡2 小时前
计算机视觉--Opencv(图像形态学)
人工智能·opencv·计算机视觉
懈尘2 小时前
基于Spring Boot与LangChain4j的AI驱动新闻系统设计与工程实现
java·大数据·人工智能·spring boot·后端·langchain
倔强的石头1062 小时前
假设空间与版本空间 —— 机器学习是 “猜规律” 的过程
人工智能·机器学习
永远都不秃头的程序员(互关)2 小时前
【决策树深度探索(五)】智慧之眼:信息增益,如何找到最佳决策问题?
算法·决策树·机器学习
flying_13142 小时前
图神经网络分享系列-GGNN(GATED GRAPH SEQUENCE NEURAL NETWORKS)(三)
人工智能·深度学习·神经网络·图神经网络·ggnn·门控机制·图特征学习
cooldream20092 小时前
Agent Skill:新一代 AI 设计模式的原理、实践与 MCP 协同应用解析
人工智能·mcp·agent skill
劈星斩月2 小时前
机器学习(Machine Learning)系列
深度学习·神经网络·机器学习