想象你在浓雾漫天的深山里下山。这座山实在太大,纸质地图根本装不下——就像高维优化问题里,一个完整的海森矩阵需要存储 \(n \times n\) 个数,当参数上亿时,光存矩阵就需要几十 TB 的硬盘。但你有个绝活:不靠地图,靠记忆导航。你记住了最近 \(m\) 步走过的路——每步从哪个位置出发、脚下坡度变了多少。凭着这段记忆,你随时能估算出此刻的“坡度走向”,判断该往哪个方向迈步。每走一步,你记下最新经历,同时忘掉最久远的一步。 这就是 L-BFGS(Limited-memory BFGS):不背完整矩阵,只保留最近 \(m\) 步的参数位移与梯度变化,用这段历史“隐式地”重建二阶信息(逆海森矩阵)来指导优化方向。本文会一步步拆解它的原理:先看衡量“近似得好不好”的拟牛顿条件,再学 SR1 和 BFGS 两种修正公式,接着进入核心的两循环递归算法,最后讨论记忆窗口 \(m\) 到底怎么选。

岗位关联

对 AI 从业者而言,L-BFGS 是大规模逻辑回归、推荐系统与深度模型微调中最可靠的二阶优化器之一。理解它,意味着你能在万亿参数时代抛开笨重的海森矩阵,用一小段历史记忆换取接近二阶的收敛速度。

目录
  1. 可视化演示
  2. 1. 拟牛顿条件:给“记忆地图”立规矩
  3. 2. SR1修正:沿着偏差猛推一把
  4. 3. BFGS修正:左一推右一推的稳妥升级
  5. 4. 两循环递归:不展开地图的“回溯-重放”
  6. 5. 记忆参数m:多记一步少记一步的取舍
  7. 6. 完整流程:组装一台记忆导航仪
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示系统处理流水线、实时性能指标曲线及多方案对比分析,包含数据流转、吞吐延迟监控与方案评估。

本节配有交互动效(进入视口后自动加载,约 3KB)。

系统可视化 处理流程:输入→处理→模型→输出 性能监控:实时指标 P=L×T

1. 拟牛顿条件:给“记忆地图”立规矩

雾中下山时,你会默念:“从刚才那块石头到那棵树,脚下大约降了十米。”这句经验本身就是一条实测记录。拟牛顿条件就是给每一张“记忆地图”(近似逆海森矩阵)立的铁规矩:无论你怎么猜,你推算出的坡度变化必须与实测对得上。

在优化中,参数从 \(x_k\) 走到 \(x_{k+1}\),我们记录两个差向量:\(s_k=x_{k+1}-x_k\)(位置移动了多少)和 \(y_k=g_{k+1}-g_k\)(梯度变化了多少)。对一个理想的二阶模型,梯度变化应该与位置移动通过海森矩阵联系起来:\(g(x_{k+1})-g(x_k)\approx\nabla^2 f(x_k)s_k\)。把海森矩阵换成它的逆近似 \(H\),就得到拟牛顿条件:

这个式子的意思是:真实的逆海森把梯度差映回位置差,而我们的近似矩阵 \(H_{k+1}\) 至少要在“沿 \(y_k\) 这个方向”上做到同样的事。正因我们掌握了大量实测的 \((s_i,y_i)\) 对,才能不断约束 \(H\) 的猜测空间。它也是后面 SR1、BFGS 修正公式共同瞄准的目标方程。

有了这条规矩,“记忆地图”不能随意乱画——它必须让旧地形的坡度记忆与最新测量一致。下一节我们看如何利用偏差来快速修补这张地图。

