有关 Python 中 scipy.optimize.minimize 的问题和疑问

问题描述 投票:0回答:1

我对 scipy 的 optimization.minimize 例程有一些疑问和问题。我想最小化该功能:

f(eta) = sum_i |eta*x_i - y_i|

关于eta。由于我对最小化例程和相应的方法不熟悉,所以我尝试了一些。但是,使用 BFGS 方法会引发以下错误:

File "/usr/local/lib/python3.4/dist-packages/scipy/optimize/_minimize.py", line 441, in minimize return _minimize_bfgs(fun, x0, args, jac, callback, **options)
File "/usr/local/lib/python3.4/dist-packages/scipy/optimize/optimize.py", line 904, in _minimize_bfgs
A1 = I - sk[:, numpy.newaxis] * yk[numpy.newaxis, :] * rhok
IndexError: 0-d arrays can only use a single () or a list of newaxes (and a single ...) as an index

我无法解决。请查找导致以下错误的代码。我在 Ubuntu 14.04.3 LTS 上使用 Python3 和 scipy 0.17.0 和 numpy 1.8.2。

此外,共轭梯度方法似乎比其他方法表现更差。

最后但并非最不重要的一点是,我赞成通过 scipy.optimize.brentq 找到一阶导数的零来估计最小值。这可以吗还是您推荐另一种方法?比起速度,我更喜欢稳健性。

这里有一些代码说明了问题和疑问:

from scipy import optimize
import numpy as np

def function(x, bs, cs):
    sum = 0.
    for b, c in zip(bs, cs):
        sum += np.abs(x*b - c)
    return sum

def derivativeFunction(x, bs, cs):
    sum = 0.
    for b, c in zip(bs, cs):
        if x*b > c:
            sum += b
        else:
            sum -= b
    return sum

np.random.seed(1000)
bs = np.random.rand(10)
cs = np.random.rand(10)

eta0 = 0.5

res = optimize.minimize(fun=function, x0=eta0, args=(bs, cs), method='Nelder-Mead', tol=1e-6)
print('Nelder-Mead:\t', res.x[0], function(res.x[0], bs, cs))

res = optimize.minimize(fun=function, x0=eta0, args=(bs, cs,),  method='CG', jac=derivativeFunction, tol=1e-6)
print('CG:\t', res.x[0], function(res.x[0], bs, cs))

x = optimize.brentq(f=derivativeFunction, a=0, b=2., args=(bs, cs), xtol=1e-6, maxiter=100) 
print('Brentq:\t', x, function(x, bs, cs))

#Throwing the error
res = optimize.minimize(fun=function, x0=eta0, args=(bs, cs), method='BFGS', jac=derivativeFunction, tol=1e-6)
print('BFGS:\t', res.x[0], function(res.x[0], bs, cs))

它的输出是:

Nelder-Mead:     0.493537902832 3.71986334101
CG:  0.460178525461 3.72659733011
Brentq:  0.49353725172947666 3.71986347245

其中第一个值是最小值的位置,第二个值是最小值本身。输出错过了上面的错误消息。

谢谢您的帮助!

python scipy mathematical-optimization minimize
1个回答
0
投票

在 SciPy 1.12 中,您的代码产生输出:

Nelder-Mead:     0.49353790283203125 3.7198633410125184
CG:  0.5 3.729437707615416
Brentq:  0.4935375286374616 3.7198634165481654
BFGS:    0.4756548864945291 3.7234732416312872

没有警告或错误。

最后但并非最不重要的一点是,我赞成通过

scipy.optimize.brentq
找到一阶导数的零来估计最小值。这可以吗还是您推荐另一种方法?比起速度,我更喜欢稳健性。

这是一个非常好的解决方案。如果导数在您提供的括号内恰好过零一次,则

brentq
基本上可以保证找到零交叉点。这是非常强大的,而且它比您建议的其他方法快了 10 倍。 这并不奇怪,因为

brentq

专门针对一个变量的函数。它利用了标量情况的简单性,而其他情况则适用于具有任意数量输入的函数。

(对于足够平滑的函数,找到导数的过零通常可以比找到函数的最小值更精确。这是因为函数的斜率在最小值处为零,因此函数的最小值64 位精度的数字可能不是唯一的,但过零通常是唯一的。)

另一个合理的解决方案是使用标量最小化器

scipy.optimize.brent

optimize.brent(function, brack=(eta0-0.1, eta0+0.1), args=(bs, cs)) # 0.49353790029693073

如果您提供有效的
三点括号

并且函数在括号内是单峰的,则基本上也可以保证找到最小值。但在这种情况下似乎有点慢,并且找到有效的三点最小化括号通常比找到有效的两点求根括号要多一些工作,所以继续坚持使用brentq

    

© www.soinside.com 2019 - 2024. All rights reserved.