【问题标题】:nonlinear optimization with vectors, scalars and inequality constraints具有向量、标量和不等式约束的非线性优化
【发布时间】:2018-03-15 19:40:22
【问题描述】:

我有一组方程式:Y=aA+bB 其中 Y 是知道浮点数的向量(只有这个是已知的!); a、b 是未知标量(浮点数),A、B 是浮点数的未知向量。每个方程都有自己的 Y、a、b,而所有方程共享相同的未知向量 A 和 B。

我已经设置了这样的方程,所以我的问题是最小化函数: (Y-aA-bB)+(Y'-a'A-b'B)+.... 我也有许多类型的不等式约束:Ai>Aj(向量 A 的 Ai i-th 元素),Bi>= Bk,Bi>0,a>a',...

是否有任何软件或库(最适合 python)可以处理这个问题?

【问题讨论】:

  • 到目前为止你研究了什么?你看过 numpy 吗?

标签: python optimization scipy mathematical-optimization nonlinear-optimization


【解决方案1】:

我同意 sascha 的观点,即这是一个线性问题。由于我不太喜欢约束,实际上我更喜欢使它成为没有约束的非线性。我这样做是通过像这样设置向量A=(a1**2, a1**2+a2**2, a1**2+a2**2+a3**2, ...) 来确保它都是正的,A_i > A_ji>j。这使得错误有点问题,因为您现在必须考虑错误传播以获取 A1A2 等,包括相关性,但我将在最后提出重要的一点。 “简单”的解决方案如下所示:

import numpy as np
from scipy.optimize import leastsq
from random import random
np.set_printoptions(linewidth=190)


def generate_random_vector(n, sortIt=True):
    out=np.fromiter( (random() for x in range(n) ),np.float)
    if sortIt:
        out.sort()
    return out


def residuals(parameters,dataVec,dataLength,vecDims):
    aParams=parameters[:dataLength]
    bParams=parameters[dataLength:2*dataLength]
    AParams=parameters[-2*vecDims:-vecDims]
    BParams=parameters[-vecDims:]
    YList=dataVec
    AVec=[a**2 for a in AParams]##assures A_i > 0
    BVec=[b**2 for b in BParams]
    AAVec=np.cumsum(AVec)##assures A_i>A_j for i>j
    BBVec=np.cumsum(BVec)
    dist=[ np.array(Y)-a*np.array(AAVec)-b*np.array(BBVec) for Y,a,b in zip(YList,aParams,bParams)  ]
    dist=np.ravel(dist)
    return dist


if __name__=="__main__":

    aList=generate_random_vector(20, sortIt=False)
    bList=generate_random_vector(20, sortIt=False)
    AVec=generate_random_vector(5)
    BVec=generate_random_vector(5)
    YList=[a*AVec+b*BVec for a,b in zip(aList,bList)]

    aGuess=20*[.2]
    bGuess=20*[.3]
    AGuess=5*[.4]
    BGuess=5*[.5]

    bestFitValues, covMX, infoDict, messages ,ier = leastsq(residuals, aGuess+bGuess+AGuess+BGuess ,args=(YList,20,5) ,full_output=True)


    print "a"
    print aList
    besta = bestFitValues[:20]
    print besta

    print "b"
    print bList
    bestb = bestFitValues[20:40]
    print bestb

    print "A"
    print AVec
    bestA = bestFitValues[-2*5:-5]
    realBestA = np.cumsum([x**2 for x in bestA])
    print realBestA

    print "B"
    print BVec
    bestB = bestFitValues[-5:]
    realBestB = np.cumsum([x**2 for x in bestB])
    print realBestB
    print covMX

关于错误和相关性的问题是问题的解决方案不是唯一的。如果Y = a A + b B 是一个解决方案并且我们,例如,旋转使得A = c E + s FB = -s E + c F 那么Y = (ac-bs) E + (as+bc) F =e E + f F 也是一个解决方案。因此,参数空间在“解决方案”处完全平坦,导致巨大的错误和世界末日的相关性。

【讨论】:

  • 感谢您的解决方案。在我看来,它仅适用于一个方程 (Y=aA+bB),而不适用于具有共同 A 和 B 的一组方程 (Y=aA+bB, Y'=a'A+b'B, ...) . 据我了解此解决方案中的“约束”:1)我需要能够对 A 进行排序(从最低到最高) 2)并且此顺序在 A 和 B 中都需要相同( A_i>A_j 和 B_i> B_j for i>j ) 3) A 和 B 的所有元素都需要大于 0。不幸的是 2 和 3 在我的情况下是不可能的。
  • @rmrmg 我的解决方案是一组任意的ab 固定但随机的ABAB 的元素满足您的第 1)、2) 和 3) 点。在第一个版本中,我没有合并 a1a'...)您能否澄清是否需要此条件,即 a10 和 b_i >0 是否也是必需的,或者它们可以是负数吗?
  • 如果是这样,您可以对aParamsbParams 使用与AVecAVecBVec 相同的技巧,但您的解决方案仍然不是唯一的。除了复杂的旋转,您可以通过简单的缩放看到:将A 的所有元素放大u 的因子并将所有a 的值除以u 不会改变Y' s...B 相同。这是因为您正在寻找加权线性组合。随着您的限制,您减少了可能性,但仍然有无限缩放的线性组合为您提供相同的Y。唯一不变的信息是AB 跨度的二维平面。