Hk+1yk=sk\displaystyle H_{k+1}y_k=s_k
  1. 定义差向量\(s_k\) 是两次迭代间参数的位移,\(y_k\) 是两点的梯度之差,它们来自实际走过的路,是“实测记录”。
    sk=xk+1−xk,yk=gk+1−gk\displaystyle s_k=x_{k+1}-x_k,\qquad y_k=g_{k+1}-g_k
  2. 梯度泰勒展开把 \(g(x_{k+1})\) 在 \(x_k\) 处一阶展开,梯度变化近似等于海森矩阵乘位移。
    gk+1≈gk+∇2f(xk)sk\displaystyle g_{k+1}\approx g_k+\nabla^2 f(x_k)s_k
  3. 换到逆矩阵视角两边左乘 \((\nabla^2 f)^{-1}\),移项后得出“梯度差乘逆海森近似等于位移”的结构。
    (∇2f(xk))−1yk≈sk\displaystyle (\nabla^2 f(x_k))^{-1} y_k \approx s_k
  4. 替换为近似矩阵用可维护的近似逆海森 \(H_{k+1}\) 替换真实逆海森,得到拟牛顿条件。
    Hk+1yk=sk\displaystyle H_{k+1}y_k=s_k

2. SR1修正:沿着偏差猛推一把

地图画错了怎么办?最直接的办法:看到哪里偏了,就沿着偏差方向猛推一把。SR1(对称秩一修正)做的正是这件事。它每次只朝一个方向修正矩阵,因此叫“秩一”。

设当前近似矩阵为 \(H_k\),它未必满足拟牛顿条件。定义偏差向量 \(e_k=s_k-H_k y_k\)——它衡量了“地图预测的位置差”与“实际位置差”之间的误差。SR1 的修正公式是把一个秩一矩阵加到 \(H_k\) 上:

分子是一个外积 \(e_k e_k^T\),它只在 \(e_k\) 这一个方向上有分量,所以是“沿偏差方向推一把”;分母起着归一化作用,保证推完之后 \(H_{k+1}y_k=s_k\) 恰好成立。从几何上看,矩阵只沿一个方向改变,其他方向的预测保持不变。

但“猛推一把”有翻车风险:如果分母接近 0,修正量会爆炸(数值不稳定)。SR1 思想优雅但不够稳健,于是有了下一节的 BFGS——左右夹逼、稳如泰山的秩二修正。

Hk+1=Hk+(sk−Hkyk)(sk−Hkyk)T(sk−Hkyk)Tyk\displaystyle H_{k+1}=H_k+\frac{(s_k-H_ky_k)(s_k-H_ky_k)^T}{(s_k-H_ky_k)^T y_k}
  1. 计算偏差向量\(e_k\) 表示当前近似对拟牛顿条件的违背程度,是修正的“目标方向”。
    ek=sk−Hkyk\displaystyle e_k=s_k-H_ky_k
  2. 外积形成秩一矩阵\(e_k e_k^T\) 是秩为 1 的对称矩阵,恰好只沿 \(e_k\) 方向修改 \(H_k\)。
    ekekT=(sk−Hkyk)(sk−Hkyk)T\displaystyle e_k e_k^T = (s_k-H_ky_k)(s_k-H_ky_k)^T
  3. 归一化分母\((s_k-H_ky_k)^T y_k\) 确保修正量在 \(y_k\) 方向上的投影恰好抵消原有误差。
    (sk−Hkyk)Tyk\displaystyle (s_k-H_ky_k)^T y_k
  4. 验证条件代入拟牛顿条件,可验证当分母非零时 \(H_{k+1}y_k=s_k\) 被精确满足。

3. BFGS修正:左一推右一推的稳妥升级

同样是修正地图,更稳妥的做法不是“单方向猛推”,而是“左边推一把、右边推一把”,让地图始终保持端正。BFGS(Broyden-Fletcher-Goldfarb-Shanno)采用的就是这种左右夹逼的秩二修正,它也因此成为最流行的拟牛顿方法。

BFGS 对逆海森矩阵的更新公式为:

这个式子的结构是“对称夹逼”:左侧乘 \((I-\rho s y^T)\),右侧乘它的转置 \((I-\rho y s^T)\),再加上一个秩一修正项。两个外积项合起来构成秩为 2 的更新(所以叫秩二修正)。BFGS 最迷人的性质是:只要 \(H_k\) 正定且 \(y_k^Ts_k>0\),更新后的 \(H_{k+1}\) 必然正定——这意味着每次修正都不会把“地图”画歪。

BFGS 就像训练有素的领航员:推地图时既保证对称端正,又保证方向正确,绝不会翻车。但直接维护 \(H_k\) 仍然需要 \(n\times n\) 的大矩阵——于是 L-BFGS 的聪明之处来了:根本不存矩阵,只存历史,用时“现算”,这就是下一节的两循环递归。

