Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/280.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/azure/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 使用scipy最小化多变量函数。导数未知_Python_Scipy_Mathematical Optimization_Minimization - Fatal编程技术网

Python 使用scipy最小化多变量函数。导数未知

Python 使用scipy最小化多变量函数。导数未知,python,scipy,mathematical-optimization,minimization,Python,Scipy,Mathematical Optimization,Minimization,我有一个函数,它实际上是对另一个程序的调用(一些Fortran代码)。当我调用这个函数(run\u moog)时,我可以解析4个变量,它返回6个值。这些值都应该接近0(以便最小化)。但是,我这样组合它们:np.sum(results**2)。现在我有一个标量函数。我希望最小化此函数,即使np.sum(结果**2)尽可能接近于零。 注意:当此函数(run\u moog)接受4个输入参数时,它会为依赖于这些参数的Fortran代码创建一个输入文件 我已经尝试了几种方法来优化它。但没有一个像预期的那样

我有一个函数,它实际上是对另一个程序的调用(一些Fortran代码)。当我调用这个函数(
run\u moog
)时,我可以解析4个变量,它返回6个值。这些值都应该接近0(以便最小化)。但是,我这样组合它们:
np.sum(results**2)
。现在我有一个标量函数。我希望最小化此函数,即使
np.sum(结果**2)
尽可能接近于零。
注意:当此函数(
run\u moog
)接受4个输入参数时,它会为依赖于这些参数的Fortran代码创建一个输入文件

我已经尝试了几种方法来优化它。但没有一个像预期的那样有效。最小化应该能够在4个变量上有界。以下是一个尝试:

from scipy.optimize import minimize # Tried others as well from the docs
x0 = 4435, 3.54, 0.13, 2.4
bounds = [(4000, 6000), (3.00, 4.50), (-0.1, 0.1), (0.0, None)]
a = minimize(fun_mmog, x0, bounds=bounds, method='L-BFGS-B')  # I've tried several different methods here
print a
这就给了我

  status: 0
 success: True
    nfev: 5
     fun: 2.3194639999999964
       x: array([  4.43500000e+03,   3.54000000e+00,   1.00000000e-01,
         2.40000000e+00])
 message: 'CONVERGENCE: NORM_OF_PROJECTED_GRADIENT_<=_PGTOL'
     jac: array([ 0., 0., -54090399.99999981, 0.])
     nit: 0
状态:0
成功:真的
nfev:5
乐趣:2.319463999964
x:阵列([4.4350000E+03,3.54000000e+00,1.00000000e-01,
2.40000000e+00])