【解决方案2】:

一般说明

这是一个线性问题(至少在线性最小二乘意义上,继续阅读)!

它也没有完全指定,因为不清楚在您的情况下是否应该始终有一个可行的解决方案,或者您是否希望总体上最小化一些给定的损失。您的文字听起来像后者,但在这种情况下,必须选择损失(这在可能的算法方面有所不同)。让我们采用欧几里得范数(可能是这里最好的选择)!

暂时忽略约束,我们可以将此问题视为基本的线性矩阵方程的最小二乘解问题(欧几里得范数与平方欧几里德范数没有区别!) .

min || b - Ax ||^2

这里:

M = number of Y's
N = size of Y
b = (Y0,
     Y1,
     ...) -> shape: M*N (flattened: Y_x = (y_x_0, y_x_1).T)
A = ((a0, 0, 0, ..., b0, 0, 0, ...),
     (0, a0, 0, ..., 0, b0, 0, ...),
     (0, 0, a0, ..., 0, 0, b0, ...),
     ...
     (a1, 0, 0, ..., b1, 0, 0, ...)) -> shape: (M*N, N*2)
x = (A0, A1, A2, ... B0, B1, B2, ...) -> shape: N*2 (one for A, one for B)

你应该怎么做

  • 如果不受约束
    • 转换为标准格式并使用numpy的lstsq
  • 如果受约束
    • 要么使用定制的优化算法,要么:
      • 线性规划(如果最小化绝对差异/l1-norm)
        • 我懒得为scipy的linprog制定它
        • 没那么难,但使用 scipy 的 API,l1-norm 并非易事
        • 使用cvxpy (obj=cvxpy.norm(X, 1)) 更容易制定
      • 二次规划/二阶锥规划(如果最小化欧几里得范数/l2-范数)
        • 再次,懒得制定它; scipy 上还没有特殊的求解器
        • 可以使用 cvxpy (obj=cvxpy.norm(X, 2)) 轻松制定
      • 紧急情况:使用通用的约束非线性优化算法,如 SLSQP -> 参见代码

一些 hacky 代码(不是最好的方法!)

这段代码:

  • 只是一个演示!
  • 使用来自 scipy 的一般非线性优化算法
    • 因此:
      • 更容易制定
      • 不如 LP、QP、SOCP 快速和稳健
      • 但在保证凸优化问题收敛的情况下,将获得大致相同的结果
    • 在需要时使用自动微分
      • (作者懒得加渐变)
      • 如果性能很重要,这真的很糟糕
  • 在 np.repeat 与广播方面真的很难看!

代码:

import numpy as np
from scipy.optimize import minimize
np.random.seed(1)

""" Fake-problem (usually the job of the question-author!) """
def get_partial(N=10):
    Y = np.random.uniform(size=N)
    a, b = np.random.uniform(size=2)
    return Y, a, b