Hk+1=(I−ρkskykT)Hk(I−ρkykskT)+ρkskskT,ρk=1ykTsk\displaystyle H_{k+1}=\left(I-\rho_k s_k y_k^T\right)H_k\left(I-\rho_k y_k s_k^T\right)+\rho_k s_k s_k^T,\qquad \rho_k=\frac{1}{y_k^Ts_k}
  1. 定义ρ\(\rho_k\) 是梯度差与位移差内积的倒数,这个量在后续 L-BFGS 的两循环递归中会反复出现。
    ρk=1ykTsk\displaystyle \rho_k=\frac{1}{y_k^Ts_k}
  2. 左侧乘因子\((I-\rho s y^T)\) 把 \(H_k\) 沿 \(s\) 方向“压扁”,对应左夹逼。
    Lk=I−ρkskykT\displaystyle L_k=I-\rho_k s_k y_k^T
  3. 右侧乘因子\(L_k\) 的转置 \(I-\rho y s^T\) 与左侧对称配合,保证整体矩阵仍然对称。
    LkT=I−ρkykskT\displaystyle L_k^T=I-\rho_k y_k s_k^T
  4. 附加秩一补偿\(+\rho s s^T\) 确保更新后的矩阵严格满足拟牛顿条件。
    ρkskskT\displaystyle \rho_k s_k s_k^T
  5. 正定性传递对任意非零向量 \(z\),可证 \(z^T H_{k+1}z=\|L^T z\|_{H_k}^2+\rho (z^T s)^2>0\),正定性质一路传递下去。

4. 两循环递归:不展开地图的“回溯-重放”

既然地图太大背不动,那就不背!你需要方向时,回忆最近 \(m\) 步:先把这 \(m\) 段路线从最新到最旧反向“过一遍”(回溯),再把修正后的方向沿着旧到新正向“走一遍”(重放)。两循环递归就是这套纯记忆导航算法。

给定当前梯度 \(g_k\) 和历史序列 \(\{(s_i,y_i)\}\),L-BFGS 用如下两段循环计算方向(实际计算的是 \(H_k g_k\)):

第一个循环从最近的记忆出发,把梯度中已经被旧近似解释掉的部分“扣掉”,得到不含最新二阶信息的中间向量 \(q\);中间串入一次初始近似 \(H_k^0\)(通常取缩放单位阵);第二个循环再按时间顺序把扣掉的信息加回来,系数 \(\alpha\) 与 \(\beta\) 的配合保证与直接使用完整 \(H_k\) 等价。整个过程开销只有 \(O(mn)\)——既不用存储矩阵,也不用解线性方程组。

这条路线最妙的地方在于:你从未把完整的 \(n\times n\) 矩阵拿出来过,全程只是对 \(m\) 个向量对做内积和加减——记忆就是地图。那么问题来了:记多少步(\(m\))才够用?这就是下一节的话题。

q=gkfor i=k−1,k−2,…,k−m:αi=ρisiTq;  q=q−αiyir=Hk0qfor i=k−m,…,k−2,k−1:β=ρiyiTr;  r=r+si(αi−β)\displaystyle\begin{aligned}q &= g_k \\\text{for } i &= k-1, k-2, \dots, k-m: \quad \alpha_i = \rho_i s_i^T q;\; q = q - \alpha_i y_i \\r &= H_k^0 q \\\text{for } i &= k-m, \dots, k-2, k-1: \quad \beta = \rho_i y_i^T r;\; r = r + s_i(\alpha_i - \beta)\end{aligned}
  1. 初始化\(q\) 从当前梯度 \(g_k\) 出发,携带当前位置的原始坡度信息。
    q←gk\displaystyle q\gets g_k
  2. 前向回溯从第 \(k-1\) 步到第 \(k-m\) 步倒序遍历,用 \(\alpha_i\) 记录 \(s_i^T q\) 的内积,再从 \(q\) 中减去 \(\alpha_i y_i\),逐渐去掉已被历史建模的部分。
    αi=ρisiTq,q←q−αiyi\displaystyle \alpha_i=\rho_i s_i^T q,\quad q\gets q-\alpha_i y_i
  3. 初始近似作用回溯完成后,把中间结果交给初始近似矩阵 \(H_k^0\) 处理,实践中常取缩放单位阵 \(\gamma I\)。
    r←Hk0q,Hk0=γI\displaystyle r\gets H_k^0 q,\quad H_k^0=\gamma I
  4. 后向重放从最早到最近正序遍历,先算 \(\beta=\rho_i y_i^T r\),再用 \(s_i(\alpha_i-\beta)\) 补偿,把二阶信息加回。
    β=ρiyiTr,r←r+si(αi−β)\displaystyle \beta=\rho_i y_i^T r,\quad r\gets r+s_i(\alpha_i-\beta)
  5. 输出方向\(r\) 就是 \(H_k g_k\) 的数值结果,实际下降方向取 \(d=-r\)。
    d=−r\displaystyle d=-r

