首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在Python中查找变量的值以最大化函数的返回

在Python中查找变量的值以最大化函数的返回
EN

Stack Overflow用户
提问于 2022-05-10 22:04:26
回答 1查看 876关注 0票数 7

我希望获得与Excel中的Solver函数类似的结果。我一直在阅读Scipy优化,并试图构建一个函数来输出我想要找到的最大值。该公式基于四个不同的变量,参见下面的代码:

代码语言:javascript
复制
import pandas as pd
import numpy as np
from scipy import optimize

cols = {
    'Dividend2': [9390, 7448, 177], 
    'Probability': [341, 376, 452], 
    'EV': [0.53, 0.60, 0.55], 
    'Dividend': [185, 55, 755], 
    'EV2': [123, 139, 544],
}

df = pd.DataFrame(cols)

def myFunc(params):
    """myFunc metric."""
    (ev, bv, vc, dv) = params
    df['Number'] = np.where(df['Dividend2'] <= vc, 1, 0) \
                    + np.where(df['EV2'] <= dv, 1, 0)
    df['Return'] =  np.where(
        df['EV'] <= ev, 0, np.where(
            df['Probability'] >= bv, 0, df['Number'] * df['Dividend'] - (vc + dv)
        )
    )
    return -1 * (df['Return'].sum())

b1 = [(0.2,4), (300,600), (0,1000), (0,1000)]
start = [0.2, 600, 1000, 1000]
result = optimize.minimize(fun=myFunc, bounds=b1, x0=start)
print(result)

因此,当更改变量ev、bv、vc和dv时,我想在df中找到列返回的最大值。我希望它们在ev: 0.2-4,bv: 300-600,vc: 0-1000和dv: 0-1000之间。

当运行我的代码时,函数似乎停止在x0。

EN

回答 1

Stack Overflow用户

发布于 2022-05-17 07:57:08

正如我在评论中提到的,关键问题是np.where(),既不可微,也不连续。因此,您的目标函数违反了scipy.optimize.minimize框架下的大多数(基于派生的)算法的数学假设。

所以,基本上,你有三个选择:

  1. 使用一种无导数算法,并希望得到最好的结果。
  2. np.where()替换为光滑的近似,这样您的目标就会不断地可微性。
  3. 将您的问题重新描述为MIP

由于@CypherX答案追求的是方法1,所以我想集中讨论2,这里的主要思想是近似np.where函数。一种可能的近似是

代码语言:javascript
复制
def smooth_if_then(x):
    eps = 1e-12
    return 0.5 + x/(2*np.sqrt(eps + x*x))

它是连续的和可微的。然后,给定一个np.ndarray arr和一个标量值x,表达式np.where(arr <= x, 1, 0)等效于smooth_if_then(x - arr)

因此,目标函数成为:

代码语言:javascript
复制
div = df['Dividend'].values
div2 = df['Dividend2'].values
ev2 = df['EV2'].values
ev = df['EV'].values
prob = df['Probability'].values

def objective(x, *params):
    ev, bv, vc, dv = x
    div_vals, div2_vals, ev2_vals, ev_vals, prob_vals = params
    number = smooth_if_then(vc - div2_vals) + smooth_if_then(dv - ev2_vals)
    part1 = smooth_if_then(bv - prob_vals) * (number * div_vals - (vc + dv))
    part2 = smooth_if_then(-1*(ev - ev_vals)) * part1
    return -1 * part2.sum()

使用trust-constr算法(这是scipy.optimize.minimize内部最健壮的算法),将产生以下结果:

代码语言:javascript
复制
res = minimize(lambda x: objective(x, div, div2, ev2, ev, prob), x0=start, bounds=b1, method="trust-constr")
代码语言:javascript
复制
 barrier_parameter: 1.0240000000000006e-08
 barrier_tolerance: 1.0240000000000006e-08
          cg_niter: 5
      cg_stop_cond: 0
            constr: [array([8.54635975e-01, 5.99253512e+02, 9.95614973e+02, 9.95614973e+02])]
       constr_nfev: [0]
       constr_nhev: [0]
       constr_njev: [0]
    constr_penalty: 1.0
  constr_violation: 0.0
    execution_time: 0.2951819896697998
               fun: 1.3046631387761482e-08
              grad: array([0.00000000e+00, 0.00000000e+00, 8.92175218e-12, 8.92175218e-12])
               jac: [<4x4 sparse matrix of type '<class 'numpy.float64'>'
    with 4 stored elements in Compressed Sparse Row format>]
   lagrangian_grad: array([-3.60651033e-09,  4.89643010e-09,  2.21847918e-09,  2.21847918e-09])
           message: '`gtol` termination condition is satisfied.'
            method: 'tr_interior_point'
              nfev: 20
              nhev: 0
               nit: 14
             niter: 14
              njev: 4
        optimality: 4.896430096425101e-09
            status: 1
           success: True
         tr_radius: 478515625.0
                 v: [array([-3.60651033e-09,  4.89643010e-09,  2.20955743e-09,  2.20955743e-09])]
                 x: array([8.54635975e-01, 5.99253512e+02, 9.95614973e+02, 9.95614973e+02])

最后但并非最不重要的一点是:使用光滑逼近是实现可微性的常见方法。但是,值得一提的是,这些近似不是凸的。在实践中,这意味着您的优化问题不是凸的,因此,您无法保证找到的平稳点(局部极小值)是全局最优的。为此,需要使用全局优化算法或将问题描述为MIP。从数学和实践的角度来看,后者都是推荐的方法。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72193393

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档