如何获取scipy.optimize.curve_fit迭代的参数及残差平方和
获取scipy curve_fit迭代过程的参数与残差平方和
你已经通过scipy.optimize.curve_fit完成函数拟合,但需要获取迭代过程中的参数值或对应残差平方和。由于curve_fit底层依赖的minpack库(leastsq函数)是Fortran实现,没有内置的迭代回调机制,无法直接从原函数获取这些信息,可通过以下两种方法实现需求:
方法一:改用scipy.optimize.minimize实现拟合并记录迭代
minimize支持自定义回调函数,可以在每次迭代时保存参数和对应残差平方和。具体步骤如下:
- 定义残差函数和目标函数(残差平方和)
- 定义回调函数,用于记录每次迭代的参数与残差平方和
- 调用
minimize执行拟合,并提取记录的迭代信息
示例代码:
import math import numpy as np import pandas as pd import matplotlib.pyplot as plt from scipy.optimize import minimize data = pd.read_csv('at_5.csv', index_col='index') x_data = np.array(data['x']) y_data = np.array(data['y']) # 定义拟合函数 def log_function(x, params): k, h, xo, a, ang, gamma = params return k * np.log(((((x - xo) - a * math.cos(ang)) ** 2) + (h - a * math.sin(ang)) ** 2) / ((((x - xo) + a * math.cos(ang)) ** 2) + (h + a * math.sin(ang)) ** 2)) + gamma # 定义残差平方和作为目标函数 def objective(params): y_pred = log_function(x_data, params) return np.sum((y_pred - y_data)**2) # 存储迭代信息的列表 iter_records = [] # 回调函数:每次迭代时保存参数和残差平方和 def callback(params): res_sum = objective(params) iter_records.append({ 'params': params.copy(), 'residual_sum': res_sum }) # 初始猜测值 guess = [75, 80, 550, 25, math.radians(10), 0] # 调用minimize拟合,选择合适的优化器(比如L-BFGS-B) result = minimize(objective, guess, method='L-BFGS-B', callback=callback, options={'maxiter': 10000}) # 输出最优参数 popt = result.x print(f'''K = {popt[0]}, h = {popt[1]}, Xo = {popt[2]}, a = {popt[3]} and angle = {math.degrees(popt[4])}, gamma = {popt[5]}''') # 查看迭代记录 print("迭代次数:", len(iter_records)) print("第一次迭代的参数与残差:", iter_records[0]) print("最后一次迭代的参数与残差:", iter_records[-1]) # 绘图 plt.scatter(x_data, y_data, c='blue', label='Data') plt.plot(x_data, log_function(x_data, guess), color='green', label='Guess') plt.plot(x_data, log_function(x_data, popt), color='red', label='Matched Data') plt.xlabel('X') plt.ylabel('V(X)') plt.legend(loc='lower right') plt.show()
方法二:通过全局变量记录函数调用时的参数与残差
如果坚持使用curve_fit,可以给拟合函数添加全局变量,每次函数被调用时记录参数和残差。注意:这种方法会记录curve_fit内部所有的函数调用(包括迭代过程中的试探性调用),需要后续筛选有效迭代步骤:
示例代码片段:
# 全局列表存储调用记录 call_records = [] def log_function(x, k, h, xo, a, ang, gamma): y_pred = k * np.log(((((x - xo) - a * math.cos(ang)) ** 2) + (h - a * math.sin(ang)) ** 2) / ((((x - xo) + a * math.cos(ang)) ** 2) + (h + a * math.sin(ang)) ** 2)) + gamma # 计算当前参数对应的残差平方和 res_sum = np.sum((y_pred - y_data)**2) call_records.append({ 'params': [k, h, xo, a, ang, gamma], 'residual_sum': res_sum }) return y_pred # 后续调用curve_fit的代码不变 popt, cov = curve_fit(log_function, x_data, y_data, p0=guess, maxfev=10000) # 查看记录(需自行筛选迭代步骤) print("函数调用次数:", len(call_records))
关于scipy源码的说明
在scipy 1.11.2的_minpack_py.py中,curve_fit最终调用leastsq函数(位于同文件),而leastsq是对Fortran minpack库的封装,该库没有暴露迭代过程的回调接口,因此无法直接通过修改curve_fit的参数获取迭代信息,必须通过上述两种间接方法实现。
内容的提问来源于stack exchange,提问作者Mukilan
相关产品推荐
相关产品推荐