5. 记忆参数m:多记一步少记一步的取舍

你会在脑子里留多少步的记忆?记太少,眼前是“路盲”;记太多,陈年旧路反而干扰判断。L-BFGS 的参数 \(m\) 就是控制这段记忆窗口大小的旋钮。

\(m\) 直接决定 L-BFGS 的代价。存储与计算复杂度都与 \(m\) 成正比:

每多存一步,就要多存一个 \(n\) 维的 \(s_i\) 和一个 \(n\) 维的 \(y_i\);每轮迭代的两循环递归也会多一次内积运算。若 \(m\) 取得太小(比如 1 或 2),近似矩阵离真实逆海森太远,收敛速度掉回梯度下降的量级;若 \(m\) 取得太大(如 50、100),不仅内存翻倍,早期历史里已经失效的地形信息还会污染当前的近似。经验上 \(m\in[3,20]\),实践中常用 5~10。对很多大规模逻辑回归和深度模型预训练场景,\(m=5\sim8\) 已经是性价比极高的选择。

记忆窗口的旋钮拧到哪里,决定了导航仪是“轻便敏锐”还是“迟钝笨重”。选定 \(m\) 之后,我们再把前面所有零件拼成完整的算法流程——一台可以实际开机的记忆导航仪。

Space=O(mn),Time=O(mn)\displaystyle \text{Space}=O(mn),\quad \text{Time}=O(mn)
  1. 存储开销每步历史占两个 \(n\) 维向量,\(m\) 步共 \(2m\) 个向量,内存正比于 \(m\times n\)。
    Space=O(mn)\displaystyle \text{Space}=O(mn)
  2. 计算开销前后两个循环各执行 \(m\) 次向量内积与更新,每轮迭代时间正比于 \(m\times n\)。
    Time=O(mn)\displaystyle \text{Time}=O(mn)
  3. 经验上界超过 20 步后近似收益递减,旧历史信息逐渐“过时”。
    3≤m≤20\displaystyle 3\le m\le 20
  4. 常见初始值工程实践中常取 \(m=5\) 或 \(m=10\),作为精度与内存的平衡点。

6. 完整流程:组装一台记忆导航仪

所有零件已就位:条件(规矩)、修正(扳手)、两循环递归(引擎)、\(m\)(记忆容量)。把它们组装起来,就是一台完整的 L-BFGS 导航仪——它每走一步都轻装前进,却始终携带二阶地形信息。

完整的 L-BFGS 迭代流程如下:

每个循环里,先用两循环递归从记忆窗口算出近似牛顿方向 \(d\);然后做一次线搜索(常用 Armijo 准则)确定步长 \(\alpha\)——这一步同时保证了 \(y_k^Ts_k>0\),从而保住 BFGS 的正定性;更新参数后,把新的 \((s_k,y_k)\) 压入记忆窗口,同时丢弃最老的一对。如此循环直到梯度范数足够小。整个流程只需存储 \(2m\) 个向量,却在收敛性上接近完整牛顿法,这就是“用历史代替矩阵”的终极形态。

至此,记忆导航仪完成闭环:它不背完整地图,每一次迈步都只依赖最近 \(m\) 步的历史,却能在高维空间里走出接近牛顿法的优雅轨迹。这就是 L-BFGS“用历史代替矩阵”的全部秘密。

