【问题标题】:SciPy's minimize is not iterating at allSciPy 的最小化根本没有迭代
【发布时间】:2019-09-22 08:30:49
【问题描述】:

我正在尝试最小化一个基本上看起来像这样的函数:

实际上它有两个自变量,但由于 x1 + x2 = 1,它们并不是真正独立的。

现在是目标函数

def calculatePVar(w,covM):
    w = np.matrix(w)
    return (w*covM*w.T) [0,0]

wnere w 是每个资产的权重列表,covM 是 pandas 的 .cov() 返回的协方差矩阵

这里是调用优化函数的地方:

w0 = []
for sec in portList:
    w0.append(1/len(portList))

bnds = tuple((0,1)  for x in w0)
cons = ({'type': 'eq', 'fun': lambda x:  np.sum(x)-1.0})
res= minimize(calculatePVar, w0, args=nCov, method='SLSQP',constraints=cons, bounds=bnds)
weights = res.x

现在函数有一个明确的最小值,但最小化只会吐出初始值作为结果,它确实说“优化成功终止”。有什么建议吗?

优化结果:

附:图片作为链接,因为我不符合要求!

【问题讨论】:

  • 您可能想尝试不同的方法,例如 ‘Nelder-Mead’。此外,您需要明确,您的变量是什么?它们是权重w_i 还是x_i。此外,您还需要区分 * 乘法和点乘法,并确保哪一种能满足您的需求。
  • 我很难找到其他接受约束的方法,我相信我得到了 Nelder-Mead 的错误。关于变量,权重(calculatePVar 函数中的 w0 和 w)是我的变量。至于乘法,我不确定有什么区别?发生的是矩阵乘法,但 * 正在解决问题(数学正在检查)
  • 你能提供portListnCov的例子吗?
  • portList 只是一个字符串列表,但这里有一个例子:portList = ['ABEV3', 'BBDC4'],这里是 dfCov(必须先转换为 numpy 矩阵):covariance matrix

标签: python scipy scipy-optimize


【解决方案1】:

您的代码只有一些令人困惑的变量,所以我只是将其清除并简化了一些行,现在最小化可以正常工作。然而,现在的问题是:结果是否正确?它们有意义吗?那是你来判断的:

import numpy as np 
from scipy.optimize import minimize

def f(w, cov_matrix):
    return (np.matrix(w) * cov_matrix * np.matrix(w).T)[0,0]

cov_matrix = np.array([[1, 2, 3],
                       [4, 5, 6],
                       [7, 8, 9]])
p    = [1, 2, 3]
w0   = [(1/len(p))  for e in p]
bnds = tuple((0,1)  for e in w0)
cons = ({'type': 'eq', 'fun': lambda w:  np.sum(w)-1.0})

res  = minimize(f, w0, 
                args        = cov_matrix, 
                method      = 'SLSQP',
                constraints = cons, 
                bounds      = bnds)
weights = res.x
print(res)
print(weights)

更新:

根据您的 cmets,在我看来-也许-您的函数有多个最小值,这就是 scipy.optimize.minimize 被困在那里的原因。我建议scipy.optimize.basinhopping 作为替代方案,这将使用随机步骤来遍历您的函数的大部分最小值,并且它仍然会很快。代码如下:

import numpy as np 
from scipy.optimize import basinhopping


class MyBounds(object):
     def __init__(self, xmax=[1,1], xmin=[0,0] ):
         self.xmax = np.array(xmax)
         self.xmin = np.array(xmin)

     def __call__(self, **kwargs):
         x = kwargs["x_new"]
         tmax = bool(np.all(x <= self.xmax))
         tmin = bool(np.all(x >= self.xmin))
         return tmax and tmin

def f(w):
    global cov_matrix
    return (np.matrix(w) * cov_matrix * np.matrix(w).T)[0,0]

cov_matrix = np.array([[0.000244181, 0.000198035],
                       [0.000198035, 0.000545958]])

p    = ['ABEV3', 'BBDC4']
w0   = [(1/len(p))  for e in p]
bnds = tuple((0,1)  for e in w0)
cons = ({'type': 'eq', 'fun': lambda w:  np.sum(w)-1.0})

bnds = MyBounds()
minimizer_kwargs = {"method":"SLSQP", "constraints": cons}
res  = basinhopping(f, w0, 
                    accept_test  = bnds)
weights = res.x
print(res)
print("weights: ", weights)

输出:

                        fun: 2.3907094432990195e-09
 lowest_optimization_result:       fun: 2.3907094432990195e-09
 hess_inv: array([[ 2699.43934183, -1184.79396719],
       [-1184.79396719,  1210.50404805]])
      jac: array([1.34548553e-06, 2.00122166e-06])
  message: 'Optimization terminated successfully.'
     nfev: 60
      nit: 6
     njev: 15
   status: 0
  success: True
        x: array([0.00179748, 0.00118076])
                    message: ['requested number of basinhopping iterations completed successfully']
      minimization_failures: 0
                       nfev: 6104
                        nit: 100
                       njev: 1526
                          x: array([0.00179748, 0.00118076])
weights:  [0.00179748 0.00118076]

【讨论】:

  • 我实际上认为它运行的事实更多地与协方差矩阵有关,而不是代码本身。如果我输入这些值,两次迭代的权重会得到 [1.0, 0.0, 0.0] 的结果,这就是你的结果吗?
  • [1.0, 0.0, 0.0],是的。我希望我的更新对您有所帮助,但是是的,您的函数可能与使用的求解器不兼容。
  • 谢谢!我会调查它,但从你放在那里的结果来看,np.sum(w) - 1 = 0 的约束似乎没有传递到优化器中。是否会像更改对res = basinhopping(f, w0, accept_test = bnds, minimizer_kwargs = minimizer_kwargs) 的调用一样简单?此外,该边界类适用于任何大小的 w,对吗?
  • 回答我自己的问题,是的,我只需要添加 minimizer_kwargs = minimizer_kwargs,但我确实必须显着更改 MyBounds 类:class MyBounds(object): def __call__(self, **kwargs): x = kwargs["x_new"] xmax = np.ones(len(x)) xmin = np.zeros(len(x)) @ 987654334@tmin = bool(np.all(x &gt;= xmin))return tmax and tmin
  • 很高兴它有帮助 ;)
【解决方案2】:

我有一个类似的问题,问题结果是函数和约束是用单个元素输出numpy阵列。改变这两个函数的输出解决问题。

对令人困惑的问题的一个非常简单的解决方案。

【讨论】:

    猜你喜欢
    • 2022-10-16
    • 2022-06-23
    • 1970-01-01
    • 2022-01-15
    • 2018-04-06
    • 1970-01-01
    • 2013-12-03
    • 2017-04-11
    • 1970-01-01
    相关资源
    最近更新 更多