使用scipy.optimize最小化二元函数时遇标量返回值错误求助
scipy.optimize.minimize报错:目标函数必须返回标量值
问题重现
你编写了用于最小化卡方值的Python代码,但运行时抛出错误:
ValueError: The user-provided objective function must return a scalar value.
完整代码及依赖
import numpy as np import matplotlib.pyplot as plt import math from scipy.integrate import quad import scipy.optimize as optimize # 已定义变量 f_0 = 0.261 G_F = 1.166 * 10**(-5) m_e = 0.511 * 10**(-3) dB_dqsq_arr = np.array([7.2,7.14,6.7,7.56,6.44,7.17,6.67,6.33,6.2,4.32,4.25,3.4,1.17]) dBdqsq_err_arr = np.array([0.70,0.45,0.39,0.43,0.43,0.45,0.47,0.48,0.44,0.43,0.41,0.42,0.26]) q_sq_arr = np.array([1,3,5,7,9,11,13,15,17,19,21,23,25]) def f_multi_para(q_sq, alpha_par): return f_0 / (q_sq * alpha_par) def dB_dqsq_model2_para(q_sq, V_ub_par, alpha_par): sec1 = G_F**2 * V_ub_par sec2 = (1 - m_e**2 / q_sq)**2 sec3 = (q_sq * (1 + m_e**2 / q_sq) * f_multi_para(q_sq, alpha_par)**2) return sec1 * sec2 * sec3 def chi_sq(params): V_ub_par, alpha_par = params return np.sum(((dB_dqsq_arr - np.array([dB_dqsq_model2_para(v, V_ub_par, alpha_par) for v in q_sq_arr])) / dBdqsq_err_arr)**2) initial_guess = [0.0037, 0.54] result = optimize.minimize(chi_sq, initial_guess) if result.success: fitted_params = result.x print(fitted_params) else: raise ValueError(result.message)
错误原因
这个错误的核心是scipy.optimize.minimize要求目标函数(即chi_sq)必须返回单个数值(标量),你的代码出现问题的可能原因:
- 循环遍历计算模型值时,隐性引入了数组维度不匹配问题(虽然代码中长度一致,但循环写法容易出错);
- 优化过程中
alpha_par可能趋近于0,导致f_multi_para计算出现无穷大,部分旧版本scipy会将其判定为无效返回值; - 循环计算效率低,容易在数组转换时产生隐性错误。
修复方法
方法1:改用数组广播计算(推荐)
将模型函数改为直接支持数组输入,避免循环,既高效又能彻底消除维度问题:
def f_multi_para(q_sq, alpha_par): return f_0 / (q_sq * alpha_par) # 直接处理q_sq数组 def dB_dqsq_model2_para(q_sq, V_ub_par, alpha_par): sec1 = G_F**2 * V_ub_par sec2 = (1 - m_e**2 / q_sq)**2 sec3 = (q_sq * (1 + m_e**2 / q_sq) * f_multi_para(q_sq, alpha_par)**2) return sec1 * sec2 * sec3 def chi_sq(params): V_ub_par, alpha_par = params # 直接传入整个q_sq_arr数组计算模型值 model_vals = dB_dqsq_model2_para(q_sq_arr, V_ub_par, alpha_par) # 数组自动广播运算,无需循环 return np.sum(((dB_dqsq_arr - model_vals) / dBdqsq_err_arr)**2)
方法2:添加参数边界约束
为避免alpha_par趋近于0导致的除以0问题,给优化参数设置合理范围:
initial_guess = [0.0037, 0.54] # 根据实际物理意义调整参数范围 bounds = [(0.001, 0.01), (0.01, 1.0)] result = optimize.minimize(chi_sq, initial_guess, bounds=bounds)
方法3:验证目标函数返回值
在chi_sq函数中添加打印语句,确认返回值类型和数值:
def chi_sq(params): V_ub_par, alpha_par = params model_vals = np.array([dB_dqsq_model2_para(v, V_ub_par, alpha_par) for v in q_sq_arr]) chi_sq_val = np.sum(((dB_dqsq_arr - model_vals) / dBdqsq_err_arr)**2) print(f"返回值类型:{type(chi_sq_val)},值:{chi_sq_val}") return chi_sq_val
如果返回值是标量但仍报错,建议更新scipy到最新版本,旧版本可能存在兼容性问题。
修复后完整代码
import numpy as np import scipy.optimize as optimize # 已定义变量 f_0 = 0.261 G_F = 1.166 * 10**(-5) m_e = 0.511 * 10**(-3) dB_dqsq_arr = np.array([7.2,7.14,6.7,7.56,6.44,7.17,6.67,6.33,6.2,4.32,4.25,3.4,1.17]) dBdqsq_err_arr = np.array([0.70,0.45,0.39,0.43,0.43,0.45,0.47,0.48,0.44,0.43,0.41,0.42,0.26]) q_sq_arr = np.array([1,3,5,7,9,11,13,15,17,19,21,23,25]) def f_multi_para(q_sq, alpha_par): return f_0 / (q_sq * alpha_par) def dB_dqsq_model2_para(q_sq, V_ub_par, alpha_par): sec1 = G_F**2 * V_ub_par sec2 = (1 - m_e**2 / q_sq)**2 sec3 = (q_sq * (1 + m_e**2 / q_sq) * f_multi_para(q_sq, alpha_par)**2) return sec1 * sec2 * sec3 def chi_sq(params): V_ub_par, alpha_par = params model_vals = dB_dqsq_model2_para(q_sq_arr, V_ub_par, alpha_par) return np.sum(((dB_dqsq_arr - model_vals) / dBdqsq_err_arr)**2) initial_guess = [0.0037, 0.54] bounds = [(0.001, 0.01), (0.01, 1.0)] result = optimize.minimize(chi_sq, initial_guess, bounds=bounds) if result.success: fitted_params = result.x print(f"拟合参数:V_ub_par={fitted_params[0]:.6f}, alpha_par={fitted_params[1]:.6f}") else: raise ValueError(result.message)
内容的提问来源于stack exchange,提问作者noob anomaly
相关产品推荐
相关产品推荐