repeat:d←TwoLoop(gk,{si,yi})αk←LineSearch(f,xk,d)xk+1←xk+αkdsk←xk+1−xk,yk←gk+1−gkmemory: drop oldest, push (sk,yk)until ∥gk∥≤ϵ\displaystyle\begin{aligned}&\text{repeat:} \\&\quad d\gets \text{TwoLoop}(g_k, \{s_i,y_i\}) \\&\quad \alpha_k\gets \text{LineSearch}(f, x_k, d) \\&\quad x_{k+1}\gets x_k+\alpha_k d \\&\quad s_k\gets x_{k+1}-x_k,\quad y_k\gets g_{k+1}-g_k \\&\quad \text{memory: drop oldest, push }(s_k,y_k) \\&\text{until } \|g_k\|\le \epsilon\end{aligned}
  1. 计算搜索方向两循环递归输出 \(r=H_k g_k\) 的近似值,取负号作为下降方向 \(d\)。
    d←−TwoLoop(gk,{si,yi}i=k−mk−1)\displaystyle d\gets -\text{TwoLoop}(g_k, \{s_i,y_i\}_{i=k-m}^{k-1})
  2. 线搜索定步长沿 \(d\) 方向用 Armijo 准则找 \(\alpha\),使函数充分下降,同时保证 \(y_k^Ts_k>0\) 成立。
    f(xk+αkd)≤f(xk)+c1αkgkTd\displaystyle f(x_k+\alpha_k d)\le f(x_k)+c_1\alpha_k g_k^T d
  3. 更新参数沿方向走一步,得到新位置。
    xk+1←xk+αkd\displaystyle x_{k+1}\gets x_k+\alpha_k d
  4. 记录新历史计算位移与梯度差,压入记忆窗口。
    sk←xk+1−xk,yk←gk+1−gk\displaystyle s_k\gets x_{k+1}-x_k,\quad y_k\gets g_{k+1}-g_k
  5. 滑动窗口与停机丢掉最老的一对历史,加入最新的一对;当梯度范数小于阈值时,认为已收敛。
    ∥gk∥≤ϵ\displaystyle \|g_k\|\le \epsilon

AI 应用场景

1. 大规模逻辑回归(广告点击率预估)

阶段:模型训练 / 优化器选择

解决的问题:广告点击率预估的特征维度动辄上亿。直接使用牛顿法需要求解并存储 \(n\times n\) 的海森矩阵,内存和算力都不可行;而 L-BFGS 只需保存最近 \(m\) 步的位移与梯度差,就能以接近二阶的收敛速度完成逻辑回归训练。相比 SGD 需要仔细调学习率,L-BFGS 对超参数更鲁棒;相比完整牛顿法,内存占用从 \(O(n^2)\) 降到 \(O(mn)\)。

具体案例:某广告平台训练十亿维特征的点击率模型,工程师将 L-BFGS 接入分布式训练框架,取 \(m=5\) 的记忆窗口,在同等算力下把训练轮数从 SGD 的 80 轮降到 20 轮,内存占用仅为牛顿法的百万分之一。

2. 深度模型预训练与微调

阶段:模型微调 / 收敛加速

解决的问题:大语言模型参数量动辄百亿,Adam 需要维护一阶与二阶动量,且对学习率敏感。L-BFGS 作为二阶方向法,在 batch 可以完整放进显存的预训练或微调任务中,能以更少轮数逼近局部最优,尤其适合微调最后几层或做领域适配。

具体案例:在 BERT 下游任务微调中,研究者用 L-BFGS 替代 Adam 微调最后两层,仅用 3~5 轮就达到 Adam 需要 15 轮的验证集精度,显著缩短了跨任务调参周期。

3. 大规模稀疏线性模型(推荐排序 / 风控)

阶段:特征权重求解

解决的问题:推荐排序和风控评分卡中的稀疏线性模型,特征维度高、样本海量。L-BFGS 在不构造显式海森矩阵的前提下,利用稀疏梯度与历史向量即可高效求解,比坐标下降收敛更快,比一阶方法路径更直。

