我有一个包含64个变量的Python函数,我尝试在最小化函数中使用L-BFGS-B方法对其进行优化,但是这种方法对初始猜测非常依赖,并且未能找到全局最小值。
但我喜欢它为变量设置界限的能力。是否存在一种方法/函数来找到全局最小值,同时具有变量的边界?
答案 0 :(得分:27)
可以使用scipy.optimize.basinhopping
完成此操作。 Basinhopping是一个旨在找到目标函数的全局最小值的函数。它使用函数scipy.optimize.minimize
重复最小化,并在每次最小化后在坐标空间中进行随机步骤。通过使用实现边界的最小化器之一(例如L-BFGS-B),流域购物仍然可以遵守边界。以下是一些显示如何执行此操作的代码
# an example function with multiple minima
def f(x): return x.dot(x) + sin(np.linalg.norm(x) * np.pi)
# the starting point
x0 = [10., 10.]
# the bounds
xmin = [1., 1.]
xmax = [11., 11.]
# rewrite the bounds in the way required by L-BFGS-B
bounds = [(low, high) for low, high in zip(xmin, xmax)]
# use method L-BFGS-B because the problem is smooth and bounded
minimizer_kwargs = dict(method="L-BFGS-B", bounds=bounds)
res = basinhopping(f, x0, minimizer_kwargs=minimizer_kwargs)
print res
上面的代码适用于一个简单的情况,但是如果盆地随机位移例程将你带到那里,你仍然可以最终进入一个禁区。幸运的是,可以通过使用关键字take_step
class RandomDisplacementBounds(object):
"""random displacement with bounds"""
def __init__(self, xmin, xmax, stepsize=0.5):
self.xmin = xmin
self.xmax = xmax
self.stepsize = stepsize
def __call__(self, x):
"""take a random step but ensure the new position is within the bounds"""
while True:
# this could be done in a much more clever way, but it will work for example purposes
xnew = x + np.random.uniform(-self.stepsize, self.stepsize, np.shape(x))
if np.all(xnew < self.xmax) and np.all(xnew > self.xmin):
break
return xnew
# define the new step taking routine and pass it to basinhopping
take_step = RandomDisplacementBounds(xmin, xmax)
result = basinhopping(f, x0, niter=100, minimizer_kwargs=minimizer_kwargs,
take_step=take_step)
print result
答案 1 :(得分:5)
调试和可视化任何优化器的一些常识性建议 关于你的功能:
您的目标函数和约束条件是否合理?
如果目标函数是总和f() + g()
,
为x
中的所有"fx-opt.nptxt"
分别打印这些内容(下方);
如果f()
为总金额的99%且g()
为1%,请进行调查。
约束:x_i
中有多少组件xfinal
被困在边界,
x_i <= lo_i
或>= hi_i
?
<小时/> 你在全球范围内的功能有多坎坷? 运行几个随机起始点,并将结果保存到analyze / plot:
title = "%s n %d ntermhess %d nsample %d seed %d" % ( # all params!
__file__, n, ntermhess, nsample, seed )
print title
...
np.random.seed(seed) # for reproducible runs
np.set_printoptions( threshold=100, edgeitems=10, linewidth=100,
formatter = dict( float = lambda x: "%.3g" % x )) # float arrays %.3g
lo, hi = bounds.T # vecs of numbers or +- np.inf
print "lo:", lo
print "hi:", hi
fx = [] # accumulate all the final f, x
for jsample in range(nsample):
# x0 uniformly random in box lo .. hi --
x0 = lo + np.random.uniform( size=n ) * (hi - lo)
x, f, d = fmin_l_bfgs_b( func, x0, approx_grad=1,
m=ntermhess, factr=factr, pgtol=pgtol )
print "f: %g x: %s x0: %s" % (f, x, x0)
fx.append( np.r_[ f, x ])
fx = np.array(fx) # nsample rows, 1 + dim cols
np.savetxt( "fx-opt.nptxt", fx, fmt="%8.3g", header=title ) # to analyze / plot
ffinal = fx[:,0]
xfinal = fx[:,1:]
print "final f values, sorted:", np.sort(ffinal)
jbest = ffinal.argmin()
print "best x:", xfinal[jbest]
如果某些ffinal
值看起来相当不错,
尝试更近的随机起点 -
这肯定比纯随机好。
如果x
是曲线或任何真实曲线,请绘制最佳的x0
和xfinal
。
(经验法则是d
维度中的样本~5 * d或10 * d。
太慢了,太多了?减少maxiter
/ maxeval
,减少ftol
-
你不需要像ftol
1e-6那样进行探索。)
如果您想要可重复的结果,
然后你必须列出title
中的所有相关参数
以及派生文件和图表。
否则,你会问“这个来自哪里?”
<小时/> 你的函数在epsilon规模上有多么坎坷~10 ^ -6?
from scipy.optimize._numdiff import approx_derivative # 3-point, much better than
## from scipy.optimize import approx_fprime
for eps in [1e-3, 1e-6]:
grad = approx_fprime( x, func, epsilon=eps )
print "approx_fprime eps %g: %s" % (eps, grad)
然而,如果在优化器退出之前梯度估计很差/颠簸,
你不会看到的。
然后你必须保存所有中间[f, x, approx_fprime]
看他们;在python中很容易 - 问一下这是不是很清楚。
在某些问题区域,通常会从声称的xmin
进行备份和重新启动。
例如,如果你在乡间小路上迷路,
先找到一条主要道路,然后从那里重新开始。
<小时/> 摘要:
答案 2 :(得分:1)
非常感谢你的详细回复,但是作为我对python的新手,我不太知道如何在我的程序中实现代码,但这是我尝试优化:
x0=np.array((10, 13, f*2.5, 0.08, 10, f*1.5, 0.06, 20,
10, 14, f*2.5, 0.08, 10, f*1.75, 0.07, 20,
10, 15, f*2.5, 0.08, 10, f*2, 0.08, 20,
10, 16, f*2.5, 0.08, 10, f*2.25, 0.09, 20,
10, 17, f*2.5, -0.08, 10, f*2.5, -0.06, 20,
10, 18, f*2.5, -0.08, 10, f*2.75,-0.07, 20,
10, 19, f*2.5, -0.08, 10, f*3, -0.08, 20,
10, 20, f*2.5, -0.08, 10, f*3.25,-0.09, 20))
# boundary for each variable, each element in this restricts the corresponding element above
bnds=((1,12), (1,35), (0,f*6.75), (-0.1, 0.1),(1,35), (0,f*6.75), (-0.1, 0.1),(13, 35),
(1,12), (1,35), (0,f*6.75), (-0.1, 0.1),(1,35), (0,f*6.75), (-0.1, 0.1),(13, 35),
(1,12), (1,35), (0,f*6.75), (-0.1, 0.1),(1,35), (0,f*6.75), (-0.1, 0.1),(13, 35),
(1,12), (1,35), (0,f*6.75), (-0.1, 0.1),(1,35), (0,f*6.75), (-0.1, 0.1),(13, 35),
(1,12), (1,35), (0,f*6.75), (-0.1, 0.1),(1,35), (0,f*6.75), (-0.1, 0.1),(13, 35),
(1,12), (1,35), (0,f*6.75), (-0.1, 0.1),(1,35), (0,f*6.75), (-0.1, 0.1),(13, 35),
(1,12), (1,35), (0,f*6.75), (-0.1, 0.1),(1,35), (0,f*6.75), (-0.1, 0.1),(13, 35),
(1,12), (1,35), (0,f*6.75), (-0.1, 0.1),(1,35), (0,f*6.75), (-0.1, 0.1),(13, 35), )
from scipy.optimize import basinhopping
from scipy.optimize import minimize
merit=a*meritoflength + b*meritofROC + c*meritofproximity +d*(distancetoceiling+distancetofloor)+e*heightorder
minimizer_kwargs = {"method": "L-BFGS-B", "bounds": bnds, "tol":1e0}
ret = basinhopping(merit_function, x0, minimizer_kwargs=minimizer_kwargs, niter=10, T=0.01)
zoom = ret['x']
res = minimize(merit_function, zoom, method = 'L-BFGS-B', bounds=bnds, tol=1e-5)
print res
优值函数将x0与其他一些值组合在一起,形成8条曲线的6个控制点,然后计算它们的长度,曲率半径等。它将最终的优点作为这些参数与一些权重的线性组合。
我使用精度较低的basinhopping
来找到一些最小值,然后使用minimize
来提高最低值的精度。
P.S。我正在运行的平台是Enthoght canopy 1.3.0,numpy 1.8.0 scipy 0.13.2 mac 10.8.3
答案 3 :(得分:0)
自从您提出这个问题以来,全局优化方面已经取得了一些不错的进展,可能会对您有所帮助。特别是,我会提请您注意 SHGO 算法 (package),它现在也在 scipy.optimize 中作为标准选项之一。但是,如果您真的无法减少它,它可能会与您的搜索空间的维数斗争。
您可以尝试一些经典的方法,例如模式搜索或 PySOT 中的代理方法,其中 benchmark very well 也是如此。如果您真的遇到困难,请考虑使用 optuna 之类的东西,或者,如果您不顾一切,请考虑 hyperopt。您还可以尝试一系列受自然启发的算法,但您会从我提供的链接中看到,它们并不总是像宣传的那样有效。