SciPy:优化.shgo函数传递参数的问题

3
我正在尝试使用SciPy中实现的SHGO算法,但是当目标函数需要多个参数时会遇到问题。如果我正确理解错误,那么我没有按预期向目标函数传递附加参数(参数),但是我不知道我的语法哪里出了错。有人能解释一下错误的根本原因以及如何解决吗?

下面是我所面临问题的可重现示例。
Image presenting the mathematical formulation of the problem implemented in the Python code
import numpy as np
import scipy.optimize as opt


def fobj(x, y, z):
    return (x+y+z).sum()

x0 = np.array([0.5, 0.5, 0.5, 0.5])
y = np.array([1, 3, 5, 7])
z = np.array([10, 20, 30, 40])
bnds = list(zip([0, 1, 2, 3], [2, 3, 4, 5]))
cons = {'type': 'eq', 'fun': lambda x: x.sum() - 14}
min_kwargs = {'method': 'SLSQP', 'options': {'maxiter': 100, 'disp': True}}
ret = opt.shgo(func=fobj, bounds=bnds, args=(y, z), constraints=cons, minimizer_kwargs=min_kwargs, options={'disp': True})

运行时会显示以下回溯信息。

Splitting first generation
Traceback (most recent call last):
  File "C:\path\lib\site-packages\scipy\optimize\_shgo_lib\triangulation.py", line 630, in __getitem__
    return self.cache[x]