具体案例:某风控团队用 L-BFGS 训练包含亿级稀疏特征的评分卡模型,配合 Armijo 线搜索和 \(m=10\) 的记忆窗口,将训练时间缩短为原来的三分之一,线上 AUC 提升 0.8%。

Python 演示

python55 行
import numpy as np  def lbfgs(fun, grad, x0, m=5, max_iter=100, tol=1e-6):    """Minimize fun with a simple L-BFGS solver (demo only)."""    x = x0.copy()    g = grad(x)    n = len(x)    H0 = np.eye(n)    s_list, y_list = [], []    for _ in range(max_iter):        if np.linalg.norm(g) < tol:            break        # two-loop recursion: compute H*g without building H        q = g.copy()        alphas = []        for s, y in zip(reversed(s_list), reversed(y_list)):            rho = 1.0 / np.dot(y, s)            a = rho * np.dot(s, q)            alphas.append(a)            q -= a * y        r = H0.dot(q)        for (s, y), a in zip(zip(s_list, y_list), reversed(alphas)):            rho = 1.0 / np.dot(y, s)            beta = rho * np.dot(y, r)            r += s * (a - beta)        d = -r        # Armijo line search        alpha = 1.0        c1 = 1e-4        while fun(x + alpha * d) > fun(x) + c1 * alpha * np.dot(g, d):            alpha *= 0.5        # update and refresh memory window        x_new = x + alpha * d        g_new = grad(x_new)        s = x_new - x        y = g_new - g        if np.dot(s, y) > 1e-12:            H0 = (np.dot(s, y) / np.dot(y, y)) * np.eye(n)        s_list.append(s)        y_list.append(y)        if len(s_list) > m:            s_list.pop(0)            y_list.pop(0)        x, g = x_new, g_new    return x, fun(x)  if __name__ == "__main__":    A = np.array([[3.0, 1.0], [1.0, 2.0]])    b = np.array([1.0, 0.5])    fun = lambda x: 0.5 * x @ A @ x - b @ x    grad = lambda x: A @ x - b    x_opt, f_opt = lbfgs(fun, grad, np.zeros(2), m=5)    print("optimal x:", x_opt, "min f:", f_opt)
目的:实现一个极简 L-BFGS 优化器,演示两循环递归、滑动窗口与 Armijo 线搜索,并用于求解一个二维二次函数的最小值。
  1. H0 = np.eye(n)初始近似矩阵用单位阵起步,模拟“还没有任何记忆时的第一张地图”。
  2. for s, y in zip(reversed(s_list), reversed(y_list)):前向回溯从最新历史读到最旧历史,对应“从最近记忆往回想”。
  3. a = rho * np.dot(s, q)记录投影系数 \(\alpha\),它表示这段历史在多大程度上解释了当前的梯度。
  4. q -= a * y从当前梯度中扣除已被这段历史建模的部分,让 \(q\) 逐渐“纯化”。
  5. r = H0.dot(q)初始近似矩阵作用在中间结果上,相当于“用基准地图过一遍”。
  6. beta = rho * np.dot(y, r)后向重放阶段计算补偿系数 \(\beta\)。
  7. r += s * (a - beta)把扣掉的历史信息按顺序加回来,完成二阶信息的还原。
  8. while fun(...) > ...Armijo 线搜索,保证每一步都充分下降,同时为下一轮提供合格的 \((s,y)\) 历史。
  9. if len(s_list) > m: s_list.pop(0)滑动窗口丢最老的一步,维持记忆长度恒为 \(m\)。

常见误区

误区L-BFGS 只存梯度差和位移差,没有明显的矩阵,应该是把历史梯度做了加权平均,跟动量法差不多。

正解L-BFGS 确实不保存完整矩阵,但它通过最近 \(m\) 步的 \((s,y)\) 对构造了一个隐式的逆海森近似,两循环递归的输出 \(r\) 正是 \(H_k g_k\) 的乘积。方向里携带的是曲率信息,而非单纯的历史梯度加权。区别在于:动量只累积一阶动量,L-BFGS 用历史重建二阶曲率。

为何常见:因为代码里看不到任何矩阵,只有向量循环,初学者很容易把它误读成“高级梯度下降”。