信息:“收敛:投影梯度的范数”两种可能性:

  • 试试科比拉。它应该是无导数的,并且支持不等式约束
  • 您不能通过普通接口使用不同的ε;因此,尝试将第一个变量缩放1e4。(输入时除以,输出时乘以。)
  • 跳过普通的自动雅可比构造函数,创建自己的:
  • 假设您试图使用SLSQP,但没有提供雅可比函数。这是给你的。它的代码在
    approx_jacobian
    in中。以下是一个简明版本:

    def approx_jacobian(x,func,epsilon,*args):
        x0 = asfarray(x)
        f0 = atleast_1d(func(*((x0,)+args)))
        jac = zeros([len(x0),len(f0)])
        dx = zeros(len(x0))
        for i in range(len(x0)):
            dx[i] = epsilon
            jac[i] = (func(*((x0+dx,)+args)) - f0)/epsilon
            dx[i] = 0.0
    
        return jac.transpose()
    
    您可以尝试将该循环替换为:

        for (i, e) in zip(range(len(x0)), epsilon):
            dx[i] = e
            jac[i] = (func(*((x0+dx,)+args)) - f0)/e
            dx[i] = 0.0
    
    您不能将其作为雅可比矩阵提供给
    最小化
    ,但要解决这个问题很简单:

    def construct_jacobian(func,epsilon):
        def jac(x, *args):
            x0 = asfarray(x)
            f0 = atleast_1d(func(*((x0,)+args)))
            jac = zeros([len(x0),len(f0)])
            dx = zeros(len(x0))
            for i in range(len(x0)):
                dx[i] = epsilon
                jac[i] = (func(*((x0+dx,)+args)) - f0)/epsilon
                dx[i] = 0.0
    
            return jac.transpose()
        return jac
    
    然后您可以调用
    minimize
    如下:

    minimize(fun_mmog, x0,
             jac=construct_jacobian(fun_mmog, [1e0, 1e-4, 1e-4, 1e-4]),
             bounds=bounds, method='SLSQP')
    

    听起来你的目标函数没有表现良好的导数。输出
    jac:array([0,0.,-54090399.9999981,0.])
    中的行表示仅更改第三个变量值是重要的。因为这个变量的导数w.r.t实际上是无限的,所以函数中可能有错误。这也是第三个变量值最终达到最大值的原因

    我建议你看看导数,至少在你的参数空间的几个点上。使用有限差分和SciPy的默认步长
    fmin_l_bfgs_b
    1e-8
    计算它们。这是一个如何计算导数的示例


    还可以尝试绘制目标函数。例如,保持其中两个参数不变,让其他两个参数变化。如果函数具有多个局部最优值,则不应使用基于梯度的方法,如BFGS。

    获得梯度的分析表达式有多困难?如果你有,你可以用有限差分法,用向量近似海森积。然后您可以使用其他可用的优化例程

    在SciPy中可用的各种优化例程中,称为TNC(带截断的牛顿共轭梯度)的例程对与问题相关的数值非常稳健。

    众所周知,(在上面的评论中建议的)是优化的好选择(可能表现不好)不了解导数的函数(请参阅)

    你的问题有两个特定的方面。第一个是对输入的约束,第二个是缩放问题。下面给出了这些问题的解决方案,但您可能需要在它们之间手动迭代几次,直到一切正常为止

    输入约束

    假设您的输入约束形式为a(如上面的示例所示,但我想概括一下),那么您可以编写一个函数

    is_in_bounds(p):
        # Return if p is in the bounds
    
    使用此函数,假设算法要从点
    移动到点
    ,其中
    已知位于区域中。然后,以下功能将有效地查找两个点之间的直线上的最远点,并在其上继续:

    from numpy.linalg import norm
    
    def progress_within_bounds(from_, to, eps):
        """
        from_ -- source (in region)
        to -- target point
        eps -- Eucliedan precision along the line
        """
    
        if norm(from_, to) < eps:
            return from_
        mid = (from_ + to) / 2
        if is_in_bounds(mid):
            return progress_within_bounds(mid, to, eps)
        return progress_within_bounds(from_, mid, eps)
    
    注意此实现非常简单,无论对您是否合适。不过,从任何伪代码修改NM都非常容易,而且在任何情况下都可能需要加入

    缩放

    这是一个更棘手的问题,但也提出了一个有趣的观点。一旦修改后的NM算法找到一个点,您可能希望在修复一些维度的同时运行,以便查看函数的行为。此时,您可能需要重新缩放一个或多个维度,然后重新运行修改后的NM


    在迭代一次之前,您的函数似乎被非常相似的值绊住了。也许,像Nelder Mead(不使用导数)这样的方法,具有较小的ftol和xtol参数,可能会失败。首先,网格搜索可能是最好的开始(然后给出初始值以最小化)。你确定你没有得到NaN或Inf吗?(有时,即使为参数设置了理论界,算法也会返回其中一个,如果它在数值上不稳定)问题在于计算近似梯度的步骤必须太小,因此雅可比矩阵中的零。尝试使用
    method='L-BFGS-B'
    添加
    options={'epsilon':1e-4}
    ,或一些更大的值(默认情况下为
    1e-8
    ),直到雅可比矩阵没有零为止。这两种解决方案似乎都有助于工作。但是,我想使用其中一个,在这里我可以使用约束(
    BFGS
    L-BFGS-B
    SLSQP
    )。因此,通过设置eps:1e0,它会运行,但在某个点上,我会在约束集之外运行。从OP添加的唯一内容是
    ,选项={'eps':1e+0})
    最小化中import copy
    
    '''
        Pure Python/Numpy implementation of the Nelder-Mead algorithm.
        Reference: https://en.wikipedia.org/wiki/Nelder%E2%80%93Mead_method
    '''
    
    
    def nelder_mead(f, x_start, 
            step=0.1, no_improve_thr=10e-6, no_improv_break=10, max_iter=0,
            alpha = 1., gamma = 2., rho = -0.5, sigma = 0.5):
        '''
            @param f (function): function to optimize, must return a scalar score 
                and operate over a numpy array of the same dimensions as x_start
            @param x_start (numpy array): initial position
            @param step (float): look-around radius in initial step
            @no_improv_thr,  no_improv_break (float, int): break after no_improv_break iterations with 
                an improvement lower than no_improv_thr
            @max_iter (int): always break after this number of iterations.
                Set it to 0 to loop indefinitely.
            @alpha, gamma, rho, sigma (floats): parameters of the algorithm 
                (see Wikipedia page for reference)
        '''
    
        # init
        dim = len(x_start)
        prev_best = f(x_start)
        no_improv = 0
        res = [[x_start, prev_best]]
    
        for i in range(dim):
            x = copy.copy(x_start)
            x[i] = x[i] + step
            score = f(x)
            res.append([x, score])
    
        # simplex iter
        iters = 0
        while 1:
            # order
            res.sort(key = lambda x: x[1])
            best = res[0][1]
    
            # break after max_iter
            if max_iter and iters >= max_iter:
                return res[0]
            iters += 1
    
            # break after no_improv_break iterations with no improvement
            print '...best so far:', best
    
            if best < prev_best - no_improve_thr:
                no_improv = 0
                prev_best = best
            else:
                no_improv += 1
    
            if no_improv >= no_improv_break:
                return res[0]
    
            # centroid
            x0 = [0.] * dim
            for tup in res[:-1]:
                for i, c in enumerate(tup[0]):
                    x0[i] += c / (len(res)-1)
    
            # reflection
            xr = x0 + alpha*(x0 - res[-1][0])
            ##################################################################
            ##################################################################
            xr = progress_within_bounds(x0, x0 + alpha*(x0 - res[-1][0]), prog_eps)
            ##################################################################
            ##################################################################
            rscore = f(xr)
            if res[0][1] <= rscore < res[-2][1]:
                del res[-1]
                res.append([xr, rscore])
                continue
    
            # expansion
            if rscore < res[0][1]:
                xe = x0 + gamma*(x0 - res[-1][0])
                ##################################################################
                ##################################################################
                xe = progress_within_bounds(x0, x0 + gamma*(x0 - res[-1][0]), prog_eps)
                ##################################################################
                ################################################################## 
                escore = f(xe)
                if escore < rscore:
                    del res[-1]
                    res.append([xe, escore])
                    continue
                else:
                    del res[-1]
                    res.append([xr, rscore])
                    continue
    
            # contraction
            xc = x0 + rho*(x0 - res[-1][0])
            ##################################################################
            ##################################################################
            xc = progress_within_bounds(x0, x0 + rho*(x0 - res[-1][0]), prog_eps)
            ##################################################################
            ################################################################## 
            cscore = f(xc)
            if cscore < res[-1][1]:
                del res[-1]
                res.append([xc, cscore])
                continue
    
            # reduction
            x1 = res[0][0]
            nres = []
            for tup in res:
                redx = x1 + sigma*(tup[0] - x1)
                score = f(redx)
                nres.append([redx, score])
            res = nres