You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在使用scipy.optimize.minimize(L-BFGS-B)时记录步长

关于scipy L-BFGS-B提取线搜索步长的解决方案

scipy官方封装的scipy.optimize.minimize的L-BFGS-B接口默认没有直接返回线搜索步长的参数,你可以通过以下两种方式获取步长:

方案1:自定义回调+包装函数计算步长(无需修改源码,推荐)

L-BFGS-B每次迭代满足更新规则:x_new = x_old + α * p,其中α是线搜索得到的最终接受步长,p是本次迭代的搜索方向。你可以通过记录每次迭代的参数、梯度计算得到步长,参考实现代码如下:

import numpy as np
import pandas as pd
from scipy.optimize import minimize

class LBFGSStepRecorder:
    def __init__(self, obj_func, grad_func, m=10):
        self.obj_func = obj_func
        self.grad_func = grad_func
        self.m = m # L-BFGS保留的历史更新对数量,和算法默认值保持一致
        self.prev_x = None
        self.prev_grad = None
        self.prev_dir = None
        self.step_lengths = []
        self.history_pairs = [] # 存储L-BFGS的{s,y}更新对

    def wrapped_grad(self, x):
        grad = self.grad_func(x)
        # 首次迭代搜索方向为负梯度
        if self.prev_x is None:
            self.prev_dir = -grad
        else:
            # 标准L-BFGS双循环逻辑计算搜索方向
            s = x - self.prev_x
            y = grad - self.prev_grad
            sy_dot = np.dot(s, y)
            if sy_dot > 1e-10: # 避免数值问题
                self.history_pairs.append((s, y, 1/sy_dot))
                if len(self.history_pairs) > self.m:
                    self.history_pairs.pop(0)
            q = grad.copy()
            alpha_list = []
            for s_i, y_i, rho_i in reversed(self.history_pairs):
                alpha_i = rho_i * np.dot(s_i, q)
                q -= alpha_i * y_i
                alpha_list.append(alpha_i)
            # 初始逆海森缩放
            if self.history_pairs:
                s_last, y_last, _ = self.history_pairs[-1]
                r = q * (np.dot(s_last, y_last) / np.dot(y_last, y_last))
            else:
                r = q
            for (s_i, y_i, rho_i), alpha_i in zip(self.history_pairs, reversed(alpha_list)):
                beta_i = rho_i * np.dot(y_i, r)
                r += (alpha_i - beta_i) * s_i
            self.prev_dir = -r
        self.prev_grad = grad.copy()
        return grad

    def callback(self, x):
        if self.prev_x is not None and self.prev_dir is not None:
            delta_x = x - self.prev_x
            # 步长为delta_x在搜索方向上的投影长度
            dir_norm = np.linalg.norm(self.prev_dir)
            alpha = np.linalg.norm(delta_x) / dir_norm if dir_norm > 1e-12 else 0
            self.step_lengths.append(alpha)
        self.prev_x = x.copy()

# 示例使用
def objective(x):
    return x[0]**2 + x[1]**2

def gradient(x):
    return np.array([2*x[0], 2*x[1]])

recorder = LBFGSStepRecorder(objective, gradient)
res = minimize(objective, x0=np.array([10, 10]), method='L-BFGS-B', jac=recorder.wrapped_grad, callback=recorder.callback)

# 导出步长到csv
pd.DataFrame({
    '迭代次数': range(1, len(recorder.step_lengths)+1),
    '步长': recorder.step_lengths
}).to_csv('lbfgs步长记录.csv', index=False, encoding='utf-8-sig')

方案2:直接调用底层L-BFGS-B接口

如果你需要获取线搜索过程中产生的中间测试步长(而非最终接受的步长),可以直接调用scipy底层的scipy.optimize._lbfgsb.minimize_lbfgsb非公开接口,该接口的回调参数可以拿到更多内部状态,你可以直接在回调中提取步长变量。注意该接口属于scipy内部实现,不同版本的参数结构可能存在差异,不建议跨版本使用。

内容的提问来源于stack exchange,提问作者lhell

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 00:36:00