basinhopping#
- 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)[源代码][源代码]#
使用 basin-hopping 算法找到函数的全局最小值。
盆地跳跃法是一种两阶段方法,它结合了全局步进算法与每一步的局部最小化。设计用来模拟原子团簇能量最小化的自然过程,它对于具有“漏斗状但崎岖”能量景观的类似问题效果良好 [5]。
由于步长、步接受和最小化方法都是可自定义的,因此此功能还可用于实现其他两阶段方法。
- 参数:
- func : 可调用的
f(x, *args)
可调用 待优化的函数。
args
可以作为字典 minimizer_kwargs 中的可选项传递。- x0array_like
初始猜测。
- niter整数,可选
盆地跳跃迭代的次数。总共会有
niter + 1
次局部最小化器的运行。- Tfloat, 可选
接受或拒绝标准的“温度”参数。较高的“温度”意味着函数值的较大跳跃将被接受。为了获得最佳结果,T 应与局部最小值之间的(函数值)分离相当。
- 步长float, 可选
用于随机位移的最大步长。
- minimizer_kwargsdict, 可选
传递给本地最小化器
scipy.optimize.minimize
的额外关键字参数。一些重要的选项可能是:- 方法str
最小化方法(例如
"L-BFGS-B"
)- 参数元组
传递给目标函数 (func) 及其导数(雅可比矩阵、海森矩阵)的额外参数。
- take_step : 可调用
take_step(x)
, 可选可调用 用这个例程替换默认的步进例程。默认的步进例程是对坐标的随机位移,但对于某些系统,其他步进算法可能更合适。take_step 可以选择性地拥有属性
take_step.stepsize
。如果存在此属性,那么basinhopping
将调整take_step.stepsize
以尝试优化全局最小值搜索。- accept_test : 可调用对象,
accept_test(f_new=f_new, x_new=x_new, f_old=fold, x_old=x_old)
, 可选可调用对象, 定义一个测试,用于判断是否接受该步骤。这将在基于“温度” T 的 Metropolis 测试之外使用。可接受的返回值为 True、False 或
"force accept"
。如果任何测试返回 False,则该步骤将被拒绝。如果是后者,则这将覆盖任何其他测试以接受该步骤。例如,这可以用于强制逃离basinhopping
陷入的局部最小值。- 回调函数 : 可调用对象,
callback(x, f, accept)
, 可选可调用对象, 一个回调函数,将在找到所有最小值时被调用。
x
和f
是试验最小值的坐标和函数值,accept
表示该最小值是否被接受。例如,这可以用于保存找到的最低 N 个最小值。此外,callback 可以用于通过可选地返回 True 来指定用户定义的停止标准,从而停止basinhopping
例程。- 间隔整数,可选
更新 stepsize 的间隔时间
- dispbool, 可选
设置为 True 以打印状态消息
- niter_success整数,可选
如果在这么多迭代中全局最小候选值保持不变,则停止运行。
- seed : {None, int,
numpy.random.Generator
,numpy.random.RandomState
}, 可选{None, int,} 如果 seed 是 None(或 np.random),则使用
numpy.random.RandomState
单例。如果 seed 是 int,则使用一个新的RandomState
实例,并以 seed 作为种子。如果 seed 已经是Generator
或RandomState
实例,则使用该实例。指定 seed 以实现可重复的最小化。使用此种子生成的随机数仅影响默认的 Metropolis accept_test 和默认的 take_step。如果您提供了自己的 take_step 和 accept_test,并且这些函数使用随机数生成,那么这些函数负责其随机数生成器的状态。- 目标接受率float, 可选
用于调整 stepsize 的目标接受率。如果当前接受率大于目标值,则增加 stepsize。否则,减少 stepsize。范围是 (0, 1)。默认值为 0.5。
Added in version 1.8.0.
- stepwise_factorfloat, 可选
每次更新时,stepsize 会乘以或除以这个逐步因子。范围是 (0, 1)。默认值是 0.9。
Added in version 1.8.0.
- func : 可调用的
- 返回:
- res优化结果
优化结果表示为一个
OptimizeResult
对象。重要的属性包括:x
解数组,fun
解处的函数值,以及message
描述终止原因。在最低最小值处返回的OptimizeResult
对象也包含在此对象中,可以通过lowest_optimization_result
属性访问。有关其他属性的描述,请参见OptimizeResult
。
参见
minimize
本地最小化函数在每次 basinhopping 步骤中调用一次。minimizer_kwargs 被传递给此例程。
注释
盆地跳跃是一种随机算法,它试图找到一个或多个变量的光滑标量函数的全局最小值 [1] [2] [3] [4]。该算法在其当前形式中由David Wales和Jonathan Doye描述 [2] http://www-wales.ch.cam.ac.uk/。
该算法是迭代的,每个周期由以下特征组成
坐标随机扰动
局部最小化
根据最小化后的函数值接受或拒绝新的坐标
这里使用的接受测试是标准蒙特卡罗算法的Metropolis准则,尽管还有许多其他可能性 [3]。
这种全局最小化方法已被证明在物理和化学的广泛问题中极为高效。当函数有许多被大障碍分隔的极小值时,它特别有用。请参阅 剑桥集群数据库 ,该数据库包含主要使用 basin-hopping 优化过的分子系统。该数据库包括超过 300 个自由度的最小化问题。
请参阅免费的软件程序 GMIN ,这是一个基于Fortran的 basin-hopping 实现。此实现包含上述过程的许多变体,包括更高级的步进算法和替代接受准则。
对于随机全局优化,无法确定是否已经找到了真正的全局最小值。相反,作为一种一致性检查,可以从多个不同的随机起点运行算法,以确保在每个示例中找到的最低最小值已收敛到全局最小值。因此,
basinhopping
默认情况下只会运行 niter 次迭代并返回找到的最低最小值。用户需要确保这确实是全局最小值。选择 stepsize: 这是
basinhopping
中的一个关键参数,取决于所解决的问题。步长在每个维度上从 x0-stepsize 到 x0+stepsize 的区域内均匀选择。理想情况下,它应与被优化函数局部最小值之间的典型分离(在参数值中)相当。basinhopping
默认会调整 stepsize 以找到一个最佳值,但这可能需要多次迭代。如果你为stepsize
设置一个合理的初始值,你将更快地得到结果。选择 T:参数 T 是在 Metropolis 准则中使用的“温度”。如果
func(xnew) < func(xold)
,则 Basinhopping 步骤总是被接受。否则,它们以以下概率被接受::exp( -(func(xnew) - func(xold)) / T )
因此,为了获得最佳结果,T 应该与局部最小值之间的典型差异(在函数值中)相当。(局部最小值之间的“墙”的高度是无关紧要的。)
如果 T 为 0,该算法变为单调盆地跳跃法,其中所有增加能量的步骤都会被拒绝。
Added in version 0.12.0.
参考文献
[1]Wales, David J. 2003, 能量景观, 剑桥大学出版社, 英国剑桥。
[2] (1,2)Wales, D J, 和 Doye J P K, 通过盆地跳跃进行全局优化及其包含多达110个原子的Lennard-Jones团簇的最低能量结构。《物理化学杂志A》, 1997, 101, 5111.
[3] (1,2)Li, Z. 和 Scheraga, H. A., 蒙特卡罗-最小化方法解决蛋白质折叠中的多重最小值问题, Proc. Natl. Acad. Sci. USA, 1987, 84, 6611.
[4]Wales, D. J. 和 Scheraga, H. A., 簇、晶体和生物分子的全局优化, Science, 1999, 285, 1368.
[5]Olson, B., Hashmi, I., Molloy, K., 和 Shehu1, A., 盆地跳跃作为一种通用且多功能的优化框架用于生物大分子的表征, 人工智能进展, 第2012卷 (2012), 文章ID 674832, DOI:10.1155/2012/674832
示例
以下示例是一个一维最小化问题,有许多局部最小值叠加在一个抛物线上。
>>> 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.]
Basinhopping 在内部使用局部最小化算法。我们将使用参数 minimizer_kwargs 来告诉 basinhopping 使用哪种算法以及如何设置该最小化器。此参数将传递给
scipy.optimize.minimize
。>>> minimizer_kwargs = {"method": "BFGS"} >>> ret = basinhopping(func, x0, minimizer_kwargs=minimizer_kwargs, ... niter=200) >>> # the global minimum is: >>> ret.x, ret.fun -0.1951, -1.0009
接下来考虑一个二维最小化问题。同样,这次我们将使用梯度信息来显著加快搜索速度。
>>> def func2d(x): ... f = np.cos(14.5 * x[0] - 0.3) + (x[1] + 0.2) * x[1] + (x[0] + ... 0.2) * x[0] ... df = np.zeros(2) ... df[0] = -14.5 * np.sin(14.5 * x[0] - 0.3) + 2. * x[0] + 0.2 ... df[1] = 2. * x[1] + 0.2 ... return f, df
我们还将使用一种不同的局部最小化算法。此外,我们必须告诉最小化器我们的函数返回能量和梯度(雅可比矩阵)。
>>> minimizer_kwargs = {"method":"L-BFGS-B", "jac":True} >>> x0 = [1.0, 1.0] >>> ret = basinhopping(func2d, x0, minimizer_kwargs=minimizer_kwargs, ... niter=200) >>> print("global minimum: x = [%.4f, %.4f], f(x) = %.4f" % (ret.x[0], ... ret.x[1], ... ret.fun)) global minimum: x = [-0.1951, -0.1000], f(x) = -1.0109
以下是使用自定义步进例程的示例。假设您希望第一个坐标比其余坐标采取更大的步长。这可以如下实现:
>>> class MyTakeStep: ... def __init__(self, stepsize=0.5): ... self.stepsize = stepsize ... self.rng = np.random.default_rng() ... def __call__(self, x): ... s = self.stepsize ... x[0] += self.rng.uniform(-2.*s, 2.*s) ... x[1:] += self.rng.uniform(-s, s, x[1:].shape) ... return x
由于
MyTakeStep.stepsize
存在,basinhopping 将调整 stepsize 的大小以优化搜索。我们将使用与之前相同的 2-D 函数。>>> mytakestep = MyTakeStep() >>> ret = basinhopping(func2d, x0, minimizer_kwargs=minimizer_kwargs, ... niter=200, take_step=mytakestep) >>> print("global minimum: x = [%.4f, %.4f], f(x) = %.4f" % (ret.x[0], ... ret.x[1], ... ret.fun)) global minimum: x = [-0.1951, -0.1000], f(x) = -1.0109
现在,让我们用一个自定义回调函数来做一个示例,该函数会打印找到的每个最小值。
>>> def print_fun(x, f, accepted): ... print("at minimum %.4f accepted %d" % (f, int(accepted)))
这次我们只运行10个 basinhopping 步骤。
>>> rng = np.random.default_rng() >>> ret = basinhopping(func2d, x0, minimizer_kwargs=minimizer_kwargs, ... niter=10, callback=print_fun, seed=rng) at minimum 0.4159 accepted 1 at minimum -0.4317 accepted 1 at minimum -1.0109 accepted 1 at minimum -0.9073 accepted 1 at minimum -0.4317 accepted 0 at minimum -0.1021 accepted 1 at minimum -0.7425 accepted 1 at minimum -0.9073 accepted 1 at minimum -0.4317 accepted 0 at minimum -0.7425 accepted 1 at minimum -0.9073 accepted 1
在 -1.0109 处的最小值实际上是全局最小值,已经在第 8 次迭代中找到。