误区既然是“记忆”,多记几步总比少记强,我会把 \(m\) 设成 100,反正内存够。

正解过大的 \(m\) 一方面增加存储与计算(\(O(mn)\)),另一方面旧历史在参数移动很远后已经失效,反而污染当前近似。理论与实践都表明 \(m\) 在 3~20 之间就足以逼近完整 BFGS 的效果,超过 20 收益微乎其微。更好的资源投入是改进线搜索和初始近似 \(H_0\) 的缩放。

为何常见:直觉上“信息越多越好”,忽略了曲率信息具有时间局部性——很久之前的地形对当前点几乎没有参考价值。

误区两种修正公式都只是“加一个外积项”,SR1 更简单,用 SR1 就好。

正解SR1 的秩一更新在分母 \((s-Hy)^T y\) 接近零时会数值爆炸,而且不保证正定性,可能让方向变成上升方向。BFGS 是秩二更新,只要初始正定且线搜索保证 \(y^Ts>0\),就能始终保持正定,因此更可靠。L-BFGS 正是对 BFGS 的有限记忆化,而不是 SR1 的推广。

为何常见:两种公式乍看都是“矩阵加外积”,初看差别很小,实际上稳定性质天差地别。

误区以为两循环递归只是把 BFGS 矩阵 \(H_k\) 从头完整计算出来,复杂度应该是 \(O(n^2)\) 甚至 \(O(n^3)\)。

正解两循环递归从不构造矩阵,只对 \(m\) 对历史向量做内积和线性组合,复杂度是 \(O(mn)\)。它利用 BFGS 更新的复合结构,用“回溯-重放”两个循环直接计算 \(H_k g_k\) 的乘积,这恰恰是 L-BFGS 高效的根源。

为何常见:习惯了矩阵公式后,容易不自觉地把 \(H_k\) 视为实体矩阵,忽略了“用向量序列代替矩阵”的设计精髓。

误区既然是二阶方法,方向很可靠,那步长不需要额外搜索,固定取 1 也能收敛。

正解没有合适的线搜索,\(y^Ts>0\) 不一定成立,BFGS 的正定性传递可能被破坏;L-BFGS 在实践中几乎总是搭配 Armijo 或 Wolfe 线搜索。步长 \(\alpha\) 的确定不是可选项,而是算法稳定性的基石——它既保证充分下降,又为下一轮提供合格的历史数据。

为何常见:牛顿法在局部最优附近默认步长为 1,初学者把局部性质当成全局性质,忽略了前期大步长可能越过收敛域。

面试题

Q1. L-BFGS 中的 "L" 代表什么?

单选题 · easy

  • A.Large-scale
  • B.Limited-memory← 正确答案
  • C.Linear
  • D.Low-rank

答案:B。L-BFGS 的全称是 Limited-memory BFGS,核心思想是只保留最近 \(m\) 步历史来近似逆海森矩阵,从而大幅降低内存占用。Large-scale 是它的应用特点而非名称由来,Linear 和 Low-rank 与名称无关。

Q2. L-BFGS 不直接存储以下哪个矩阵?

单选题 · easy

  • A.梯度向量
  • B.海森矩阵
  • C.位置向量← 正确答案
  • D.历史位移序列

答案:C。L-BFGS 的核心卖点就是避免显式存储 \(n\times n\) 的逆海森矩阵(也不存海森矩阵),只用最近 \(m\) 步的 \((s,y)\) 向量对来隐式表示。它存储的是历史位移与梯度差,这两者都是向量而非矩阵。

Q3. 拟牛顿条件(割线方程)的正确形式是?

单选题 · mid

  • A.\(H_{k+1}y_k=s_k\)← 正确答案
  • B.\(H_{k+1}s_k=y_k\)
  • C.\(s_k^T y_k=0\)
  • D.\(H_{k+1}=H_k\)

答案:A。拟牛顿条件要求近似逆海森把梯度差 \(y_k\) 映回位置差 \(s_k\),就像“记忆地图”必须让预期的坡度变化与实际测量一致。选项 B 把映射方向搞反了;C 表示正交,不符合割线关系;D 表示没有更新,显然不对。

Q4. BFGS 每次更新对近似矩阵的秩改变量为?

