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

scipy least_squares求解非线性最小二乘如何避免重复计算

可行解决方案

最通用、零额外开销的方案是做轻量参数缓存,完全适配scipy.optimize.least_squares的调用逻辑,不需要更换求解器,也不会损失求解速度。

实现原理

scipy.optimize.least_squares在每一步迭代中,对同一个参数点x的调用顺序固定:先调用残差计算函数拿到残差值,紧接着用完全相同的x调用雅可比计算函数。只要缓存上一次计算时的输入x和对应的残差、雅可比结果,两次连续相同输入的调用就只会触发一次真实的目标函数计算,完全避免重复运算。

注意不要直接用functools.lru_cache做缓存,numpy数组为不可哈希类型,会直接触发报错,自己实现十几行代码的轻量缓存类即可:

import numpy as np
from scipy.optimize import least_squares

class CombinedFunCache:
    def __init__(self, calc_fun):
        self.calc_fun = calc_fun  # 传入你自己一次返回(val, jac)的函数
        self._last_x = None
        self._cached_val = None
        self._cached_jac = None

    def _calc_if_needed(self, x):
        # 仅当输入x和上次缓存的x不一致时,才真实调用目标函数
        if self._last_x is None or not np.array_equal(x, self._last_x):
            self._cached_val, self._cached_jac = self.calc_fun(x)
            self._last_x = x.copy()

    def get_residual(self, x):
        self._calc_if_needed(x)
        return self._cached_val

    def get_jac(self, x):
        self._calc_if_needed(x)
        return self._cached_jac

调用方式

把你原本实现的、一次返回残差和雅可比的函数传入缓存类,分别把类的两个方法传给least_squares的fun和jac参数即可,不需要修改你已经写好的目标函数逻辑:

# 假设你自己实现的联合计算函数为my_fun,调用形式为 (residual, jacobian) = my_fun(x)
cache = CombinedFunCache(my_fun)
result = least_squares(
    fun=cache.get_residual,
    x0=initial_guess,
    jac=cache.get_jac,
    # 其余原有参数照常填写,比如method、bounds、tol等
)

注意事项

  • 该方案的缓存命中率为100%,额外开销仅为一次numpy数组相等判断,对整体求解速度的影响可以忽略不计,同时保留了least_squares专用最小二乘求解器(LM法、截断牛顿法、狗腿法)的速度优势,比用通用的minimize快很多。
  • 如果你的目标函数需要传入额外固定参数,直接在初始化CombinedFunCache时传入、或者用functools.partial绑定参数即可,不影响缓存逻辑。
  • 仅当你使用自己提供的解析雅可比时该方案生效,如果你让求解器自动做数值差分计算雅可比,本身就不存在重复计算的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 08:09:26