【问题标题】:What is the fastest way to minimize a function in python?在python中最小化函数的最快方法是什么?
【发布时间】:2017-04-28 07:24:35
【问题描述】:

所以我有以下问题需要最小化。我有一个向量w,我需要找到它以最小化以下函数:

import numpy as np
from scipy.optimize import minimize

matrix = np.array([[1.0, 1.5, -2.],
                   [0.5, 3.0, 2.5],
                   [1.0, 0.25, 0.75]])

def fct(x):
    return x.dot(matrix).dot(x)

x0 = np.ones(3) / 3
cons = ({'type': 'eq', 'fun': lambda x: x.sum() - 1.0})
bnds = [(0, 1)] * 3

w = minimize(fct, x0, method='SLSQP', bounds=bnds, constraints=cons)['x']

我选择了method='SLSQP',因为它似乎是唯一允许boundsconstraints 的。我的问题是我将不得不在多个选择中循环我的解决方案,所以我试图在这里获得一些速度。我的解决方案是使用优化器最快的解决方案,还是有其他更快的解决方案?谢谢。

【问题讨论】:

  • 提供雅可比和粗麻布的某些方法可以显着提高性能。看看scipy lectures
  • 我测试并添加了 jacobian(顺便感谢链接,我不知道有这样的东西存在),我将优化时间减少了 35%。我会阅读更多内容,看看我可以做些什么来改进。非常感谢
  • 如果您得到解决方案,请发布答案。我很好奇结果如何。
  • 好的,刚做了。我会继续寻找,看看我是否可以改进并在需要时进行编辑。非常感谢您的宝贵时间,我得到了更好的解决方案,并且对优化方法有了更深入的了解。
  • 请记住,对于一般的可能不定矩阵,这个问题(可能)是非凸的,并且 scipy 中的每个求解器都会返回一些局部最优值(对全局最优值一无所知) )。这也对不同方法的基准测试产生影响,因为结果可能不同。如果矩阵是 pos-def 或 neg-dev,它会改变(然后它看起来像一个凸优化问题,并且有比 SLSQP 更快的方法)。

标签: python scipy


【解决方案1】:

简介

一般来说,最快的方法总是最适合问题的。

由于 scipy.minimize 中的所有优化算法都非常通用,因此会有 总是更快的方法,从你的问题的特殊特征中获得性能。 这将是一个权衡,需要做多少分析和工作才能获得性能。

需要注意的是,例如 SLSQP 是一种算法,它能够 解决非凸问题,在这种情况下保证收敛到一些局部最优 (忽略实现中的数值问题;这始终是一个可能的问题)。

这种能力是有代价的:与算法相比,SLSQP 的速度和鲁棒性会降低 专门为凸问题设计的(甚至在凸问题中,虽然 它们都是多项式可解的,有更简单的线性规划和更难的 作为半定编程)。

问题分析

如上面的cmets所示,对于一些一般的不定矩阵M,这个问题 是非凸的(很有可能;我没有给出正式的证明),这意味着, 没有进一步的假设就没有一般可行的方法(忽略 特殊分析,因为一些非凸问题可以在多项式时间内全局解决)。

这意味着:

  • scipy 中的每个优化算法,最多只能保证局部最优,这可能 与全局最优相比,任意差

假设:M 是正定/负定

如果我们假设矩阵 M 是正定的或负定的,但不是不定的,这是一个凸优化问题。由于您似乎对这种情况感兴趣,这里有一些评论和方法。

这意味着:

  • SLSQP 保证收敛到全局最优
  • 您可以使用专为凸优化问题设计的求解器
    • 商业求解器:Gurobi、CPLEX、Mosek
    • 开源求解器:ECOS、SCS

使用 Python + cvxpy + ecos/scs 的示例代码

除了用于线性规划的 linprog 之外,没有特殊的凸优化求解器 因此无法解决这个问题。

如上所述,还有其他选择,并且有许多可能的途径 使用它们。

这里我将介绍最简单的一种:

  • cvxpy 用于模型制定
    • 它会自动证明这个问题是凸的!
      • (模型构建和凸性推理的成本可能很高)
  • ecos
    • 用于许多凸优化问题的通用求解器
      • 基于内点法
  • scs
    • 用于许多凸优化问题的通用求解器
      • 基于乘法器交替方向法 (ADMM)
    • 支持两种不同的方法来求解线性方程:
      • 直接(基于分解)
      • 间接(基于共轭梯度)
        • GPU 支持这个,因为它完全是关于矩阵向量产品
    • 与 ECOS 相比,解决方案通常不太准确,但通常要快得多

示例代码:

  • 使用示例:
    • 1000x1000 矩阵
    • 求解器:SCS
      • 间接模式
      • CPU
      • 多线程(如果 BLAS 可用,则自动执行)

代码:

import time
import numpy as np
from cvxpy import *                 # Convex-Opt

""" Create some random pos-def matrix """
N = 1000
matrix_ = np.random.normal(size=(N,N))
matrix = np.dot(matrix_, matrix_.T)

""" CVXPY-based Convex-Opt """
print('\ncvxpy\n')
x = Variable(N)
constraints = [x >= 0, x <= 1, sum(x) == 1]
objective = Minimize(quad_form(x, matrix))
problem = Problem(objective, constraints)
time_start = time.perf_counter()
problem.solve(solver=SCS, use_indirect=True, verbose=True)  # or: solver=ECOS
time_end = time.perf_counter()
print(problem.value)
print('cvxpy (modelling) + ecos/scs (solving) used (secs): ', time_end - time_start)

示例输出:

cvxpy

