对于返回小值的目标函数,以及我想在基于梯度的算法中使用数值导数的问题,我在使用 SciPy 进行最小化时遇到了问题。 它针对 Cochran 1977 年《抽样技术》教科书中与最佳抽样大小相关的问题。 我的最小工作示例如下:
import numpy as np
import math
from scipy.optimize import minimize
#parameters
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
#parameter to control the scale of the objective function
scale_value = 1.0
#objective function that the variance of a stratified estimator
def objective_function(n_vec_in_short):
#convert from numpy to a list
if isinstance(n_vec_in_short,np.ndarray):
n_vec_in_short_list= n_vec_in_short.tolist()
else:
n_vec_in_short_list = n_vec_in_short
#get the sample size of the last stratum
n_last_stratum = total_sample - sum( n_vec_in_short_list)
#now create the vector that is of lenght n_strata
n_vec = n_vec_in_short_list
n_vec.append(n_last_stratum)
variance_total=0.0
#get the sum of variance across strata
for h in range(0,n_strata):
variance_total = variance_total + (strata_size[h]**2)*strata_variance[h]/n_vec[h]
return variance_total*scale_value
solution = minimize(objective_function, [2500,2500,2500] , method='BFGS' )
print(solution)
true_optimum = objective_function([2000,2000,2000])
print(true_optimum)
我的问题是,如果我将目标函数的比例值设置为 1.0,它仅返回初始猜测 [2500,2500,2500]。 但是,如果我将比例值更改为 100000.0,我确实会得到最佳解决方案。
我知道如何针对最小工作示例修复此问题。 但对于我正在解决的实际问题,我希望能帮助您更好地理解 SciPy,以了解如何最好地为使用数值导数的算法设置参数,从而更好地避免此类收敛问题。
尝试过有效的临时解决方案,但希望社区帮助更好地理解最小化,以获得更少的临时解决方案
在使用
scipy.optimize.minimize
时遇到了一个常见问题,该问题源于数值导数和基于梯度的优化算法的敏感性。让我们分解一下发生的事情,并找出一些稳健的解决方案。
问题
的目标函数在绝对值上产生非常小的值(当
scale_value = 1.0
时)。当使用数值导数时,尤其是涉及有限差分时,这就会成为一个问题。这是因为:
-
有限精度: 计算机以有限精度存储数字。当计算非常小数字的差值时,就会出现舍入误差,从而导致导数不准确。
-
步长: 有限差分法涉及使用一个“步长”来逼近导数。小目标函数值与(可能)非最佳步长相结合,会导致梯度计算不佳。优化算法“认为”它已经在一个平坦的区域中,并且停止迭代,即使它没有在真正的最优点。
解决方案
-
缩放目标函数: 如所注意到的,缩放目标函数可以解决问题。这本质上是通过放大数值导数中的差异来提高梯度的准确性。但是,合适的缩放因子可能需要一些试验和错误。
-
提供解析梯度: 如果能计算出目标函数的解析梯度,则将其提供给
scipy.optimize.minimize
。解析梯度比数值梯度更准确、更高效。这消除了有限差分和相关问题。
```python def gradient(n_vec_in_short): # ...计算 n_vec_in_short 中每个变量的解析偏导数... # 返回一个包含偏导数的列表或数组
solution = minimize(objective_function, [2500, 2500, 2500], method='BFGS', jac=gradient) ```
-
调整优化器容差:
可以调整
scipy.optimize.minimize
中的gtol
(梯度容差)和xtol
(变量容差)参数。这些参数控制优化器在声明收敛之前梯度和变量变化的允许大小。增加这些容差可以帮助算法在目标函数平坦时找到更好的解决方案。
python
solution = minimize(objective_function, [2500, 2500, 2500], method='BFGS', options={'gtol': 1e-8, 'xtol': 1e-8})
- 使用不同的优化算法: 虽然 BFGS 是一个很好的通用优化器,但还有其他算法,如 Nelder-Mead 单纯形法,它们不依赖于梯度信息,并且在处理噪声函数或具有平坦区域的函数时可能更稳健。
python
solution = minimize(objective_function, [2500, 2500, 2500], method='Nelder-Mead')
最佳实践
- 只要可行,就提供解析梯度。这通常是解决此类问题的最有效和最准确的方法。
- 如果无法提供解析梯度,请尝试缩放目标函数。
- 实验不同的优化算法和容差设置,以找到适合的特定问题的最佳组合。
请记住,优化是一个复杂的任务,没有一个适用于所有情况的解决方案。通过了解问题和可用工具,可以做出明智的决定,并提高获得可靠和准确结果的可能性。
标签:python,scipy,minimize From: 78813806