从 scipy optimization 运行最小化函数时,我得到了一些非常奇怪的结果。
这是代码
from scipy.optimize import minimize
def objective(x):
return - (0.05 * x[0] ** 0.64 + 0.4 * x[1] ** 0.36)
def constraint(x):
return x[0] + x[1] - 5000
cons = [{'type':'eq', 'fun': constraint}]
跑步时
minimize(objective, [2500.0, 2500.0], method='SLSQP', constraints=cons)
我为 x 的每个元素分配
2500
。
与 fun: -14.164036415985395
快速检查一下,此分配
[3800, 1200]
给出 -14.9
它对初始条件也高度敏感。
关于我做错了什么的任何想法
两个函数绘制
更新 它实际上返回初始条件。
如果我尝试这个
def objective(x):
return - (x[0] ** 0.64 + x[1] ** 0.36)
def constraint(x):
return x[0] + x[1] - 5000.0
cons = [{'type':'eq', 'fun': constraint}]
minimize(objective, [2500, 2500], method='SLSQP', constraints=cons)
返回的结果似乎很好(我改变了目标函数)
这有一些赞成票,所以我认为它值得一个答案。
关于我做错了什么的任何想法
没什么,真的。这只是问题缩放和默认容差的问题。
method='slsqp'
的一个重要的默认终止容差是 1e-6
(_minimize_slsqp
)。将其设置为 1e-7
:
minimize(objective, [2500.0, 2500.0], method='SLSQP', constraints=cons, tol=1e-7)
您得到了您期望的解决方案:
message: Optimization terminated successfully
success: True
status: 0
fun: -14.913839470893286
x: [ 3.901e+03 1.099e+03]
nit: 15
jac: [-1.630e-03 -1.630e-03]
nfev: 45
njev: 15
请注意,终止时,
x
的顺序为 1e3
,而 jac
的顺序为 1e-3
。虽然细节无疑更加复杂,但我不认为6个数量级的规模差异是巧合。如果我们通过在目标和约束中进行变量替换x = x * scale
来重新调整您的问题:
import numpy as np
from scipy.optimize import minimize
scale = 1.5 # it doesn't take much
def objective(x):
x = x*scale
return - (0.05 * x[0] ** 0.64 + 0.4 * x[1] ** 0.36)
def constraint(x):
x = x*scale
return x[0] + x[1] - 5000
cons = [{'type':'eq', 'fun': constraint}]
x0 = np.asarray([2500.0, 2500.0])
minimize(objective, x0/scale, method='SLSQP', constraints=cons)
然后,我们不更改目标或约束的初始值,但求解器会继续前进并最终收敛到预期答案,即使不更改终止容差。
您会发现,如果将
scale
更改为 10,则最终的 x
和 jac
会更接近两个数量级,并且您可以将 tol
增加到 1e-5
并仍然收敛到所需的值解决方案,但对于 tol=1e-4
则不然。更一般地,如果您设置 tol = 1e-7 * scale**2
,它会收敛到所需的解决方案,但使用 tol = 1e-6 * scale**2
,则不会(对于 scale
在 1
几个数量级内)。