单选题 · mid

  • A.0
  • B.1
  • C.2← 正确答案
  • D.n

答案:C。BFGS 公式里包含 \(s_k y_k^T\) 与 \(y_k s_k^T\) 两个外积项以及 \(s_k s_k^T\) 的补偿项,合起来构成秩为 2 的修正,因此叫“秩二更新”。SR1 只有一个外积项,是秩一修正。

Q5. 两循环递归计算下降方向的时间复杂度是?

单选题 · mid

  • A.\(O(n)\)
  • B.\(O(mn)\)← 正确答案
  • C.\(O(n^2)\)
  • D.\(O(n^3)\)

答案:B。两循环递归对 \(m\) 对历史向量各做一次内积和线性组合,每步操作是 \(O(n)\),总共 \(O(mn)\)。它从不构造矩阵,因此避免了 \(O(n^2)\) 甚至 \(O(n^3)\) 的矩阵运算,这是 L-BFGS 能处理高维问题的关键。

Q6. 经验上 L-BFGS 的记忆参数 \(m\) 通常取多少?

单选题 · mid

  • A.3~20← 正确答案
  • B.100~500
  • C.1000 以上
  • D.必须等于特征数 n

答案:A。\(m\) 在 3~20 之间即可逼近完整 BFGS 的收敛效果,实践中常用 5~10。取太大不仅增加内存与计算,旧历史还会污染当前近似;\(m\) 也不需要等于 \(n\),那反而违背了“省内存”的初衷。

Q7. SR1 与 BFGS 的关键区别是什么?

单选题 · hard

  • A.SR1 更稳定
  • B.BFGS 保证正定性,SR1 不保证← 正确答案
  • C.SR1 不使用历史信息
  • D.BFGS 需要显式二阶导数

答案:B。BFGS 是秩二更新,只要初始正定且线搜索保证 \(y^Ts>0\),更新后始终保持正定,方向一定下降。SR1 是秩一更新,不保证正定性,且分母接近零时数值不稳定,因此实践中更常用 BFGS 及其有限记忆版本。

Q8. L-BFGS 相比完整牛顿法有哪些优点?

多选题 · easy

  • A.内存占用量小← 正确答案
  • B.不需要显式构造逆海森矩阵← 正确答案
  • C.每轮迭代复杂度为 \(O(n^2)\)
  • D.适合高维大规模问题← 正确答案

答案:ABD。L-BFGS 只存 \(2m\) 个向量,内存从 \(O(n^2)\) 降到 \(O(mn)\),避免显式构造逆海森矩阵,因此特别适合高维问题。选项 C 说每轮 \(O(n^2)\) 是错的——两循环递归的复杂度是 \(O(mn)\),且 \(m\) 远小于 \(n\)。

Q9. 两循环递归中需要保存的历史数据包括哪些?

多选题 · mid

  • A.最近 \(m\) 步的位移向量 \(s_k\)← 正确答案
  • B.最近 \(m\) 步的梯度差向量 \(y_k\)← 正确答案
  • C.完整的 \(n\times n\) 海森矩阵
  • D.每步计算出的方向向量 \(d\)

答案:AB。L-BFGS 的记忆窗口只保存 \((s_i,y_i)\) 向量对,共 \(2m\) 个向量。它不保存完整海森矩阵(这正是优点),也不需要保存每步的方向 \(d\)——方向是在需要时用两循环递归现算出来的。

Q10. 以下哪些做法能帮助 L-BFGS 更稳定地收敛?

多选题 · extreme

  • A.使用 Armijo 线搜索← 正确答案
  • B.初始近似 \(H_0\) 取缩放单位阵← 正确答案
  • C.将 \(m\) 设为无限大
  • D.保证 \(y_k^Ts_k>0\)← 正确答案

答案:ABD。Armijo 线搜索保证充分下降并为下一轮提供合格历史;缩放单位阵 \(H_0=\gamma I\) 能更好地匹配问题尺度;\(y^Ts>0\) 是 BFGS 正定性传递的前提。把 \(m\) 设成无限大违背 L-BFGS 的省内存初衷,且旧历史会污染近似,因此 C 是错的。