如何在scipy.optimize.minimize()中设置容差?

时间:2014-06-03 01:16:17

标签: python scipy mathematical-optimization scientific-computing

我正在使用scipy.optimize.minimize来解决有效的投资组合。

使用默认设置时,我经常遇到“BaseException:linesearch的正方向导数”错误,当使用某些输入时。我注意到如果我将公差设置得足够高,问题会变得更少,但不会消失。有什么建议吗?

import numpy as np
import pandas as pd
import scipy.optimize


def fx(TOLERANCE):
    #TOLERANCE = 1.5

    def solve_weights(R, C, rf, b_):
        def port_mean_var(W,R,C):
            return sum(R*W), np.dot(np.dot(W, C), W)
        def fitness(W, R, C, rf):
            mean, var = port_mean_var(W, R, C)    # calculate mean/variance of the portfolio
            util = (mean - rf) / np.sqrt(var)        # utility = Sharpe ratio
            return 1/util                        # maximize the utility, minimize its inverse value
        n = len(R)
        W = np.ones([n])/n                        # start optimization with equal weights
        #b_ = [(0.,1.) for i in range(n)]    # weights for boundaries between 0%..100%. No leverage, no shorting
        c_ = ({'type':'eq', 'fun': lambda W: sum(W)-1. })    # Sum of weights must be 100%
        optimized = scipy.optimize.minimize(fitness, W, (R, C, rf), 
                                            method='SLSQP', constraints=c_, 
                                            bounds=b_, tol=TOLERANCE)    
        if not optimized.success: 
            raise BaseException(optimized.message)
        return optimized.x

    def mean_var_opt2(ret_df, upper_bounds=None):
        R = (ret_df.mean(0)*252).values
        C = (ret_df.cov()*252).values
        rf = 0.0
        if upper_bounds == None:
            upper_bounds = pd.Series(1.0,index=ret_df.columns)
        b_ = [(0.0,float(num)) for num in upper_bounds]
        wgts = solve_weights(R, C, rf, b_)
        return pd.Series(wgts, index=ret_df.columns)


    np.random.seed(45)
    rets = []
    for i in range(10000):
        rets.append(pd.DataFrame(np.random.randn(100,4)/100.0))

    try:
        for i in range(10000):
            mean_var_opt2(rets[i])
    except BaseException as e:
        print e
    finally: print "Tolerance: %s, iter: %s" % (TOLERANCE,i)

for k in [0.001, 0.01, 0.025, 0.05, 0.1, 0.5, 1.0, 5.0, 50.0, 500.0]:
    fx(k)


Positive directional derivative for linesearch
Tolerance: 0.001, iter: 0
Positive directional derivative for linesearch
Tolerance: 0.01, iter: 30
Positive directional derivative for linesearch
Tolerance: 0.025, iter: 77
Inequality constraints incompatible
Tolerance: 0.05, iter: 212
Positive directional derivative for linesearch
Tolerance: 0.1, iter: 444
Positive directional derivative for linesearch
Tolerance: 0.5, iter: 444
Positive directional derivative for linesearch
Tolerance: 1.0, iter: 1026
Positive directional derivative for linesearch
Tolerance: 5.0, iter: 1026
Positive directional derivative for linesearch
Tolerance: 50.0, iter: 1026
Positive directional derivative for linesearch
Tolerance: 500.0, iter: 1026

3 个答案:

答案 0 :(得分:1)

调整容差直到它不崩溃是一个非常弱的解决方案,对您的数据或功能进行小的更改,您一定会崩溃。请参阅here您的错误消息的含义。

我的猜测是约束不能很好地发挥作用。求解器在平滑函数上工作得最好,而你的约束可能会使问题变得非常困难。我可以想出两种解决方法:

  • 在问题定义中包含约束:算法尝试拟合W的前N-1值,然后从中计算最后一个值。通过这种方法,您可能会发现自己处于负权重状态。
  • 在目标函数中添加约束。删除约束,获取权重,对它们进行规范化,使它们相加为1,然后添加到返回值(W.sum() - 1)**2。这种行为在W中是抛物线的,这就是解算器最好的效果。在最终结果中,权重可能不完全相加,但是当你在内部使用它们进行标准化时,它应该不是问题。

另一种方法是减少自由参数的数量。如果W[17]具有与W[18]类似的含义,则可以自然地期望它们的值相似。然后,您可以使用平滑函数计算它们,例如抛物线,或sin /余弦系列中的一些项。这允许您引入位置信息并减少问题的维数。

答案 1 :(得分:0)

SLSQP要求成本函数必须是两次可导的,如果不满足该条件,则可能会失败。除非容差增加过大,否则您将始终获得初始解决方案,这对您没有任何帮助。

您可能想尝试使用COBYLA来代替-但是请注意,这会要求您将边界转换为约束,因为COBYLA不接受单独的bounds参数。

答案 2 :(得分:0)

我提供的答案很晚,因为似乎该示例的源代码仍在网络here上。

要像函数fitness()中那样使Sharpe比率最大化,则不必使它的倒数最小。您可以简单地减少负面影响。

因此,如果我在该函数中将return 1/util替换为return -util并运行发问者的代码-我这样做了---我发现任何10,000迭代中的任何一个都没有错误TOLERANCE