如何在最小化使用scipy时设置边界

时间:2018-06-09 11:55:58

标签: python numpy optimization scipy

我在numpy数组中有一些data

我想根据以下规则使用线性函数来缩放数据:

  1. 平均值尽可能接近65
  2. 最小值至少为50
  3. 对于我的第一次尝试,我做了一个评分功能:

    import numpy as np
    from scipy.optimize import minimize
    def score(x):
        return abs(np.mean(x[0]*data+x[1]) - 65) + abs(x[0]*np.min(data)+x[1] - 50)
    

    我在abs(x[0]*np.min(data)+x[1] - 50)上添加了徒劳的尝试,以使其满足规则2。

    然后我尝试了:

    x0 = [0.85,0]
    res = minimize(score,x0)
    np.set_printoptions(suppress=True)
    print res
    

    这给出了:

    fun: 4.8516444911893615
    hess_inv: array([[ 0.0047, -0.1532],
           [-0.1532,  5.2375]])
          jac: array([-50.9628,  -2.    ])
    message: 'Desired error not necessarily achieved due to precision loss.'
         nfev: 580
          nit: 2
         njev: 142
       status: 2
      success: False
            x: array([0.7408, 1.4407])
    

    换句话说,优化失败了。

    我还想设置系数的界限,例如: bounds = [(0.7,1.3),(-5,5)]

    我的问题是,使用缩放最小值至少为50的边界条件运行优化的正确方法是什么?另外,我怎样才能使优化运行而不失败?

2 个答案:

答案 0 :(得分:2)

请考虑以下事项:

import numpy as np
from scipy.optimize import minimize

data = np.array([ 59. ,  59.5,  61. ,  61.5,  62.5,  63. ,  63. ,  65.5,  66.5,
    67. ,  68. ,  69. ,  69.5,  70.5,  70.5,  70.5,  71. ,  72. ,
    72. ,  73.5,  73.5,  74. ,  75. ,  75.5,  78. ,  79. ,  79. ,
    79. ,  79.5,  80.5,  80.5,  80.5,  80.5,  80.5,  82.5,  82.5,
    82.5,  83. ,  83. ,  83. ,  83. ,  83. ,  83.5,  83.5,  84. ,
    84.5,  84.5,  84.5,  86. ,  86. ,  86. ,  86.5,  86.5,  87.5,
    88. ,  88. ,  88.5,  89. ,  90. ,  90.5,  90.5,  90.5,  91. ,
    91.5,  91.5,  92. ,  92. ,  93. ,  93. ,  93. ,  93.5,  93.5,
    94. ,  94. ,  94. ,  94. ,  94. ,  94. ,  94.5,  94.5,  94.5,
    94.5,  95.5,  95.5,  95.5,  95.5,  95.5,  95.5,  96. ,  96. ,
    96. ,  96.5,  96.5,  96.5,  98. ,  98. ,  98. ,  98. ,  98. ,
    98. ,  98. ,  98. ,  98.5,  98.5,  98.5,  98.5,  98.5, 100. ,
   100. , 100. , 100. ])

def scale(data, coeffs):
    m,b = coeffs
    return (m * data) + b

def score(coeffs):
    scaled = scale(data, coeffs)
    # Penalty components
    p_1 = abs(np.mean(scaled) - 65)
    p_2 = max(0, (50 - np.min(scaled)))
    return p_1 + p_2

res = minimize(score, (0.85, 0.0), method = 'Powell')
#np.set_printoptions(suppress=True)
print(res)

post = scale(data, res.x)

print(np.mean(post))
print(np.min(post))
print(score(res.x))

输出:

   direc: array([[ -3.05475495e-02,   2.62047576e+00],
       [  7.54828106e-07,  -6.47892698e-05]])
     fun: 1.4210854715202004e-14
 message: 'Optimization terminated successfully.'
    nfev: 360
     nit: 8
  status: 0
 success: True
       x: array([  0.55914442,  17.02691959])
print(np.mean(post))  # 65.0
print(np.min(post))   # 50.0164406291
print(score(res.x))   # 1.42108547152e-14

一些事情:

  • 我添加了scale辅助函数来清理代码,因为我在score函数中使用它以及在末尾显示缩放数据。
  • 为了清楚起见,score功能已修复并分为两个单独的处罚(每个要求一个)。它计算一次缩放矢量(并将其称为scaled),然后计算惩罚分量。
  • 注意:由于max调用,此得分函数在min(data)= 50附近有一个奇数非平滑区域。这可能会导致某些优化方法出现问题。
  • 我使用Powell算法,因为我之前使用过它,并且在使用min / max运算符时遇到了类似的问题。维基百科says

      

    该方法可用于计算连续但复杂函数的局部最小值,尤其是没有基础数学定义的函数,因为没有必要采用导数

    更熟悉优化方法的人可能会建议更好的替代方案。

  • (编辑)最后,关于边界条件的问题。通常,当我们谈论边界条件时,我们谈论的是自变量的边界,我们正在优化的向量(这里是coeffsx的元素) - 例如,“x [0]必须小于0“,或”x [1]必须介于0和1之间“ - 不是你想要的。

答案 1 :(得分:2)

很抱歉,如果我理解你错了,但只是根据这两条规则扩展数据是直接的线性代数:

e = np.mean(data)
m = e - np.min(data)

data * (65-50)/m + (65 - e*(65-50)/m)
# i.e. (data-e) * (65-50)/m + 65

这恰好意味着65和最低50。