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

如何统计Scipy minimize相邻迭代间的函数与梯度评估次数

统计相邻迭代间函数/梯度评估次数的实现方法

核心思路是给目标函数、梯度函数增加调用计数逻辑,结合每次迭代触发的callback记录累计评估值,最后做相邻项差值即可得到迭代间隔的评估次数。

具体实现步骤

  • 给目标函数、自定义梯度函数(如果手动传入的话)添加调用计数器,每执行一次评估对应计数器自增。如果不手动传入梯度函数,BFGS默认用有限差分做数值求导,这部分产生的函数调用会自动被目标函数的计数器捕获。
  • 在已有的callback逻辑中,除了记录当前迭代的参数值,同时记录当前时刻累计的函数评估次数、梯度评估次数。
  • 迭代完成后,对累计计数序列做相邻项差值计算,得到的结果就是相邻两次参数迭代之间的评估次数。

可运行代码示例

import numpy as np
from scipy.optimize import minimize

# 初始化存储容器
params = np.empty([0, 3])  # 存储每次迭代的参数
cum_func_evals = []        # 存储每次迭代触发时的累计函数评估次数
cum_grad_evals = []        # 存储每次迭代触发时的累计梯度评估次数

# 计数器
n_func_call = 0
n_grad_call = 0

def rosen(X):
    global n_func_call
    n_func_call += 1
    return (1.0 - X[0])**2 + 100.0 * (X[1] - X[0]**2)**2 + \
           (1.0 - X[1])**2 + 100.0 * (X[2] - X[1]**2)**2

# 若手动传入梯度,可添加对应计数逻辑,不传则BFGS走数值差分,不会触发该计数
def rosen_jac(X):
    global n_grad_call
    n_grad_call += 1
    # 此处补充Rosenbrock函数的梯度计算代码即可
    pass

def callback_params(theta):
    global params, n_func_call, n_grad_call
    params = np.vstack((params, theta))
    cum_func_evals.append(n_func_call)
    cum_grad_evals.append(n_grad_call)

init = np.random.rand(3)
# 若要使用自定义梯度,给minimize传入参数jac=rosen_jac即可
res = minimize(rosen, init, method='BFGS', options={'disp': True}, callback=callback_params)

# 补全初始点评估的计数,对齐参数序列
cum_func_evals = [res.nfev - np.diff(cum_func_evals).sum()] + cum_func_evals
cum_grad_evals = [res.njev - np.diff(cum_grad_evals).sum()] + cum_grad_evals

# 计算相邻迭代间隔的评估次数
iter_interval_func_evals = np.diff(cum_func_evals)
iter_interval_grad_evals = np.diff(cum_grad_evals)

说明

  • 最终得到的iter_interval_func_evals数组第i个元素,就对应params第i行参数和上一个迭代点(初始点或params第i-1行)之间的函数评估次数,梯度评估次数iter_interval_grad_evals同理。
  • 优化返回结果res自带总评估次数字段res.nfev(总函数评估次数)、res.njev(总梯度评估次数),可以用来校验计数结果是否准确。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.31 23:57:35