----------------------------------------------------------------------------
    SCS v1.2.6 - Splitting Conic Solver
    (c) Brendan O'Donoghue, Stanford University, 2012-2016
----------------------------------------------------------------------------
Lin-sys: sparse-indirect, nnz in A = 1003002, CG tol ~ 1/iter^(2.00)
eps = 1.00e-03, alpha = 1.50, max_iters = 2500, normalize = 1, scale = 1.00
Variables n = 1001, constraints m = 3003
Cones:  primal zero / dual free vars: 1
    linear vars: 2000
    soc vars: 1002, soc blks: 1
Setup time: 6.76e-02s
----------------------------------------------------------------------------
 Iter | pri res | dua res | rel gap | pri obj | dua obj | kap/tau | time (s)
----------------------------------------------------------------------------
     0|      inf       inf      -nan      -inf      -inf       inf  1.32e-01 
   100| 1.54e-02  1.48e-04  7.63e-01 -5.31e+00 -4.28e+01  1.10e-11  1.15e+00 
   200| 1.53e-02  1.10e-04  7.61e-01 -3.87e+00 -3.17e+01  1.08e-11  1.95e+00 
   300| 1.53e-02  7.25e-05  7.55e-01 -2.47e+00 -2.08e+01  1.07e-11  2.79e+00 
   400| 1.53e-02  3.61e-05  7.39e-01 -1.11e+00 -1.03e+01  1.06e-11  3.61e+00 
   500| 7.64e-03  2.55e-04  1.09e-01 -2.01e-01 -6.32e-02  1.05e-11  4.64e+00 
   560| 7.71e-06  4.24e-06  8.61e-04  2.17e-01  2.16e-01  1.05e-11  5.70e+00 
----------------------------------------------------------------------------
Status: Solved
Timing: Solve time: 5.70e+00s
    Lin-sys: avg # CG iterations: 1.71, avg solve time: 9.98e-03s
    Cones: avg projection time: 3.97e-06s
----------------------------------------------------------------------------
Error metrics:
dist(s, K) = 5.1560e-16, dist(y, K*) = 0.0000e+00, s'y/|s||y| = 2.4992e-17
|Ax + s - b|_2 / (1 + |b|_2) = 7.7108e-06
|A'y + c|_2 / (1 + |c|_2) = 4.2390e-06
|c'x + b'y| / (1 + |c'x| + |b'y|) = 8.6091e-04
----------------------------------------------------------------------------
c'x = 0.2169, -b'y = 0.2157
============================================================================
0.21689554805292935
cvxpy (modelling) + ecos/scs (solving) used (secs):  7.105745473999832

额外示例:5000x5000 使用约 9 分钟(没有调整参数)。

一些小小的补充说明:

  • SCS 比 ECOS 快(预期)
  • SCS/ECOS 都比 naive(不给出 jacobi-matrix)SLSQP 更快(对于至少每 N >= 100 的所有内容);更快 = 大 N 的数量级
  • 我检查了此方法与 SLSQP 的小示例的等效性

【讨论】:

  • 非常有用,非常感谢。我设法完成了 cvxpy 和 C++ 的安装。我测试了您的解决方案,一切似乎都有效(而且速度很快)。我会在 cvxpy 上阅读更多内容。
  • 嗨,sascha,我还有一个关于 python 优化的问题。但是,我使用的是 python 3.6,所以我不能真正使用 cvxpy 对吗?我在尝试定义要优化的函数的雅可比时遇到了问题。你介意看看吗?谢谢! stackoverflow.com/questions/43793204/…
【解决方案2】:

基于 pylang cmets,我计算了函数的雅可比值,得到以下函数:

def fct_deriv(x):
    return 2 * matrix.dot(x)

优化问题变成如下

minimize(fct, x0, method='SLSQP', jac=fct_deriv, bounds=bnds, constraints=cons)['x']

但是,该解决方案不允许添加 Hessian,因为 SLSQP 方法不允许。存在其他优化方法,但 SLSQP 是唯一同时接受边界和约束的方法(这是我的优化问题的核心)。

完整代码见下:

import numpy as np
from scipy.optimize import minimize

matrix = np.array([[1.0, 1.5, -2.],
                   [0.5, 3.0, 2.5],
                   [1.0, 0.25, 0.75]])

def fct(x):
    return x.dot(matrix).dot(x)

def fct_deriv(x):
    return 2 * matrix.dot(x)

x0 = np.ones(3) / 3
cons = ({'type': 'eq', 'fun': lambda x: x.sum() - 1.0})
bnds = [(0, 1)] * 3

w = minimize(fct, x0, method='SLSQP', jac=fct_deriv, bounds=bnds, constraints=cons)['x']

已编辑(添加了约束的雅可比):

cons2 = ({'type': 'eq', 'fun': lambda x: x.sum() - 1.0, 'jac': lambda x: np.ones_like(x)})

w = minimize(fct, x0, method='SLSQP', jac=fct_deriv, bounds=bnds, constraints=cons2)['x']

【讨论】:

  • 还可以看看这个scipy tutorial的优化部分,类似于scipy docs
  • @Eric B 你也可以提供约束的雅可比,在本例中为cons = ({'type': 'eq', 'fun': lambda x: x.sum() - 1.0, 'jac': lambda x: np.ones_like(x)})
  • 谢谢 Stelios,我根据您的字典定义了我的约束,我再次将优化时间减少了大约 35%。总的来说,定义我的函数的雅可比和约束的雅可比使我的优化减少了 50% 以上。将编辑答案。
猜你喜欢
  • 2011-03-13
  • 1970-01-01
  • 2021-04-12
  • 2021-03-16
  • 1970-01-01
  • 2015-09-25
  • 2011-06-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多