【问题标题】:scipy optimize minimize not finding optimal solutionscipy优化最小化没有找到最佳解决方案
【发布时间】:2018-04-06 18:28:54
【问题描述】:

我有一个受约束的优化问题,我有很多 我想花钱购买的产品并根据模型估算我的总回报 我为每个单独的产品构建。

我正在使用 scipy.optimzie.minimize 来找到给定的最佳支出 个别模型输出。我遇到的问题是优化器 以“优化器成功终止”标志结束,但非常清楚 没有找到最优解。其实使用原版的输出 seed/x0 比优化器产生的输出要好。我放了一张印刷品 目标函数中的声明,你可以看到它在某一时刻刚刚下降 悬崖。有谁知道为什么会发生这种情况/如何解决它?

我在下面包含了我的代码的简化版本。

如果我的产品是 ['P1','P2',... 'P9'],并且我有每个产品的型号

 # estimates returns on spend for each product
  model1 = lambda money : func1(money *betas_a)
  model2 = lambda money : func2(money,*betas_b)
  ... etc

其中 func 是其中之一

def power_curve(x,beta1,beta2):
    return beta1*x**beta2

def mm_curve(x,beta1,beta2):
    "Beta2 >= 0"
    return (beta1*x)/(1+beta2*x)

def dbl_exponential(x,beta1,beta2,beta3,beta4):
    return beta1**(beta2**(x/beta4))*beta3

def neg_exp(x,beta1,beta2):
    "Beta2 > 0"
    return beta1*(1-np.exp(-beta2*x))

我现在想优化我在每个方面的支出,以最大限度地提高我的总回报。 为此,请使用 scipy.optimize.minimize 和以下函数的包装器:

def budget(products, budget, betas, models):
  """
  Given a budget distributed across each product, estimate total returns.

  products = str: names of each individual product
  budget = list-floats: amount of money/spend corresponding to each product
  models = list-funcs: function to use to predict individual returns corresponding to each product
  betas = dict: keys are product names - values are list of betas to feed to corresponding model
  """

  results = []
  target_total = 0 # total returns

  assert len(products) == len(budget) == len(betas)

  # for each product, calculate return using corresponding model
  for v,b,m in zip(products,budget,models):
      tpred = m(b,*betas[v])
      target_total+=tpred
      results.append({'val':v,'budget':b, 'tpred':tpred})
  # if you watch this you can see it drops off dramatically towards the end
  print(target_total)
  return results, target_total

最小可重现代码:

import numpy as np
from scipy import optimize

### Setup inputs to the optimizer

vals = ['P1','P2','P3','P4','P5','P6','P7','P8','P9']

funcs = [dbl_exponential,
      mm_curve,
      power_curve,
      mm_curve,
      neg_exp,
      power_curve,
      power_curve,
      mm_curve,
      dbl_exponential]

betas = {'P1': [0.018631215601097723,0.6881958654622713,43.84956270498627,
            1002.1010110475437],
      'P2': [0.002871159199956573, 1.1388317502737174e-06],
      'P3': [0.06863672099961649, 0.7295132426289046],
      'P4': [0.009954885796211378, 3.857169894090025e-05],
      'P5': [307.624705578708, 1.4454030580404746e-05],
      'P6': [0.0875910297422766, 0.6848303282418671],
      'P7': [0.12147343508583974, 0.6573539731442877],
      'P8': [0.002789390181221983, 5.72554293489956e-07],
      'P9': [0.02826834133593836,0.8999750236756555,1494.677373273538,
            6529.1531372261725]
}

bounds = [(4953.474502264151, 14860.423506792453),
       (48189.39647820754, 144568.18943462262),
       (10243.922611886792, 30731.767835660376),
       (6904.288592358491, 20712.865777075473),
       (23440.199762641503, 70320.5992879245),
       (44043.909679905664, 132131.729039717),
       (9428.298255754717, 28284.89476726415),
       (53644.56626556605, 160933.69879669815),
       (8205.906018773589, 24617.718056320766)]

seed = [9906.949005,
     96378.792956,
     20487.845224,
     13808.577185,
     46880.399525,
     88087.81936,
     18856.596512,
     107289.132531,
     16411.812038]

wrapper = lambda b: -budget(vals,b,betas, funcs)[1] # negative to get *maximum* output

## Optimizer Settings
tol = 1e-16
maxiter = 10000
max_budget = 400000
# total spend can't exceed max budget
constraint = [{'type':'ineq', 'fun': lambda budget: max_budget-sum(budget)}]

# The output from the seed is better than the final "optimized" output
print('DEFAULT OUTPUT TOTAL:', wrapper(seed))

res = optimize.minimize(wrapper, seed, bounds = bounds,
                tol = tol, constraints = (constraint),
                options={'maxiter':maxiter})

print("Optimizer Func Output:", res.fun)

【问题讨论】:

  • 初始 x0 更好是什么意思?在这里:DEFAULT OUTPUT TOTAL: -1296.67406505 Optimizer Func Output: -1318.2428505469134。所以得到的解优于x0。对我来说,进度看起来也很顺利(我使用回调来不打印 num-diff 迭代)。
  • 有趣 - 你复制粘贴了这个例子吗?我说Optimizer Func Output: -1034.0922073038896。如果您观察输出迭代(在 print 语句中),您可以看到它在最近的 20 次迭代中从 -1300ish 下降到 -1000
  • (我知道在 this example 中减少容差可以解决问题,但我的完整数据集的下降发生得更早)
  • 不适合我。 Python 3,scipy 0.19。此外,我认为 SLSQPs 代码(可能已使用)没有太大变化。是的,复制粘贴的最后一个块;看到我需要其他人;按出现顺序添加。再次:这里一切顺利,没有下降。 (并使用回调;num-diff 函数评估主要是噪音,除了寻找错误!)使用回调(只看到每个它的可行点),我也不认为目标增长是可能的(单调线搜索)(但我现在懒得检查数学)。
  • 太棒了——我的电脑一定很讨厌我(每个版本都一样)。将测试同事的薪酬

标签: python optimization scipy minimize


【解决方案1】:

与我的大多数问题一样,事实证明这很愚蠢。我传递的种子值的总和大于我给它的 max_budget 约束。因此 x0 与我的约束不兼容。为什么 scipy 没有产生相应的警告或错误,我不确定。但事实证明这是问题所在。

【讨论】:

    【解决方案2】:

    您可能会陷入局部最小值,因为您的函数似乎非常非线性。您是否尝试过将优化方法更改为 BFGS 之类的方法。

    res = optimize.minimize(wrapper, seed, bounds = bounds, method='BFGS',
                            tol = tol, constraints = (constraint),
                            options={'maxiter':maxiter})
    

    我尝试使用此算法解决问题,得到 -78464.52052455483。

    希望对你有帮助

    【讨论】:

    • 不幸的是,BFGS 不支持约束,因此为什么输出如此之高
    猜你喜欢
    • 2022-01-15
    • 2021-09-18
    • 2011-12-17
    • 1970-01-01
    • 1970-01-01
    • 2018-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多