我正在尝试将scipy.optimize.minimize
用于简单的a <= x <= b
范围。但是,经常发生我的目标函数被评估超出范围的情况。据我了解,这是在minimize
试图确定目标函数在边界处的梯度时发生的。
最小示例:
import math
import numpy as np
from scipy.optimize import Bounds, minimize
constraint = Bounds([-1, -1], [1, 1], True)
def fun(x):
print(x)
return -math.exp(-np.dot(x,x))
result = minimize(fun, [-1, -1], bounds=constraint)
输出显示最小化器跳到点[1,1]
,然后尝试在[1.00000001, 1]
处求值:
[-1. -1.]
[-0.99999999 -1. ]
[-1. -0.99999999]
[-0.72932943 -0.72932943]
[-0.72932942 -0.72932943]
[-0.72932943 -0.72932942]
[-0.22590689 -0.22590689]
[-0.22590688 -0.22590689]
[-0.22590689 -0.22590688]
[1. 1.]
[1.00000001 1. ]
[1. 1.00000001]
[-0.03437328 -0.03437328]
...
当然,在此示例中没有问题,因为fun
也可以在那里求值。但这并非总是如此...
在我的实际问题中,最小值不能在边界上,并且我可以轻松地在边界上添加epsilon。 但是有人希望应该有一个简单的解决方案,如果最小的可以处于边界,该方法也可以解决?
PS:如果我是第一个遇到这个问题的人,这将很奇怪-如果在某个地方问过这个问题,但是我在任何地方都找不到它,那就对不起。
答案 0 :(得分:0)
正如here所讨论的(感谢@“ Welcome to Stack Overflow”的评论将我引导到那里),问题确实是梯度例程不遵守边界。 我写了一个新的来完成这项工作:
import math
import numpy as np
from scipy.optimize import minimize
def gradient_respecting_bounds(bounds, fun, eps=1e-8):
"""bounds: list of tuples (lower, upper)"""
def gradient(x):
fx = fun(x)
grad = np.zeros(len(x))
for k in range(len(x)):
d = np.zeros(len(x))
d[k] = eps if x[k] + eps <= bounds[k][1] else -eps
grad[k] = (fun(x + d) - fx) / d[k]
return grad
return gradient
bounds = ((-1, 1), (-1, 1))
def fun(x):
print(x)
return -math.exp(-np.dot(x,x))
result = minimize(fun, [-1, -1], bounds=bounds,
jac=gradient_respecting_bounds(bounds, fun))
请注意,这可能会降低效率,因为fun(x)
现在在每个点都被评估两次。
lbfgsb.py中_minimize_lbfgsb
的相关片段似乎是不可避免的:
if jac is None:
def func_and_grad(x):
f = fun(x, *args)
g = _approx_fprime_helper(x, fun, epsilon, args=args, f0=f)
return f, g
else:
def func_and_grad(x):
f = fun(x, *args)
g = jac(x, *args)
return f, g
如您所见,f
的值只能由内部_approx_fprime_helper
函数重用。