请考虑以下简单的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次函数判断后放弃了,并发出了警告.

有什么方法可以直接优化这个函数吗?

推荐答案

终止参数vol_tol隐含地依赖于问题的维度数目.搜索空间被分成一系列超矩形,其中最小的中间超矩形的大小为(1/3)^n,其中n是维度的数目.

当n=20时,这意味着最内侧的立方体的体积将为2.8e-10.如果最里面的立方体的中点恰好是最低点,那么该立方体将再次细分.由于vol_tol默认为1e-16,这意味着算法将仅在两次迭代后退出.

如果不希望vol_tol导致DIRECT提前退出,可以将vol_tol设置为0:

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.

橙色线表示一个新函数,该函数向下倾斜到边缘,提示优化器在该方向上存在较低的值.

objective function graph

首先,定义组成这条曲线的基本体.我使用Sigmoid函数作为阶跃函数的连续近似值.

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,减go 以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个维度上解决这个问题.

Sources used: Lipschitzian Optimization Without the Lipschitz Constant

Python相关问答推荐

如何在PIL、Python中对图像应用彩色面膜?

指示组内的rejected_time是否在creation_timestamp后5分钟内

将轨迹优化问题描述为NLP.如何用Gekko解决这个问题?当前面临异常:@错误:最大方程长度错误

如何使用矩阵在sklearn中同时对每个列执行matthews_corrcoef?

Python plt.text中重叠,包adjust_text不起作用,如何修复?

如果索引不存在,pandas系列将通过索引获取值,并填充值

将HTML输出转换为表格中的问题

2维数组9x9,不使用numpy.数组(MutableSequence的子类)

pandas DataFrame GroupBy.diff函数的意外输出

如何检测背景有噪的图像中的正方形

如何在Python脚本中附加一个Google tab(已经打开)

如何在给定的条件下使numpy数组的计算速度最快?

在含噪声的3D点网格中识别4连通点模式

在单个对象中解析多个Python数据帧

如果初始groupby找不到满足掩码条件的第一行,我如何更改groupby列,以找到它?

Numpyro AR(1)均值切换模型抽样不一致性

在二维NumPy数组中,如何 Select 内部数组的第一个和第二个元素?这可以通过索引来实现吗?

将一个双框爆炸到另一个双框的范围内

使用Python异步地持久跟踪用户输入

删除特定列后的所有列