Jupyter与Python脚本中optimize.minimize拟合结果差异问题求助
看起来你遇到的核心问题是Scipy版本差异导致TNC优化器的行为变化——毕竟你用的Jupyter环境是Scipy 0.19.1,而脚本环境是0.18.0,这两个版本之间scipy.optimize.minimize的TNC实现有不少细节更新,很可能是拟合结果不一致的根源。结合你的代码和现象,我整理了几个针对性的解决方案:
1. 优先升级Scipy版本到0.19.1或更高
这是最直接的解决办法。Scipy 0.18到0.19的更新中,TNC优化器修复了边界约束处理、收敛准则判断的一些bug,同时优化了数值梯度的计算精度。把脚本环境的Scipy升级到和Jupyter一致的版本后,拟合逻辑对齐,结果应该就能匹配了。
2. 提供解析梯度替代数值梯度
TNC默认会用数值梯度来计算参数的变化方向,但不同版本的数值梯度计算步长、精度控制可能有差异,这会直接影响优化路径。手动计算似然函数的解析梯度,传给minimize的jac参数,能让优化过程更稳定,避免版本间的数值计算差异。
比如针对你的ds_lnlike函数,写出对应的梯度函数:
import numpy as np def ds_jac(par, logm, d, d_err): logms, ds1, ds2, a1, a2 = par delta_logm = logm - logms log10 = np.log(10) # 计算模型的中间变量 term1 = 10 ** ((a1 + 1) * delta_logm) term2 = 10 ** ((a2 + 1) * delta_logm) exp_term = np.exp(10 ** delta_logm) model = log10 * (ds1 * term1 + ds2 * term2) / exp_term # 计算每个参数的偏导数 d_logms = log10 * ( (ds1*(a1+1)*term1*log10 + ds2*(a2+1)*term2*log10)/exp_term + (ds1*term1 + ds2*term2)/exp_term * 10**delta_logm * log10 ) d_ds1 = log10 * term1 / exp_term d_ds2 = log10 * term2 / exp_term d_a1 = log10 * ds1 * term1 * delta_logm * log10 / exp_term d_a2 = log10 * ds2 * term2 * delta_logm * log10 / exp_term # 转换为负对数似然的梯度(因为nll是-lnlike) residual = (d - model) / d_err**2 grad = -0.5 * np.sum(2 * residual * (-np.array([d_logms, d_ds1, d_ds2, d_a1, d_a2])), axis=1) return grad
然后修改minimize的调用,指定jac参数:
nll = lambda *args: -model.ds_lnlike(*args) # 注意jac要对应负对数似然的梯度,所以取负的ds_jac result = op.minimize(nll, [logms[j], ds1[j], ds2[j], a1[j], a2[j]], args=(x, y, dy), method='TNC', bounds=((7,13),(0, 0.1),(0, 0.1), (-2, 4), (-2, -1)), tol=1.0e-4, jac=lambda par, *args: -model.ds_jac(par, *args))
3. 验证初始值的绝对一致性
虽然你说初始值是固定的,但还是建议在脚本和Jupyter中分别打印初始参数数组,确认[logms[j], ds1[j], ds2[j], a1[j], a2[j]]的数值完全一致——比如脚本中是否因为索引j的取值、变量提前赋值等问题,导致初始值和Jupyter有细微差别(比如浮点数精度的微小差异),这也可能让优化器走向不同的极值点。
4. 调整TNC的优化参数
如果暂时无法升级Scipy,可以尝试调整TNC的额外选项,强制让优化器更彻底地搜索最优解:
result = op.minimize(nll, [logms[j], ds1[j], ds2[j], a1[j], a2[j]], args=(x, y, dy), method='TNC', bounds=((7,13),(0, 0.1),(0, 0.1), (-2, 4), (-2, -1)), tol=1.0e-4, options={'maxiter': 1000, # 增加最大迭代次数 'epsilon': 1e-8}) # 缩小数值梯度的步长,提升精度
5. 尝试替代优化方法
可以试试用L-BFGS-B方法替代TNC,看看在两个版本中结果是否一致。如果L-BFGS-B在两个环境中拟合结果相同,那基本可以确定是TNC的版本差异问题,升级Scipy就可以解决。
内容的提问来源于stack exchange,提问作者Vladan Markov

