最小化 python 函数,它在小间隔上是常数

Minimization python function which is constant on small intervals

我想在有边界和约束的区域上最小化凸函数,因此我尝试使用 scipy.optimize.minimizeSLSQP 选项。但是我的函数只在离散点定义。线性插值似乎不是一种选择,因为在所有值中计算我的函数会花费太多时间。作为一个最小的工作示例,我有:

from scipy.optimize import minimize
import numpy as np

f=lambda x : x**2

N=1000000
x_vals=np.sort(np.random.random(N))*2-1
y_vals=f(x_vals)

def f_disc(x, x_vals, y_vals):
    return y_vals[np.where(x_vals<x)[-1][-1]]

print(minimize(f_disc, 0.5, method='SLSQP', bounds = [(-1,1)], args = (x_vals, y_vals)))

产生以下输出:

     fun: 0.24999963136767756
     jac: array([ 0.])
 message: 'Optimization terminated successfully.'
    nfev: 3
     nit: 1
    njev: 1
  status: 0
 success: True
       x: array([ 0.5])

我们当然知道它是错误的,但是 f_disc 的定义让优化器相信它在给定索引处是常量。对于我的问题,我只有 f_disc 而无法访问 f。此外,调用 f_disc 可能需要一分钟。

如果您的功能不流畅gradient-based优化技术将失败。当然你可以使用不基于梯度的方法,但这些通常需要更多的函数评估。

这里有两个可行的选项。

nelder-mead method不需要梯度,但它有一个缺点,就是不能处理边界或约束:

print(minimize(f_disc, 0.5, method='nelder-mead', args = (x_vals, y_vals)))

 #  final_simplex: (array([[ -4.44089210e-16], [  9.76562500e-05]]), array([  2.35756658e-12,   9.03710082e-09]))
 #            fun: 2.3575665763730149e-12
 #        message: 'Optimization terminated successfully.'
 #           nfev: 32
 #            nit: 16
 #         status: 0
 #        success: True
 #              x: array([ -4.44089210e-16])

differential_evolution 是一个优化器,不对平滑度做任何假设。它不仅可以处理边界;它需要他们。但是,它比 nelder-mead.

需要更多的函数评估
print(differential_evolution(f_disc, bounds = [(-1,1)], args = (x_vals, y_vals)))

#     fun: 5.5515134011907119e-13
# message: 'Optimization terminated successfully.'
#    nfev: 197
#     nit: 12
# success: True
#       x: array([  2.76298719e-06])