Scipy Minimize对不同的初始化得到不同的结果

rkue9o1l  于 2023-10-20  发布在  其他
关注(0)|答案(2)|浏览(181)

问题描述

当我在下面的代码中运行test()时,我发现scipy.optimize.minimize()对于要求解的变量的不同初始化得到了不同的结果。

import numpy as np
from numpy.random import default_rng, Generator
from scipy.optimize import check_grad, minimize, Bounds, LinearConstraint, OptimizeResult

eta=10e-27
k=1e6#to balance the magnitude of the data

# The objective function
def sum_cost(
    f: np.ndarray,
    Adstr: float,
    coef1: np.ndarray,
    coef2: np.ndarray,
) -> float:
    return (k*coef1.dot(f**2) + coef2.dot(1/f) + Adstr)*1e5 #Scale results for easier optimization

# Minimize progress
def cvx1(
    A: np.ndarray,
    ds: np.ndarray,
    ts: np.ndarray,
    tr: np.ndarray,
    Cm: np.ndarray,
    eta: float = 10e-27,
) -> tuple[np.ndarray, OptimizeResult]:
    # parameter definition
    M, N = A.shape
    Adstr = (A/tr @ ds.T).sum()
    coef2 = (A*ts).ravel()
    coef1 = coef2 * eta

    # two constraints for my problem:
    # 1.x0>=0
    # 2.sum(x0,axis=1) <= Cm.reshape((M,)
    #  A      x         Cm
    # (M MN) (MN 1) <= (M 1)
    constraint = LinearConstraint(
        A=np.repeat(np.eye(M), N, axis=1),
        ub=Cm.ravel(),
    )

    # one way to initialize x0
    # Use analytic optimum when it obeys the constraint
    # Use equally divided Cm otherwise.
    optimum = (0.5 / (eta*k)) ** (1 / 3)
    print('optimum',optimum)
    x0 = np.zeros((M, N))
    for m in range(M):
        count = len(np.where(A[m, :] == 1)[0])
        # print(count)
        # print(C_m[0, m])
        x0[m, np.where((A[m, :]) == 1)[0]] = np.minimum(Cm[0, m] / count, optimum)
        x0[m, np.where((A[m, :] == 0))[0]] = 1e-16
    print('initial value for x0:',x0)

    result = minimize(
        fun=sum_cost,
        x0=x0.ravel(), # one way to initialize x0
        # x0=np.ones(M * N),# another way to initialize x0
        args=(Adstr, coef1, coef2),
        bounds=Bounds(lb=0),
        constraints=constraint,
        tol=1e-16,
        options={'maxiter': 1000},
    )
    f = result.x.reshape((M, N))

    print(result)

    if np.allclose(f, x0):
        print('Warning: f == initial; no optimization needed')

    return f, result

def test(M: int=3, N: int=6) -> None:
    rand = default_rng(seed=11)

    #some random parameters for the objective function

    # here just to produce a 0-1 matrix A:
    A = rand.uniform(low=0, high=1, size=(M, N))
    am_current = np.argmax(A, axis=0)
    a_current = np.zeros(A.shape)
    a_current[am_current, np.arange(A.shape[1])] = 1
    A=a_current
    print('A:', A)

    #other parameters
    ds = rand.uniform(500*8, 2_000*8, (1, N))
    ts = rand.uniform(10, 20, (1, N)) * ds
    tr = rand.uniform(50_000, 300_000, (M, N))
    C_m = rand.uniform(5e6, 20e6, (1, M))

    # solve
    x1, result1 = cvx1(A=A, ds=ds, ts=ts, tr=tr, Cm=C_m)

我运行了上面的代码两次,一次是使用如上所述定制的x0,另一次是使用x0所有1,但我得到了两个不同的结果,如下所示:

结果显示

1.如上所述定制的x0的结果

