You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SciPy极小化收敛问题:小值目标函数与数值导数场景

SciPy BFGS极小化收敛问题:目标函数值过小时的解决思路

问题背景

在计算Cochran 1977年《Sampling Techniques》中的最优分层抽样样本量时,使用SciPy的minimize函数(BFGS方法)遇到收敛异常:当目标函数(分层估计量方差)的尺度为1.0时,算法直接返回初始猜测值,无法找到最优解;只有将目标函数放大100000倍后,才能收敛到正确的最优解。需要通过调整minimize的参数设置,替代临时缩放目标函数的方案。

复现代码

import numpy as np
from scipy.optimize import minimize

# 参数设置
n_strata = 4
strata_size = [0.25, 0.25, 0.25, 0.25]
strata_variance = [0.25, 0.25, 0.25, 0.25]
total_sample = 8000
scale_value = 1.0  # 目标函数尺度参数


# 目标函数:分层估计量的方差
def objective_function(n_vec_in_short):
    n_vec_in_short_list = n_vec_in_short.tolist() if isinstance(n_vec_in_short, np.ndarray) else n_vec_in_short
    n_last_stratum = total_sample - sum(n_vec_in_short_list)
    n_vec = n_vec_in_short_list + [n_last_stratum]
    
    variance_total = 0.0
    for h in range(n_strata):
        variance_total += (strata_size[h] ** 2) * strata_variance[h] / n_vec[h]
    
    return variance_total * scale_value


# BFGS求解
solution = minimize(objective_function, [2500, 2500, 2500], method='BFGS')
print("BFGS求解结果:")
print(solution)

# 真实最优值
true_optimum = objective_function([2000, 2000, 2000])
print(f"\n真实最优目标值:{true_optimum}")

问题根源

BFGS这类基于梯度的算法依赖数值梯度的精度判断收敛状态:

  • 当目标函数值极小时,数值梯度的计算误差(浮点精度限制导致)相对值会大幅升高,算法无法识别出有效的梯度方向变化,误以为当前点已经是极值点。
  • 默认的收敛阈值(如gtol=1e-5)对小尺度的梯度变化不敏感,导致算法提前终止迭代。

可行解决方案

1. 传入解析梯度替代数值梯度

数值梯度的误差是核心问题,手动推导目标函数的解析梯度并传入jac参数,能彻底消除数值误差,提升算法的收敛可靠性。

推导后的梯度函数:

def gradient_function(n_vec_in_short):
    n_arr = np.asarray(n_vec_in_short)
    n_last = total_sample - n_arr.sum()
    grad = []
    for i in range(3):
        # 对第i个变量的偏导数:d/dn_i = -W_i²σ_i²/n_i² + W_4²σ_4²/n_last²
        term1 = - (strata_size[i] ** 2 * strata_variance[i]) / (n_arr[i] ** 2)
        term2 = (strata_size[3] ** 2 * strata_variance[3]) / (n_last ** 2)
        grad.append(term1 + term2)
    return np.array(grad) * scale_value

调用时传入梯度函数:

solution = minimize(objective_function, [2500,2500,2500], method='BFGS', jac=gradient_function)

2. 调整梯度收敛阈值gtol

默认gtol=1e-5,当目标函数尺度小时,梯度绝对值也会很小,将gtol调至更小的值(如1e-10),让算法对更小的梯度变化敏感:

solution = minimize(objective_function, [2500,2500,2500], method='BFGS', options={'gtol': 1e-10})

3. 调整总收敛容差tol

tol是算法的全局收敛容差,调小该值可以让算法迭代到满足更严格的收敛条件:

solution = minimize(objective_function, [2500,2500,2500], method='BFGS', tol=1e-12)

方案对比

  • 解析梯度:最可靠的方案,完全避免数值梯度的误差,适合需要高精度的场景,但需要手动推导梯度公式。
  • 调整阈值:快速便捷,无需修改目标函数或推导梯度,但需根据目标函数尺度合理设置阈值,通用性稍弱。

内容的提问来源于stack exchange,提问作者J Egg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 08:40:55