鉴于这个问题缺乏答案和cmets,起初我认为这是不可能完成的。所以我在 Scipy 的 GitHub 页面上打开了一个enhancement issue,并询问是否要添加这个功能。
其中一位贡献者结束了我的问题,他说,尽管以一种棘手的方式(而且在我看来不是很优雅),这是可以做到的,他给了我一些提示。
这是我想出的解决方案,希望对您有所帮助
from scipy.optimize import differential_evolution
from scipy.optimize import rosen
import numpy
class MinimizeStopper(object):
def __init__(self, f=rosen, tau=1):
self.fun = f # set the objective function
self.best_x = None
self.best_func = numpy.inf
self.tau = tau # set the user-desired threshold
def __call__(self, xk, convergence=None, *args, **kwds):
fval = self.fun(xk, *args, **kwds)
if fval < self.best_func:
self.best_func = fval
self.best_x = xk
if self.best_func <= self.tau:
print("Terminating optimization: objective function threshold triggered")
print(self.best_x)
return True
else:
return False
bounds = [(0,2), (0, 2), (0, 2), (0, 2), (0, 2)]
result = differential_evolution(rosen, bounds, callback=MinimizeStopper(), polish=False,disp=True, maxiter=100, popsize=100)
print(result)
返回
differential_evolution step 1: f(x)= 10.7709
differential_evolution step 2: f(x)= 10.7709
differential_evolution step 3: f(x)= 8.02332
differential_evolution step 4: f(x)= 2.16592
differential_evolution step 5: f(x)= 2.16592
differential_evolution step 6: f(x)= 2.16592
differential_evolution step 7: f(x)= 0.812177
Terminating optimization: objective function threshold triggered
[1.01141374 0.95894166 0.91957732 0.87022813 0.70102066]
fun: 0.8121773465012827
message: 'callback function requested stop early by returning True'
nfev: 4000
nit: 7
success: False
x: array([1.01141374, 0.95894166, 0.91957732, 0.87022813, 0.70102066])
几点说明:
- 解决方案不优雅,因为它需要对适应度函数进行额外评估以检查停止标准。不幸的是,由于
scipy.optimize 模块的内部结构,没有解决方法
- 我已经在
rosen 上测试过这种方法,如果目标函数不需要任何额外的参数,它通常可以工作,但如果目标函数需要额外的参数,那么我认为必须使用*args
- 当然,在第二个
if 分支中打印self.best_x 不是强制性的。这只是我添加的一个调试检查,以查看 callback 找到的最佳解决方案是否实际上在 result 中返回(即,differential_evolution() 找到的整体最佳解决方案)