在 Python 中使用 NLOPT 进行最大函数评估

计算科学 优化 Python
2021-12-09 22:00:25

我在 Python 中实现 NLOPT 时遇到问题。我的目标是最小化一个有点复杂的最大似然函数。

我的函数称为 mle,有 6 个参数需要估计。

找到这个 MLE 的梯度并非易事,所以我决定转向数值梯度函数:

def numgrad(f, x, step=1e-6):
    """numgrad(f: function, x: num array, step: num) -> num array

    Numerically estimates the gradient of a function f which takes an array as
    its argument.
    """
    ary = len(x)

    curr = x * sp.ones((ary, ary))
    next = curr + sp.identity(ary) * step

    delta = sp.apply_along_axis(f, 1, next) - sp.apply_along_axis(f, 1, curr)

    return delta / step

然后我的 NLOPT 实现是这样的:

def myfunc(x, grad):
    if grad.size > 0:
        grad = numgrad(mle, [x[0], x[1], x[2], x[3], x[4], x[5]], step=1e-14)
    return mle([x[0], x[1], x[2], x[3], x[4], x[5]])

opt = nlopt.opt(nlopt.LD_SLSQP, 6)
opt.set_lower_bounds([mmin, smin, ming, bmin, vmin, pmin]) #min bound for each of the param.
opt.set_upper_bounds([mmax, smax, maxg, bmax, vmax, pmax])
opt.set_min_objective(myfunc)
opt.set_xtol_rel(1e-15)
opt.maxeval=10000
x = opt.optimize([x1, x2, x3, x4, x5, x6])
minf = opt.last_optimum_value()
print "optimum at ", x[0], x[1], x[2], x[3], x[4], x[5]
print "minimum value = ", minf
print "result code = ", opt.last_optimize_result()

现在的问题是......最小化过程太快了。在 matlab 中,大约需要 1 小时,而在 Python 中需要 12 秒……我在使用 fmincon 的 Matlab 中没有得到相同的结果。

我的感觉是代码无法识别opt.set_xtol_rel(1e-15)andopt.maxeval=10000语句,因为即使我增加数字......时间过程没有变化......

或者问题出在其他地方......我做错了什么?

2个回答

您基本上不应该以数字方式估计梯度。

你说梯度很难估计。一般来说,如果完全有可能获得梯度,你应该这样做,并使用适当的算法(NLopt 有几个,你使用的那个应该没问题)。

但是,如果您无法准确获得梯度,NLopt 提供了几种无导数算法,您可以使用它们来代替并期望获得更好的结果。我认为这可能是解决您的问题的最简单方法,并且会给您带来更好的结果。

然而,速度差异很容易是真实的。优化算法的差异以及 python 通常比 Matlab 快的事实可以很容易地解释这种差异。

TLDR:从 nlopt.LD_SLSQP 更改为 nlopt.LN_BOBYQA 。

希望这可以帮助。

要将梯度从 Python 传递到 NLopt,您必须

grad[:] = gradient()  # or np.copyto

不是

grad = gradient()  # wrong, a new pointer

NLopt Python 文档 对此有很大的警告;不幸的是,它不在您使用的教程示例中。

顺便说一句,在随机起点上,请参阅 NLopt MLSL
“从随机起点开始的一系列局部优化(使用其他一些局部优化算法)......聚类启发式......”。这个主题有很多变化。
此外,fwiw,该 显示了 8d 中十几个学术测试函数的 10 个随机起点的 3 个 NLopt 方法。