【问题标题】:Scipy optimize minimize always returns initial guess (SLSQP)Scipy 优化最小化总是返回初始猜测(SLSQP)
【发布时间】:2019-12-16 14:46:57
【问题描述】:

正如标题所解释的,我的程序总是返回最初的猜测。

就上下文而言,该程序正在尝试找到在多个商店之间分配某些产品的最佳方式。每家商店都有他们预计在接下来几天内销售的商品的预测 (sales_data)。这个预测不一定必须是整数,或者大于 1(很少是这样),它是统计意义上的预期。因此,如果一家商店的 sales_data = [0.33, 0.33, 0.33] ,则预计 3 天后,他们将售出 1 件产品。

我想尽量减少出售我分配的单位所需的总时间(我想以最快的速度出售它们),我的限制是我必须分配我可用的单位,我不能分配负数商店的产品数量。我现在可以进行非整数分配。对于我的初始分配,我在所有商店中平均分配我可用的单位。

以下是我遇到问题的代码的较短版本:

import numpy, random
from scipy.optimize import curve_fit, minimize

unitsAvailable = 50
days = 15

class Store:

    def __init__(self, num):
        self.num = num

        self.sales_data = []


stores = []
for i in range(10):
    # Identifier
    stores.append(Store(random.randint(1000, 9999)))
    # Expected units to be sold that day (It's unlikey they will sell 1 every day)
    stores[i].sales_data = [random.randint(0, 100) / 100 for i in range(days)]
    print(stores[i].sales_data)


def days_to_turn(alloc, store):
    day = 0

    inventory = alloc
    while (inventory > 0 and day < days):
        inventory -= store.sales_data[day]
        day += 1
    return day

def time_objective(allocations):
    time = 0
    for i in range(len(stores)):
        time += days_to_turn(allocations[i], stores[i])
    return time

def constraint1(allocations):

    return unitsAvailable - sum(allocations)

def constraint2(allocations):

    return min(allocations) - 1

cons = [{'type':'eq', 'fun':constraint1}, {'type':'ineq', 'fun':constraint2}]
guess_allocs = []

for i in range(len(stores)):
    guess_allocs.append(unitsAvailable / len(stores))

guess_allocs = numpy.array(guess_allocs)

print('Optimizing...')

time_solution = minimize(time_objective, guess_allocs, method='SLSQP', constraints=cons, options={'disp':True, 'maxiter': 500})

time_allocationsOpt = [max([a, 0]) for a in time_solution.x]

unitsUsedOpt = sum(time_allocationsOpt)
unitsDaysProjected = time_solution.fun

for i in range(len(stores)):
    print("----------------------------------")
    print("Units to send to Store %s: %s" % (stores[i].num, time_allocationsOpt[i]))
    print("Time to turn allocated: %d" % (days_to_turn(time_allocationsOpt[i], stores[i])))

print("----------------------------------")
print("Estimated days to be sold: " + str(unitsDaysProjected))
print("----------------------------------")
print("Total units sent: " + str(unitsUsedOpt))
print("----------------------------------")

优化成功完成,只进行了 1 次迭代,无论我如何更改参数,它总是返回初始的guess_allocs。

有什么建议吗?

【问题讨论】:

  • 你的目标函数是离散值的。这是一个问题,因为它的梯度为零,但优化器需要梯度来改进解决方案。尝试不依赖于梯度的优化方法(例如 nelder-mead、模拟退火或微分进化)。
  • @kazemakase 感谢您的回复!离散值是什么意思?每个分配都允许是非整数。或者你的意思是目标函数的输入是一个包含多个值的数组,而对于 SLSQP,它应该只是 1?此外,那些其他方法是否允许约束以确保我没有分配负数的单位并且我正在使用所有可用的单位?
  • 我指的是目标函数的输出,它返回离散的天数。

标签: python numpy scipy scipy-optimize scipy-optimize-minimize


【解决方案1】:

目标函数没有梯度,因为它返回离散的天数。这很容易可视化:

import numpy as np
import matplotlib.pyplot as plt

y = []
x = np.linspace(-4, 4, 1000)
for i in x:
    a = guess_allocs + [i, -i, 0, 0, 0, 0, 0, 0, 0, 0]    
    y.append(time_objective(a))

plt.plot(x, y)
plt.xlabel('relative allocation')
plt.ylabel('objective')
plt.show()

如果要优化这样的功能,则无法使用基于梯度的优化器。有两个选项:1)找到一种方法来使目标函数可微不足道。 2)使用不同的优化器。第一个很难。第二,让我们试试dual annealing。不幸的是,它不允许约束,所以我们需要修改目标函数。

将 n em>数字与恒定和相同,与具有 n-1 em>不约束的数字,并将 n em> th nange设置为常数 - 总和。

import scipy.optimize as spo

bounds = [(0, unitsAvailable)] * (len(stores) - 1)

def constrained_objective(partial_allocs):
    if np.sum(partial_allocs) > unitsAvailable:
        # can't sell more than is available, so make the objective infeasible
        return np.inf
    # Partial_alloc contains allocations to all but one store.
    # The final store gets allocated the remaining units.
    allocs = np.append(partial_allocs, unitsAvailable - np.sum(partial_allocs))
    return time_objective(allocs)

time_solution = spo.dual_annealing(constrained_objective, bounds, x0=guess_allocs[:-1])
print(time_solution)

这是一种随机优化方法。您可能想多次运行它以查看它是否可以做得更好,或者使用可选参数...

最后,我认为目标函数存在问题:

for i in range(len(stores)):
    time += days_to_turn(allocations[i], stores[i])

这表明商店不同时销售,而只有一个接一个地销售。每家商店是否等待销售,直到以前的商店耗尽物品?我想不是。相反,它们将同时销售,所有单位销售所需的时间是商店花费时间最长的时间。试试这个:

for i in range(len(stores)):
    time = max(time, days_to_turn(allocations[i], stores[i]))

【讨论】:

  • 哇,这是一个很好的答案谢谢!但我正在进行一个问题。当我运行这个时它仍然给了我错误的分配(它返回猜测_Allocs),并且来自解决方案的消息表示它达到了最大迭代限制。当我增加限制时,我收到一条错误消息,说“停止算法,因为函数即使在尝试新的随机参数时也会创建naN或(+/-)无限值”。有什么想法吗?
  • @marco 我猜这是因为优化器尝试或多或少的随机猜测。如果违反约束,我们将更改inf 987654328的目标函数。如果所有启动猜测都违反了约束,优化器认为没有解决方案。我第一次尝试differential_evolution 时遇到了这个问题,但dual_annealing 在我的设置中工作得非常可靠......
猜你喜欢
  • 2015-06-09
  • 1970-01-01
  • 1970-01-01
  • 2021-01-29
  • 1970-01-01
  • 1970-01-01
  • 2019-07-02
  • 1970-01-01
  • 2020-11-26
相关资源
最近更新 更多