如何在不增加迭代次数的同时通过联合最小化两个函数提升概率根求解精度
看起来你现在的核心需求是找到满足 ( p_1=p_1^t )、( p_2=p_2^t )、( p_3=p_3^t ) 的概率分布,本质上是求解一个不动点问题。你当前用平方和最小化的思路比root/fixed_point表现更好,但有时得到的目标函数值偏大,又不想通过增加迭代次数来耗费时间。下面我给你几个不需要提升niter就能优化精度的方案,其中就包含你提到的“联合最小化”相关的思路:
1. 重构目标函数:用加权联合项替代单一平方和
因为 ( p_3=1-p_1-p_2 )、( p_3t=1-p_1t-p_2^t ),所以 ( (p_3t-p_3)2 ) 其实等于 ( (p_1^t-p_1 + p_2t-p_2)2 )——这意味着原目标函数里的三个平方项并非完全独立。你可以把目标拆成两个逻辑独立的部分,通过加权来“联合最小化”它们,引导优化器更均衡地满足三个概率的不动点条件:
def get_prob(x, s1, s2, s3, a, lamda, t1, t2, t3): p1, p2 = x[0], x[1] p3 = 1 - p1 - p2 # 原有计算p1_t, p2_t, p3_t的代码保持不变 b1 = s1 * (1-a) + p1 * a b2 = s2 * (1-a) + p2 * a b3 = s3 * (1-a) + p3 * a c1,c2,c3=get_attraction(b1,b2,b3) a1 = c1+t1 a2 = c2+t2 a3 = c3+t3 nom1 = np.exp(lamda*a1) nom2 = np.exp(lamda*a2) nom3 = np.exp(lamda*a3) dem = nom1 + nom2 + nom3 p1_t = nom1 / dem p2_t = nom2 / dem p3_t = nom3 / dem # 拆分为两个独立项并加权 term_independent = (p1_t - p1)**2 + (p2_t - p2)**2 # 前两个概率的独立误差 term_dependent = (p3_t - p3)**2 # 第三个概率的依赖误差 return term_independent + 2 * term_dependent # 权重可根据实际效果调整
这种方式没有增加迭代次数,只是调整了优化的目标导向,避免前两个变量的小误差累积成第三个变量的大误差,从而提升最终的精度。
2. 替换目标函数为KL散度,贴合概率问题特性
既然你处理的是概率分布,用**Kullback-Leibler散度(KL散度)**作为目标函数比平方和更贴合问题本质——它会更敏感地惩罚概率分布的偏差,尤其是小概率部分,往往能在相同迭代次数下找到更接近真实不动点的解:
def get_prob(x, s1, s2, s3, a, lamda, t1, t2, t3): p1, p2 = x[0], x[1] p3 = 1 - p1 - p2 # 原有计算p1_t, p2_t, p3_t的代码保持不变 b1 = s1 * (1-a) + p1 * a b2 = s2 * (1-a) + p2 * a b3 = s3 * (1-a) + p3 * a c1,c2,c3=get_attraction(b1,b2,b3) a1 = c1+t1 a2 = c2+t2 a3 = c3+t3 nom1 = np.exp(lamda*a1) nom2 = np.exp(lamda*a2) nom3 = np.exp(lamda*a3) dem = nom1 + nom2 + nom3 p1_t = nom1 / dem p2_t = nom2 / dem p3_t = nom3 / dem # 加入极小值避免log(0)的情况 eps = 1e-10 kl_divergence = (p1 * np.log((p1 + eps)/(p1_t + eps)) + p2 * np.log((p2 + eps)/(p2_t + eps)) + p3 * np.log((p3 + eps)/(p3_t + eps))) return kl_divergence
KL散度的梯度信息更能引导优化器向真实不动点靠近,不需要增加迭代次数就能获得更好的精度。
3. 优化局部最小化器参数,提升单次局部搜索效率
你当前用SLSQP作为局部优化器,可以调整minimizer_kwargs里的参数,让每次局部搜索更高效,而不需要增加全局的niter:
- 适当调小
tol(比如从1e-5改为1e-8),让局部优化的终止条件更严格; - 给
SLSQP增加options={'maxiter': 50}(或其他合适数值),让局部优化每次能多跑几步,提升局部搜索深度; - 确认约束的雅可比矩阵正确(你当前的
jac是对的,因为约束1-x[0]-x[1]>=0的梯度确实是[-1,-1])。
调整后的minimizer_kwargs示例:
minimizer_kwargs={ 'args': (s1,s2,s3,a,lamda,t1,t2,t3), 'method': "SLSQP", 'tol': 1.0e-8, 'bounds': [(0.0, 1.0), (0.0, 1.0)], 'constraints': {'type': 'ineq', 'fun': lambda x: 1 - x[0] - x[1], 'jac': lambda x: np.full_like(x, -1)}, 'options': {'maxiter': 50} }
4. 优化初始点,让全局搜索从更优位置起步
你当前的初始点是[0.0, 0.25],可以先跑一次局部优化得到更接近最优解的初始点,再作为basinhopping的起点,这样在相同niter下能更快找到更好的解:
from scipy.optimize import minimize # 先执行一次局部优化获取更优初始点 init_result = minimize( get_prob, x0=[0.0, 0.25], args=(s1,s2,s3,a,lamda,t1,t2,t3), method="SLSQP", tol=1.0e-5, bounds=[(0.0, 1.0), (0.0, 1.0)], constraints={'type': 'ineq', 'fun': lambda x: 1 - x[0] - x[1], 'jac': lambda x: np.full_like(x, -1)} ) # 用局部优化结果作为basinhopping的初始点 result = basinhopping( get_prob, x0=init_result.x, niter=50, seed=0, interval=10, minimizer_kwargs=minimizer_kwargs )
这些方案都不需要增加basinhopping的全局迭代次数,而是通过调整目标函数、优化局部搜索效率或初始点,来提升不动点求解的精度。其中重构目标函数的思路最贴合你“联合最小化两个函数”的需求,本质上是把单一目标拆分为两个相关项的加权组合,引导优化器更均衡地满足所有概率的不动点条件。
备注:内容来源于stack exchange,提问作者jasmine

