为何scipy minimize得到的最优系数始终与初始猜测值相同?
问题排查:scipy.minimize优化结果始终等于初始猜测值
我有一个输出为0或1的分类模型,目标是最小化预测值与观测值的绝对误差总和。mod_7是未经过特定列过滤的初始数据集,但发现无论修改first_guess的值,best_fit的结果始终和初始猜测一致,求排查原因。
from scipy.optimize import minimize import pandas as pd import numpy as np np.random.seed(0) mod_7 = pd.DataFrame(np.random.randint(2,size=(100, 5)), columns=['IsCar', 'Is10places', 'observed_value','hybrid_score', 'col_score']) def fuel_efficiency_grade_calculator(df): df['total_score_adj'] = np.where(df['hybrid_score'] == 1, df['total_score'] + 1 , df['total_score'] - 1) return df def func(params): app_coef, ext_coef = params df_temp = mod_7[(mod_7['IsCar'] != 1) & (mod_7['Is10places'] == 1)] df_temp['total_score'] = 0.55 + app_coef * df_temp["hybrid_score"] + ext_coef * df_temp["col_score"] # compute grades df_temp = fuel_efficiency_grade_calculator(df_temp) # this function does some data transfromation, but the final goal is compute column df_temp['fuel_efficient'] df_temp['predicted_value'] = np.where(df_temp['total_score_adj'] >= 7, 1, 0) return abs(sum(df_temp['predicted_value'] - df_temp['observed_value'])) # choose reasonable values for your 7 parameters here, # i.e. close to the "right" answer, this may take a few tries first_guess = [3, 5] # here we run the minimisation res = minimize(func, first_guess) # this is an array of your best fit values for a1-a7 best_fit = res.x
核心问题分析
你的目标函数func是非连续、非光滑的,这直接导致scipy.minimize默认的梯度类优化算法完全失效:
- 预测值
predicted_value是阶跃函数,只有当total_score_adj跨越7这个阈值时,调整系数才会改变预测结果;如果初始猜测对应的total_score_adj大部分都离7很远,调整系数无法让更多样本跨越阈值,误差总和就不会变化,算法会误以为当前点已经是最优解。 - 梯度类算法依赖目标函数的导数来寻找下降方向,但阶跃函数的导数在大部分点都是0,算法找不到优化方向,只能停在初始点。
另外还有两个细节问题:
- 直接在切片后的
df_temp上新增列会触发pandas的SettingWithCopyWarning,建议用.copy()创建独立副本:df_temp = mod_7[(mod_7['IsCar'] !=1) & (mod_7['Is10places'] ==1)].copy() - 注释提到要优化7个参数,但代码里只处理了2个,不过这不是当前问题的核心。
解决建议
方案1:替换为光滑损失函数
把绝对误差总和换成可导的近似损失,比如逻辑回归的交叉熵损失,让算法能通过梯度找到优化方向:
def func(params): app_coef, ext_coef = params df_temp = mod_7[(mod_7['IsCar'] !=1) & (mod_7['Is10places'] ==1)].copy() df_temp['total_score'] = 0.55 + app_coef * df_temp["hybrid_score"] + ext_coef * df_temp["col_score"] df_temp['total_score_adj'] = np.where(df_temp['hybrid_score'] ==1, df_temp['total_score']+1, df_temp['total_score']-1) # 用sigmoid将得分映射为0-1概率,计算交叉熵损失(加1e-8避免log(0)报错) prob = 1/(1+np.exp(-(df_temp['total_score_adj'] -7))) loss = -np.sum(df_temp['observed_value']*np.log(prob+1e-8) + (1-df_temp['observed_value'])*np.log(1-prob+1e-8)) return loss
方案2:使用非梯度优化算法
给minimize指定支持非光滑函数的算法,比如Nelder-Mead单纯形法,它不需要计算梯度:
res = minimize(func, first_guess, method='Nelder-Mead', options={'maxiter': 10000, 'fatol': 1e-4})
注:该算法收敛速度较慢,可能需要调整options里的迭代次数、精度参数来优化效果。
方案3:调整决策边界或特征范围
检查当前total_score_adj的分布,如果所有样本的得分都远低于或远高于7,说明这个阈值设置不合理。可以降低阈值,或者对特征进行缩放,让系数调整能影响更多样本的预测结果。
内容的提问来源于stack exchange,提问作者Lumi Nor
相关产品推荐
相关产品推荐

