scipy的直接失败(几乎)立即在这个玩具优化问题

c3frrgcw  于 8个月前  发布在  其他
关注(0)|答案(1)|浏览(83)

考虑以下简单的MWE:

import numpy as np
from scipy.optimize import  direct
def score(x):
    parity_in_range = len([v for v in x if 4 <= v <= 6])%3
    main_score = np.max(np.abs(np.diff(x)))
    return main_score + parity_in_range
length = 20
bounds = [(0,10)] * length
result = direct(score, locally_biased=False, bounds=bounds, maxiter=10000, maxfun=10000)
print(result)

最佳解决方案是使所有参数相等,而不是在4和6之间。例如,所有3。这给出了函数值0。使用scipy的不同优化器,优化的成功程度不同,但使用direct时几乎立即失败。它给出:

message: The volume of the hyperrectangle containing the lowest function value found is below vol_tol=1e-16
 success: True
  status: 4
     fun: 2.0
       x: [ 5.000e+00  5.000e+00 ...  5.000e+00  5.000e+00]
     nit: 2
    nfev: 157

我不确定它是否应该报告成功,但真实的问题是,它在157次功能评估后放弃了那个警告。
有没有办法直接优化这个函数?

5uzkadbs

5uzkadbs1#

终止参数vol_tol隐式地取决于问题的维数。搜索空间被划分成一系列超矩形,最小的中间超矩形的大小为(1/3)^n,其中n是维数。
当n=20时,这意味着最里面的立方体的体积为2.8e-10。如果最里面的立方体的中点恰好是最低点,那么该立方体将再次细分。由于vol_tol默认为1 e-16,这意味着算法将在两次迭代后退出。
如果你不想让vol_tol导致DIRECT提前退出,你可以将vol_tol设置为零:

result = direct(score, locally_biased=False, bounds=bounds, maxiter=10000, maxfun=10000, vol_tol=0)

运行这个程序,它找到了一个更好的解决方案,尽管仍然不是最优的:

message: Number of function evaluations done is larger than maxfun=10000
 success: False
  status: 1
     fun: 1.1111111111111112
       x: [ 3.889e+00  3.889e+00 ...  5.000e+00  5.000e+00]
     nit: 12
    nfev: 12021

当然,你也可以通过使函数更简单来解决这个问题,例如。使得parity_in_range物镜泄漏。

改变目标函数为连续

如果一个目标函数是连续的,那么优化这个函数通常会更容易。
在下图中,蓝线表示每个值的现有parity_in_range函数,忽略mod 3。
橙子线表示一个新函数,它向边缘向下倾斜,提示优化器在该方向有一个较低值。

首先,定义构成此曲线的基本体。我用一个S形函数作为阶跃函数的连续近似。

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

接下来,我们需要能够移动这个函数的中心,并使sigmoid函数曲线上下移动得更快。

def sigmoid_at_center(x, center, strength=1):
    return sigmoid((x - center) * strength)

接下来,将奇偶校验函数定义为以4为中心的sigmoid减去以6为中心的sigmoid。强度参数设置为10。

def get_leaky_parity(x):
    return sigmoid_at_center(x, 4, 10) - sigmoid_at_center(x, 6, 10)

最后,根据该函数定义得分函数。

def score(x):
    parity_in_range = get_leaky_parity(x).sum()
    main_score = np.max(np.abs(np.diff(x)))
    return main_score + parity_in_range

然后,您可以使用以下代码使用DIRECT来优化此操作。我发现局部偏差使它能够更快地解决它。

result = direct(score, locally_biased=True, bounds=bounds, vol_tol=0, len_tol=0.001)

通过对目标函数的这种改变,它能够在多达96个维度上解决这个问题。
使用的来源:Lipschitzian Optimization Without the Lipschitz Constant

相关问题