代码优化:如何使用scipy minimize优化代码?

Code optimization: How to optimize a code using scipy minimize?

我正在尝试优化我为计算方程组的最小二乘法而编写的代码,returns 我是未知数的最佳值:a1,a2,a3,z1,z2pottempzlevels 都知道)。

方程组如下(https://imgur.com/LQqeOgA):

我写了下面的代码,它很有效,但它不是很有效,你可以看到由于 for 循环的数量以及如果我增加 hstep 和 [=17 的数量=],我的数组变得非常大,从 leastsquared.

计算最小二乘法需要很多时间
def leastsquared(zlevels,pottemp,z1,z2,a1,a2,a3):
    dtot=0.0
    for zi in range(len(zlevels)): #zvalue of obs points 
        if zlevels[zi]<=z1:
            F=np.tan(a1)*zlevels[zi]+pottemp[0]
        elif zlevels[zi]<=z2:
            F=(np.tan(a1)*z1+pottemp[0]) + np.tan(a2)*(zlevels[zi]-z1)
        else: #zlevels[zi]<=H
            F=((np.tan(a1)*z1+pottemp[0]) + np.tan(a2)*(z2-z1) ) + np.tan(a3)*(zlevels[zi]-z2)
        d=(pottemp[zi]-F)**2.0
        dtot+=d
    dtot=dtot/len(zlevels)
    #print("dtot is" + str(dtot))
    return dtot
def optm(hstep,astep,zlevels,pottemp):

    sqdist=np.inf
    for z1 in np.linspace(0,zlevels[-1],hstep):
        for z2 in np.linspace(0,zlevels[-1],hstep):
            for a1 in np.linspace(0,0.1,astep): # max angle
                for a2 in np.linspace(0,0.01,astep):
                    for a3 in np.linspace(0,0.01,astep):
                        sqdist_new=leastsquared(zlevels,pottemp,z1,z2,a1,a2,a3)
                        if sqdist_new<sqdist:
                            sqdist=sqdist_new
                            optimalsetting=[z1,z2,a1,a2,a3]

    return optimalsetting

代码本身非常简单但效率低下。我试图使用 scipy.optimize.minimize 来实现此代码,但我无法 运行 它。有谁知道如何优化它?也许,scipy.optimize.minimize 是最简单的方法,但我不确定如何调整我的代码以适应它。

我不太确定实际变量及其值。但下面是我尝试过的东西:

import numpy as np
from scipy.optimize import minimize

def leastsquared(zlevels,pottemp,z1,z2,a1,a2,a3):
    dtot=0.0
    for zi in range(len(zlevels)): #zvalue of obs points 
        if zlevels[zi]<=z1:
            F = np.tan(a1)*zlevels[zi]+pottemp[0]
        elif zlevels[zi]<=z2:
            F = (np.tan(a1)*z1+pottemp[0]) + np.tan(a2)*(zlevels[zi]-z1)
        else: #zlevels[zi]<=H
            F=((np.tan(a1)*z1+pottemp[0]) + np.tan(a2)*(z2-z1) ) + np.tan(a3)*(zlevels[zi]-z2)
        d=(pottemp[zi]-F)**2.0
        dtot+=d
    dtot=dtot/len(zlevels)
    #print("dtot is" + str(dtot))
    return dtot

def optm(hstep,astep,zlevels,pottemp):
    steps = np.linspace(0,0.01,astep)
    x0 = np.array([steps, steps, steps]) #a1, a2, a3

    def objective(x):
        return leastsquared(zlevels,pottemp, z1, z2, x[0], x[1], x[2])

    sqdist=np.inf
    for z1 in np.linspace(0,zlevels[-1],hstep):
        for z2 in np.linspace(0,zlevels[-1],hstep):
            sol = minimize(objective, x0, method='SLSQP', options={'disp':True})
            optimalsetting=[z1,z2,sol.x[0],sol.x[1],sol.x[2]]
    return optimalsetting


# test
# some random initialization
astep = 2
steps = 3


optm(5, 2, [1, 2], [1, 2])

您可能想在此处更新 objective 函数和变量类型。 希望这能让你入门。

肯定有更有效的方法来处理这个问题,因为分段线性问题得到了很好的研究,但下面是根据要求用 scipy.optimize 完成的。 scipy 也无法处理动态约束,例如 z1<z2 的要求,因此如果不强制执行,我会交换求解器中变量的顺序。通过删除这些代码行并让数据说明一切,优化可能 more/less 稳定。

import numpy as np
import scipy.optimize

def f(zlevels, t0, a1, a2, a3, z1, z2, H):
    """
    Function to evaluate f, given a set of variables
    """
    # Check that no samples are out of bounds
    if any(zlevels < 0) or any(zlevels > H):
        quit("Values of zlevels out of bounds")

    pottemp = np.zeros(zlevels.shape)
    mask1 = zlevels <= z1
    mask2 = (z1 < zlevels) & (zlevels <= z2)
    mask3 = z2 < zlevels

    z1_arr1 = np.ones(sum(mask2))*z1
    z1_arr2 = np.ones(sum(mask3))*z1
    z2_arr = np.ones(sum(mask3))*z2

    pottemp[mask1] = zlevels[mask1] * a1 + t0
    pottemp[mask2] = z1_arr1 * a1 + t0 + a2 * (zlevels[mask2] - z1)
    pottemp[mask3] = z1_arr2 * a1 + t0 + a2 * (z2_arr - z1) + a3 * (zlevels[mask3] - z2)

    return pottemp

def obj_fun(x, zlevels, pottemp, t0, H):
    """
    Least-squares objective function for the problem
    """
    a1, a2, a3, z1, z2 = x
    # Swap order if z1 is larger than z2
    if z1 > z2:
        z1, z2 = z2, z1

    pottemp_pred = f(zlevels, t0, a1, a2, a3, z1, z2, H)
    return sum((pottemp_pred-pottemp)**2) 

def optimize(zlevels, pottemp, t0, H):
    """
    Optimize a1, a2, a3, z1, z2
    """

    starting_guess = [0,0,0,0.5,2.5]
    res = scipy.optimize.minimize(obj_fun, starting_guess, args=(zlevels, pottemp, t0, H), \
            bounds=[(None,None),(None,None),(None,None),(0,H),(0,H)])
    x = res.x
    # Swap order if z1 is larger than z2
    if x[-2] > x[-1]:
        x[-2], x[-1] = x[-1], x[-2]
    return x

# Make the true model for testing
t0 = 0.5
a1 = -1
a2 = +1
a3 = -2
z1 = 1
z2 = 2
H = 3

# Generate some variables
n = 1000
zlevels = np.random.random(n)*3
# Get values and add some random noise
pottemp = f(zlevels, t0, a1, a2, a3, z1, z2, H) + np.random.normal(0,0.1, size=n)

print("Correct values:", a1, a2, a3, z1, z2)
a1, a2, a3, z1, z2 = optimize(zlevels, pottemp, t0, H)
print("Fitted values:", a1, a2, a3, z1, z2)