scipy.optimize.least_squares迭代5次仍返回初始值问题求助
问题描述
使用scipy.optimize.least_squares拟合数据时,无论设置何种初始猜测值,优化结果始终与初始值完全一致,无任何更新。已确认代价函数和参数配置看似正常,但问题持续存在。
相关代码
def LSMM(initial_guess, ocp_cathode, ocp_anode,soc_values, soc, ocv_entries): def ocp_pe(alpha_p, beta_p, ocp_cathode, soc_values): ocp_p = {k_soc:v for k_soc, v in zip(np.around(alpha_p*soc_values+beta_p,3), ocp_cathode)} return ocp_p def ocp_ne(alpha_n, beta_n, ocp_anode, soc_values): ocp_n = {k_soc:v for k_soc, v in zip(np.round(alpha_n*soc_values+beta_n,3), ocp_anode)} return ocp_n def ocv_cell(soc, ocv_datapoints): ocv = {k_soc:v for k_soc, v in zip(np.round(soc,3), ocv_datapoints)} return ocv def cost_function(params, ocp_cathode, ocp_anode, soc_values, soc, ocv_datapoints): print("here") alpha_p, beta_p, alpha_n, beta_n = params print(params) ocp_p = ocp_pe(alpha_p, beta_p, ocp_cathode, soc_values) ocp_n = ocp_ne(alpha_n, beta_n, ocp_anode, soc_values) ocv = ocv_cell(soc, ocv_datapoints) ocv_cat = [] ocv_an = [] for i in ocv.keys(): ocv_cat.append(ocp_p[i]) ocv_an.append(ocp_n[i]) ocv_pred = np.array(ocv_cat) - np.array(ocv_an) diff = np.sqrt(np.sum((ocv_pred - np.array(list(ocv.values()))) ** 2)) #return np.sqrt(np.mean((ocv_pred - np.array(list(ocv.values()))) ** 2)) return diff opti = least_squares(cost_function, initial_guess, args = (ocp_cathode, ocp_anode, soc_values, soc, ocv_entries),method='trf') alpha_p, beta_p, alpha_n, beta_n = opti.x jac = opti.grad #stat = opti.status print("alpha p: ", alpha_p) print("beta p: ", beta_p) print("alpha n: ", alpha_n) print("beta n: ", beta_n) print(jac) o_p = ocp_pe(alpha_p, beta_p, ocp_cathode, soc_values) o_n = ocp_ne(alpha_n, beta_n, ocp_anode, soc_values) o = ocv_cell(soc, ocv_entries) ocv_cat = [] ocv_an = [] for j in o.keys(): ocv_cat.append(o_p[j]) ocv_an.append(o_n[j]) ocv_pred = np.array(ocv_cat) - np.array(ocv_an) plt.figure(figsize=(10, 6)) plt.plot(list(o_p.keys()), list(o_p.values()), label="Cathode") plt.plot(list(o_n.keys()), list(o_n.values()), label="Anode") plt.plot(list(o.keys()), ocv_pred, label="OCV Predicted") plt.plot(list(o.keys()), list(o.values()), label ="OCV Measured") plt.xlabel("State of Charge (SoC)") plt.ylabel("Open Circuit Potential (OCP) [V]") plt.title("Open Circuit Potential") plt.grid(True) plt.legend() plt.show() initial_guess = np.array([1.2,-0.1,1.2,-0.1]) LSMM(initial_guess, ocp_cathode, ocp_anode, soc_values, soc, ocv_entries)
问题根源与修复方案
1. 代价函数输出不符合要求
scipy.optimize.least_squares要求代价函数返回一维残差数组(每个数据点的预测值与真实值的差值),而非单个标量的误差平方和或均方根。你当前返回的是汇总后的标量,优化器无法通过该标量计算有效梯度,导致参数无法更新,直接返回初始值。
2. 离散字典匹配导致梯度为0
在ocp_pe、ocp_ne中,通过np.around将变换后的SoC离散为三位小数作为字典键,再匹配OCP值。这种离散化会让参数的微小变化无法改变键值,进而无法改变预测的OCP值,残差对参数的梯度为0,优化器无法感知参数变化的影响,停止更新。
修复步骤
步骤1:修改代价函数返回残差数组
将代价函数改为返回每个数据点的残差,替换原有汇总逻辑:
def cost_function(params, ocp_cathode, ocp_anode, soc_values, soc, ocv_datapoints): alpha_p, beta_p, alpha_n, beta_n = params # 用插值替代字典匹配,实现连续OCP计算 from scipy.interpolate import interp1d # 假设soc_values是升序排列的SoC数组,对应ocp_cathode的OCP值 ocp_p_interp = interp1d(soc_values, ocp_cathode, kind='linear', fill_value="extrapolate") ocp_n_interp = interp1d(soc_values, ocp_anode, kind='linear', fill_value="extrapolate") # 计算每个测量SoC变换后的正极/负极SoC transformed_soc_p = alpha_p * soc + beta_p transformed_soc_n = alpha_n * soc + beta_n # 得到预测的电池OCV ocv_pred = ocp_p_interp(transformed_soc_p) - ocp_n_interp(transformed_soc_n) # 返回每个数据点的残差数组 return ocv_pred - np.array(ocv_datapoints)
步骤2:替换离散匹配为连续插值
使用scipy.interpolate.interp1d将正极/负极的SoC-OCP曲线转换为连续插值函数,参数的微小变化会直接反映在预测OCP上,确保优化器能计算出有效梯度。
步骤3:添加优化日志验证
调用least_squares时添加verbose=2参数,查看优化过程的日志,确认参数是否开始更新:
opti = least_squares(cost_function, initial_guess, args=(ocp_cathode, ocp_anode, soc_values, soc, ocv_entries), method='trf', verbose=2)
额外建议
- 确保
soc_values是升序排列的数组,插值函数在无序输入时可能出现异常; - 可添加参数边界约束,比如
bounds=([0.8, -0.2, 0.8, -0.2], [1.5, 0.2, 1.5, 0.2]),避免参数超出物理合理范围。
内容的提问来源于stack exchange,提问作者Mukundh Balabhadra
相关产品推荐
相关产品推荐