""" Optimization """
def optimize(list_partials, N, M):
    """ General approach:
        This is a linear system of equations (with constraints)
        Basic (unconstrained) form: min || b - Ax ||^2
     """
    Y_all = np.vstack(map(lambda x: x[0], list_partials)).ravel()       # flat 1d
    a_all = np.hstack(map(lambda x: np.repeat(x[1], N), list_partials)) # repeat to be of same shape
    b_all = np.hstack(map(lambda x: np.repeat(x[2], N), list_partials)) # """

    def func(x):
        A = x[:N]
        B = x[N:]
        return np.linalg.norm(Y_all - a_all * np.repeat(A, M) - b_all * np.repeat(B, M))

    """ Example constraints: A >= B element-wise """
    cons = ({'type': 'ineq',
             'fun' : lambda x: x[:N] - x[N:]})

    res = minimize(func, np.zeros(N*2), constraints=cons, method='SLSQP', options={'disp': True})
    print(res)
    print(Y_all - a_all * np.repeat(res.x[:N], M) - b_all * np.repeat(res.x[N:], M))


""" Test """
M = 4
N = 3
list_partials = [get_partial(N) for i in range(M)]
optimize(list_partials, N, M)

输出:

Optimization terminated successfully.    (Exit mode 0)
            Current function value: 0.9019356096498999
            Iterations: 12
            Function evaluations: 96
            Gradient evaluations: 12
     fun: 0.9019356096498999
     jac: array([  1.03786588e-04,   4.84041870e-04,   2.08129734e-01,
         1.57609582e-04,   2.87599862e-04,  -2.07959406e-01])
 message: 'Optimization terminated successfully.'
    nfev: 96
     nit: 12
    njev: 12
  status: 0
 success: True
       x: array([ 1.82177105,  0.62803449,  0.63815278, -1.16960281,  0.03147683,
        0.63815278])
[  3.78873785e-02   3.41189867e-01  -3.79020251e-01  -2.79338679e-04
  -7.98836875e-02   7.94168282e-02  -1.33155595e-01   1.32869391e-01
  -3.73398306e-01   4.54460178e-01   2.01297470e-01   3.42682496e-01]

我没有检查结果!如果有错误,那就是实现错误,而不是概念错误(我的观点)!

【讨论】:

  • 非常感谢您的回答。我不确定您的意思是:x = N*2(A 一个,B 一个)
  • Scipy 的优化器是为未知向量(不是多个向量)构建的。这也适用于我介绍的这些代数矩阵形式。我们不使用两个优化向量A=[a0, a1, a2, ...]B=[b0, b1, b2, ...],而是使用一个:X=[a0, a1, a2, ..., b0, b1, b2]。而shape A == shape Y -> A 也有 N 元素和 Bshape X = N*2
  • 谢谢。如果我错了,请纠正我:我需要强制 A 保持您通过(相当大的)一组约束编写的形式。所以,我需要 i)将所有零的元素固定为 0(这在每个库中都很容易)和 ii)以某种方式(我还没有找到如何)强制矩阵 A 的几个元素彼此相等(例如 A(0, 0)=A(1,1)=A(2,2) 即 a0 以此类推)
  • 这完全取决于你在做什么。这种形式是线性最小二乘的形式(与numpy的API兼容;我不知道约束这个词是否正确,这是我开头言论的由来); LP/QP 看起来会有些不同,因为我们需要 1 个辅助变量来获取损失。
  • 回到您的评论:理想情况下它应该是一种解决方案,但在我的模型中,我希望它应该不仅仅是一种解决方案。没有约束,解决方案的数量将是巨大的。约束是一种旨在减少解决方案数量的启发式方法。我可以随机开始,也可以对 A 和 B 使用合理的初始值。先验无法说哪个更好。我需要建立相关性来测试我的模型。我不想进入模型的物理意义,它非常复杂,与我的问题无关。我想为给定的 a、b、A、B 初始值找到一些局部最小值。
猜你喜欢
  • 1970-01-01
  • 2019-05-23
  • 2023-04-03
  • 1970-01-01
  • 2015-08-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多