KeyError: (0, 0, 0, 0)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\path\lib\site-packages\scipy\optimize\_shgo.py", line 420, in shgo
    shc.construct_complex()
  File "C:\path\lib\site-packages\scipy\optimize\_shgo.py", line 733, in construct_complex
    self.iterate()
  File "C:\path\lib\site-packages\scipy\optimize\_shgo.py", line 876, in iterate
    self.iterate_complex()
  File "C:\path\lib\site-packages\scipy\optimize\_shgo.py", line 895, in iterate_hypercube
    self.HC = Complex(self.dim, self.func, self.args,
  File "C:\path\lib\site-packages\scipy\optimize\_shgo_lib\triangulation.py", line 25, in __init__
    self.n_cube(dim, symmetry=symmetry)
  File "C:\path\lib\site-packages\scipy\optimize\_shgo_lib\triangulation.py", line 76, in n_cube
    self.C0.add_vertex(self.V[origintuple])
  File "C:\path\lib\site-packages\scipy\optimize\_shgo_lib\triangulation.py", line 634, in __getitem__
    xval = Vertex(x, bounds=self.bounds,
  File "C:\path\lib\site-packages\scipy\optimize\_shgo_lib\triangulation.py", line 557, in __init__
    self.f = func(x_a, *func_args)
  File "C:\path\lib\site-packages\scipy\optimize\_optimize.py", line 466, in function_wrapper
    fx = function(np.copy(x), *(wrapper_args + args))
TypeError: fobj() takes 3 positional arguments but 5 were given

我不明白为什么会引发TypeError。它说目标函数 fobj 中传递了5个参数而不是3个,但我只传递了(y, z),所以我不知道它们怎么会变成5个!
请注意,我还尝试将本地最小化器字典重写为min_kwargs = {'method': 'SLSQP', 'args': (x0), 'options': {'maxiter': 100, 'disp': True}},但我仍然遇到相同的错误。我确定我没有正确传递参数,但我不知道该如何正确操作。非常感谢您的任何帮助。

如果漏掉了 sum 后面的括号是在创建问题时误输的,那么您应该编辑问题并修正代码,以便可以使用问题中的代码重现 实际 问题。 - Warren Weckesser
我纠正了一个打字错误(在函数fobjreturn行中,在.sum后添加了缺失的括号)。我认为这并不重要,因为另一个用户已经在下面的答案中捕捉并提到了它。对于造成的困惑,我感到抱歉。无论如何,根据此GitHub问题的报告,似乎这是SHGO当前实现(截至SciPy v1.8.1)的一个实际错误。有什么快速修复的想法吗? - AB8
2个回答

4

正如你发现的那样,这个问题是 shgo 的一个bug

避免使用 args 参数可以避免 shgo 的这个bug。代替 args ,使用一个包装器将 yz 捕捉在闭包中的方式来使用 fobj 。一个简单的方法是使用 lambda 表达式:将 shgo 的第一个参数从 func=fobj 改为 func=lambda x, y=y, z=z: fobj(x, y, z),并从调用中删除 args 参数。


这个包装器方法可行!谢谢。我想这是唯一可行的方法,直到这个错误被修复。@Bob在他的回答评论中提到他提交了一个PR,所以如果/当它被合并(我有印象几个月前有人提出了同样的修复方案,但没有得到良好的反应),我使用的语法应该可以工作,对吗? - AB8
不确定这是否是发表此评论的正确位置,但当我运行shgo时,控制台显示bounds in kwarg: [[0.0, 1.0], [1.0, 2.0], [2.0, 3.0], [3.0, 4.0]]这是什么意思? 我打算设置的边界分别为_x1_,_x2_,_x3_和_x4_,分别为[0,2],[1,3],[2,4]和[3,5]。 - AB8

0
问题在于 sum 是一个方法,而不是数组的属性。

如果你把你的目标函数修改为

def fobj(x, y, z):
    return (x+y+z).sum()

此外,开发版本存在错误(即将更新)。

该方法似乎也不是很健壮,对于sum(x) = 14,它只有一个解决方案,这可能是一个问题,因为有效的搜索空间是0维的,但即使我将其放宽到像sum(x) = 12这样的条件,该方法也无法给出解决方案。

这里有一个更完整的工作示例。

import numpy as np
import scipy.optimize as opt
def fobj(x, y, z):
    return (x+y+z).sum()

x0 = np.array([0.5, 0.5, 0.5, 0.5])
y = np.array([1, 3, 5, 7])
z = np.array([10, 20, 30, 40])
bnds = list(zip([0, 1, 2, 3], [3,4,5,6]))
cons = {'type': 'eq', 'fun': lambda x: x.sum() - 9}
min_kwargs = {'method': 'SLSQP', 'options': {'maxiter': 10000, 'disp': True}}
ret = opt.shgo(func=lambda x: fobj(x, y, z), bounds=bnds, args=(), constraints=cons, 
               minimizer_kwargs=min_kwargs, options={'disp': True})

嗨Bob,谢谢回复。你能请进一步澄清吗?我尝试按照您建议的方法(用.sum()替换.sum)来更正目标函数fobj,但是我仍然遇到相同的错误(TypeError: fobj() takes 3 positional arguments but 5 were given)。对您有效吗?您是否对我发布的代码进行了其他更改? - AB8
提交了一个修复 https://github.com/scipy/scipy/pull/16506 - Bob
顺便提一下,当_x_处于其下限(即_x_ =(0,1,2,3))时,在_x_的边界内最小化目标函数,此时_f(x,y,z)_ = 122。请注意,在该点上_x_元素的总和为6。现在,如果我们按照Bob的建议重新编写约束条件(x_元素的总和= 9),则shgo给出的解决方案是_x =(0.75,1.75,2.75,3.75)和_f_ = 125,但我们知道这是错误的!如果我们将约束条件重写为_x_元素的总和为6(应该如此),那么shgo将返回预期的解决方案(0,1,2,3)和_f_ = 122。为什么会这样? - AB8
你是否有使用shgo而不是其他可用的全局优化方法的充分理由? - Bob
我最初使用了 basinhopping,但并不完全满意,因为它返回的解决方案违反了实际问题的边界和约束条件(尽管我在这里使用了自定义的 take_stepaccept_step 类)。是否有其他的 SciPy 全局优化器可以允许边界和约束条件? - AB8
所以你也在basinhopping上发现了错误?你试过在https://scicomp.stackexchange.com/上发布吗?也许有一些数学处理方法可以使数值方法得到改进。根据你的限制,将问题转换为使用Lagrange乘数的无约束优化可能是值得的。但这些领域超出了stackoverflow的范围。 - Bob

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接