initial value for x0: [[1.0000000e-16 3.6840315e+06 1.0000000e-16 1.0000000e-16 1.0000000e-16
  3.6840315e+06]
 [1.0000000e-16 1.0000000e-16 3.6840315e+06 1.0000000e-16 1.0000000e-16
  1.0000000e-16]
 [3.6840315e+06 1.0000000e-16 1.0000000e-16 3.6840315e+06 3.6840315e+06
  1.0000000e-16]]
     fun: 86503.22680146241
     jac: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0.])
 message: 'Optimization terminated successfully'
    nfev: 19
     nit: 1
    njev: 1
  status: 0
 success: True
       x: array([1.0000000e-16, 3.6840315e+06, 1.0000000e-16, 1.0000000e-16,
       1.0000000e-16, 3.6840315e+06, 1.0000000e-16, 1.0000000e-16,
       3.6840315e+06, 1.0000000e-16, 1.0000000e-16, 1.0000000e-16,
       3.6840315e+06, 1.0000000e-16, 1.0000000e-16, 3.6840315e+06,
       3.6840315e+06, 1.0000000e-16])
Warning: f == initial; no optimization needed

============================== 1 passed in 0.18s ==============================
  1. x0所有1的结果
fun: 87740.20159970068
     jac: array([ 0.        , -0.00097656,  0.        ,  0.        ,  0.        ,
        0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
        0.        ,  0.        , -0.00097656,  0.        ,  0.        ,
       -0.00097656, -0.00097656,  0.        ])
 message: 'Optimization terminated successfully'
    nfev: 8336
     nit: 419
    njev: 417
  status: 0
 success: True
       x: array([2.57105896e+04, 3.42638038e+06, 1.51942392e+02, 1.51942383e+02,
       1.51942391e+02, 3.46166354e+06, 8.37974281e-01, 8.37972296e-01,
       4.25764167e+06, 8.37974124e-01, 8.37970035e-01, 8.37973655e-01,
       2.77064502e+06, 2.98881278e+06, 4.53859019e+04, 2.86306864e+06,
       3.19190058e+06, 3.59267809e+04])

========================= 1 passed, 1 warning in 0.43s =========================

一些疑问

1.如图所示,x0的两种不同初始化方式给予不同的结果。我的代码有什么问题吗?这些结果准确吗?
2.第一个定制的x0得到了更好的结果,不需要更多的优化(然而,它并不总是像我尝试的那样得到更好的结果),它已经是我的目标函数的一般最优解了吗?
3.顺便说一下,第一种初始化x0的方法被设计成遵循这样的规则,如果A[m][n]==0,x0[m][n]=0,但我不知道如何在Scipy中设置这种约束?
提前感谢和任何这些问题的答案是赞赏!

jyztefdp

jyztefdp1#

你遇到的是local optimum problem。当你运行极小化器时,它会搜索一些最优解,在这种情况下,“最优”是最小值。你使用的最小化器是一个数值求解器,这基本上意味着在引擎盖下有一个非常聪明的猜测和检查算法。在某些情况下,数值求解器会找到 a 解,但它可能不是 * 全局最佳 * 解。
举一个经典的例子,假设你被赋予了在一个大山脉中找到最高峰的任务,但你被蒙住了眼睛。一个合理的方法是向前走一步,如果你觉得你在走一个斜坡,继续走。如果你不再往上走了,测试一下你周围的几个不同的方向,看看是否有任何一个方向是向上的。最终,你会到达一个顶峰,在那里你周围的所有方向都通向较低的海拔,所以你必须在顶部!很好,但你怎么知道你在最高的山上?因为你被蒙上眼睛,你不能只是四处看看,所以真的,你不能保证你找到了最高的山,除非你走遍了山脉的每一寸土地--这是一种耗时的,蛮力的方法。当然,许多数学家已经提出了更好的方法来解决这个问题。
如果你想要一个全局最小值,你应该看看scipy的全局优化器。这里有多种选择(包括暴力破解),阅读文档,尝试一些,看看什么最适合你的问题。

hvvq6cgz

hvvq6cgz2#

添加到@托马斯的答案,优化技术很容易根据初始条件给出给予不同的结果,看看这个gif:

你会发现初始条件的微小差异会导致完全不同的结果。

相关问题