basinhopping可用于搜索全局最优解,采用的是盆地跳跃(即BH)算法。BH算法是1997年,Jonathan Doye博士和他的导师David Wales为了优化原子团簇结构而提出的,其核心概念当然是Basin,也就是盆地,或者坑。
所谓盆地,就是一群局部极小值组成的集合,所以算法的第一步,就是得到这个盆地。具体表现是随机生成一个初始位置 x ⃗ \vec x x,并计算这个位置的适应度 f = f ( x ⃗ ) f=f(\vec x) f=f(x),然后经过一些随机扰动,得到 x ⃗ \vec x x附近的一些值 x ⃗ i \vec x_i xi,并计算这些值的适应度 f i = f ( x ⃗ i ) f_i=f(\vec x_i) fi=f(xi),并得到其极小值。在 f i f_i fi极小值所在位置的 x i x_i xi便称为候选解。
如果此时的 f i f_i fi小于 f f f,也就意味着 f i f_i fi无可争议地称为候选解;否则的话,为了避免陷入局部最优,也有一定的几率成为为候选解。
这样反复迭代之后,就得到了由一群极小值组成的盆地,在这块盆地里,除了初始化时的值之外,每一个极小值,都是从另一个极小值或准极小值跳跃而来,故为盆地跳跃算法。
在scipy.optimize
中,封装了这种全局寻优算法,定义为
basinhopping(func, x0, niter=100, T=1.0, stepsize=0.5, minimizer_kwargs=None, take_step=None, accept_test=None, callback=None, interval=50, disp=False, niter_success=None, seed=None, *, target_accept_rate=0.5, stepwise_factor=0.9)
其中,func
为待拟合函数,x0
为初始值,此二者为必填参数,剩下的为可选参数
minimizer_kwargs
为最小化方法的字典,一般用于调用优化方法,例如{'method':'BFGS'}
,表示调用BFGS
作为局部寻优方案。
接下来找一个函数对basinhopping
进行测试,下面这个例程是官方文档中的
import numpy as np
from scipy.optimize import basinhopping
func = lambda x: np.cos(14.5 * x - 0.3) + (x + 0.2) * x
x0=[1.]
minimizer_kwargs = {"method": "BFGS"}
ret = basinhopping(func, x0, niter=200,
minimizer_kwargs=minimizer_kwargs)
print(f"全局最小值 x = {ret.x[0]:.4f}, f(x) = {ret.fun:.4f}")
# 输出为 全局最小值 x = -0.1951, f(x) = -1.0009
上面这个只是对单值函数进行了测试,属于牛刀小试,接下来搞一个变态一点的多元函数
def test(xs):
_sum = 0.0
for i in range(len(xs)):
_sum = _sum + np.cos((xs[i]*i)/5)*(i+1)
return _sum
xs = [0,0,0,0,0]
mKwargs = {"method": "BFGS"}
ret = basinhopping(test, xs, niter=200,
minimizer_kwargs=mKwargs)
msg = f"全局最小值" + ", ".join([f"{x:.4f}" for x in ret.x])
msg += f"\nf(x)={ret.fun:.4f}"
print(msg)
'''
print(msg)
全局最小值-1.5466, -15.7080, -7.8540, 5.2360, -11.7810
f(x)=-13.0000
'''
可以说效果相当霸道了。