使用scipy.minimize的BFGS算法参数接近最优但未收敛问题
我正在估计区间回归模型,参照STATA的intreg模块编写了对数似然函数,所得参数与STATA结果误差在0.001以内,但使用scipy.minimize的BFGS算法时始终未收敛,收到如下提示信息:
message: Desired error not necessarily achieved due to precision loss. success: False status: 2 fun: 26435.40918822449 x: [ 7.379e+00 -6.967e-02 ... 2.149e-01 4.295e-01] nit: 94 jac: [-4.883e-04 0.000e+00 ... -2.441e-04 4.883e-04] hess_inv: [[ 3.566e-04 -7.314e-05 ... -1.261e-04 -1.775e-06] [-7.314e-05 5.599e-05 ... 3.216e-05 5.660e-07] ... [-1.261e-04 3.216e-05 ... 4.981e-04 1.297e-06] [-1.775e-06 5.660e-07 ... 1.297e-06 8.380e-06]] nfev: 4235 njev: 121
对数似然函数代码如下:
def log_likelihood(params, x, y_lower, y_upper): beta = params[:-1] sigma = np.abs(params[-1]) z_upper = (y_upper - np.dot(x, beta)) / sigma z_lower = (y_lower - np.dot(x, beta)) / sigma epsilon = 1e-10 ll_interval = np.sum(np.log(np.clip(norm.cdf(z_upper) - norm.cdf(z_lower), epsilon, None))) log_likelihood = ll_interval return -log_likelihood
调用scipy.minimize的代码如下:
initial_params = np.concatenate([np.zeros(X.shape[1]), [1.0]]) result = minimize(log_likelihood, initial_params, args=(X, y_lower, y_upper), method='BFGS', options={'disp':True})
为何参数已接近最优,但算法仍提示未收敛?
数值精度限制导致收敛判定不通过
BFGS默认通过梯度(jac)绝对值是否小于gtol=1e-5来判定收敛。你给出的jac分量有4.88e-04,远高于阈值,但参数已接近STATA结果,说明此时似然函数地形极平缓,梯度的微小波动是数值计算精度上限导致的,并非真的未达最优。sigma的绝对值处理引入不可导点
用np.abs(params[-1])约束sigma为正,会在params[-1]=0处产生不可导点。迭代中若参数接近0,会引发梯度计算的数值不稳定,干扰收敛判定。建议改用指数化参数化:sigma = np.exp(params[-1]),确保sigma始终为正且全参数空间可导,提升稳定性。似然函数的数值计算误差
用np.clip避免log(0)时,epsilon=1e-10过小,可能引发log计算的数值误差。可尝试调大epsilon到1e-8,或用更稳定的对数概率计算方式:ll_interval = np.sum(norm.logcdf(z_upper) + np.log1p(-norm.cdf(z_lower)/(norm.cdf(z_upper)+1e-12)))这种方式避免直接计算概率差值,降低数值噪声。
调整BFGS收敛阈值
确认参数足够接近最优后,可手动放宽收敛阈值,让算法认可当前状态:result = minimize(log_likelihood, initial_params, args=(X, y_lower, y_upper), method='BFGS', options={'disp':True, 'gtol':1e-3, 'ftol':1e-6})换用更鲁棒的优化算法
BFGS对数值噪声敏感,可尝试L-BFGS-B(带边界约束)给sigma设下界,避免接近0:bounds = [(None, None)]*X.shape[1] + [(1e-3, None)] result = minimize(log_likelihood, initial_params, args=(X, y_lower, y_upper), method='L-BFGS-B', bounds=bounds, options={'disp':True})
内容的提问来源于stack exchange,提问作者Youssef

