在scipy.optimize中如何恢复一个优化过程?

6

scipy.optimize提供了许多不同的方法来进行多变量系统的局部和全局优化。然而,我需要进行一个非常长的优化运行,可能会被中断(在某些情况下,我可能希望故意中断它)。有没有办法重新启动...其中任何一种方法呢?我的意思是,显然可以提供找到的最后一个最优参数集作为初始猜测,但这不是唯一起作用的参数-例如,还有梯度(例如雅各比矩阵),差分进化中的种群等。我显然不希望这些也必须重新开始。

我看不出有什么方法可以向scipy证明这一点,也不能保存其状态。例如,对于需要雅各比矩阵的函数,有一个雅各比矩阵参数(“jac”),但它要么是一个布尔值(表示您的评估函数返回雅各比矩阵,而我的不返回),要么是可调用函数(我只能提供上次运行的单个结果)。没有东西只接受上一个雅各比矩阵数组。而且对于差分进化,种群的丢失会对性能和收敛产生可怕的影响。

有没有解决方案?有没有任何方法可以恢复优化?


1
我想到的最好方法几乎难以想象丑陋:分叉进程以创建一个或多个“断点”版本(一个具有当前正在运行的优化函数和整个应用程序的精确副本的进程!),并使断点版本在优化器的回调或评估调用中挂起,直到通过某种IPC信号继续前进,或者如果变得过时则被杀死。当然,您不能在“运行”之间更改参数,因为它们仍然是相同的运行!也不会在重新启动后持久存在。而且非常丑陋... - KarenRei
  1. 如果 jac 是你的函数,你可以轻松地使用 *kwarg 在第一次调用时加载并返回 xx.jac
  2. 具体是哪个优化器,differentialevolution?
  3. 你想要一个运行树,想要运行一段时间,逐个打印/绘制/保存吗?一个大的命令序列示例:"A10 = run 10; A100 = run to 100; B = (A; params = ...; run 100) ..." 可以澄清。
- denis
我还没有对不同的优化方法进行基准测试,以便知道哪种方法最适合我;我仍在调整我的评估函数以提高性能和准确性。例如,我不知道它会倾向于陷入局部最小值的程度。 "jac" 不是我的评估函数,它是最小化函数所采用的参数,用于指定如何确定雅可比矩阵(一阶导数/梯度)。运行树可能很好,但即使只能维护一个检查点也很棒。你如何在scipy中保留一个检查点并“运行100”次迭代? - KarenRei
我一直在尝试设计我的参数,以便可以进行交叉,而不会产生荒谬的结果(即,尽可能创建相互独立的参数),以防需要使用差分进化。但是,差分进化往往比梯度方法慢,在局部最小值不是问题的情况下。因此,这取决于梯度方法收敛的好坏。我也希望能够并行运行,但scipy似乎也不支持这种方式... - KarenRei
1
记录一下(刚好遇到了这个旧帖子),我发现了一个非常简单的恢复方法:缓存所有最小化函数的结果,以函数参数作为它们的键。保存缓存并在重新启动时加载它。在最小化函数的开头添加一个检查,看看它是否已经运行过。就这样,完成了。给定相同的参数和相同的返回结果,最小化和差分进化将重演它们之前所做的精确相同的步骤,由于缓存而以闪电般的速度执行。 :) - KarenRei
2个回答

4
一般情况下,没有通用的解决方案,除非像你所说的那样,从上次运行的最后一个估计值开始。对于差分进化算法,您可以实例化 DifferentialEvolutionSolver,并在检查点进行 pickling,以便恢复时 unpickle。 (建议来自 https://github.com/scipy/scipy/issues/6517

0
以下代码可以从先前的 x 保存并重新启动,但我猜你想要保存和重新启动更多状态,比如渐变;你能澄清一下吗?
另外参见 basinhopping,该程序具有漂亮的图形用户界面,pele-python
#!/usr/bin/env python
""" Funcgradmn: wrap f() and grad(), save all x[] f[] grad[] to plot or restart """

from __future__ import division
import numpy as np

__version__ = "2016-10-18 oct denis"


class Funcgradmon(object):
    """ Funcgradmn: wrap f() and grad(), save all x[] f[] grad[] to plot or restart

    Example: minimize, save, restart --

    fg = Funcgradmon( func, gradfunc, verbose=1 )
        # fg(x): f(x), g(x)  for minimize( jac=True )

        # run 100 iter (if linesearch, 200-300 calls of fg()) --
    options = dict( maxiter=100 )  # ... 
    min0 = minimize( fg, x0, jac=True, options=options )
    fg.savez( "0.npz", paramstr="..." )  # to plot or restart

        # restart from x[50] --
        # (won't repeat the previous path from 50
        # unless you save and restore the whole state of the optimizer)
    x0 = fg.restart( 50 )
    # change params ...
    min50 = minimize( fg, x0, jac=True, options=options )
    """

    def __init__( self, func, gradfunc, verbose=1 ):
        self.func = func
        self.gradfunc = gradfunc
        self.verbose = verbose
        self.x, self.f, self.g = [], [], []  # growing lists
        self.t = 0

    def __call__( self, x ):
        """ f, g = func(x), gradfunc(x); save them; return f, g """
        x = np.asarray_chkfinite( x )  # always
        f = self.func(x)
        g = self.gradfunc(x)
        g = np.asarray_chkfinite( g )
        self.x.append( np.copy(x) )
        self.f.append( _copy( f ))
        self.g.append( np.copy(g) )
        if self.verbose:
            print "%3d:" % self.t ,
            fmt = "%-12g" if np.isscalar(f)  else "%s\t"
            print fmt % f ,
            print "x: %s" % x ,  # with user's np.set_printoptions
            print "\tgrad: %s" % g
                # better df dx dg
        # callback: plot
        self.t += 1
        return f, g

    def restart( self, n ):
        """ x0 = fg.restart( n )  returns x[n] to minimize( fg, x0 )
        """
        x0 = self.x[n]  # minimize from here
        del self.x[:n]
        del self.f[:n]
        del self.g[:n]
        self.t = n
        if self.verbose:
            print "Funcgradmon: restart from x[%d] %s" % (n, x0)
        return x0

    def savez( self, npzfile, **kw ):
        """ np.savez( npzfile, x= f= g= ) """
        x, f, g = map( np.array, [self.x, self.f, self.g] )
        if self.verbose:
            asum = "f: %s \nx: %s \ng: %s" % (
                _asum(f), _asum(x), _asum(g) )
            print "Funcgradmon: saving to %s: \n%s \n" % (npzfile, asum)
        np.savez( npzfile, x=x, f=f, g=g, **kw )

    def load( self, npzfile ):
        load = np.load( npzfile )
        x, f, g = load["x"], load["f"], load["g"]
        if self.verbose:
            asum = "f: %s \nx: %s \ng: %s" % (
                _asum(f), _asum(x), _asum(g) )
            print "Funcgradmon: load %s: \n%s \n" % (npzfile, asum)
        self.x = list( x )
        self.f = list( f )
        self.g = list( g )
        self.loaddict = load
        return self.restart( len(x) - 1 )


def _asum( X ):
    """ one-line array summary: "shape type min av max" """
    if not hasattr( X, "dtype" ):
        return str(X)
    return "%s %s  min av max %.3g %.3g %.3g" % (
            X.shape, X.dtype, X.min(), X.mean(), X.max() )

def _copy( x ):
    return x if x is None  or np.isscalar(x) \
        else np.copy( x )

#...............................................................................
if __name__ == "__main__":
    import sys
    from scipy.optimize import minimize, rosen, rosen_der

    np.set_printoptions( threshold=20, edgeitems=10, linewidth=140,
            formatter = dict( float = lambda x: "%.3g" % x ))  # float arrays %.3g

    dim = 3
    method = "cg"
    maxiter = 10  # 1 linesearch -> 2-3 calls of fg

    # to change these params, run this.py a=1 b=None 'c = ...'  in sh or ipython
    for arg in sys.argv[1:]:
        exec( arg )

    print "\n", 80 * "-"
    print "Funcgradmon: dim %d  method %s  maxiter %d \n" % (
            dim, method, maxiter )
    x0 = np.zeros( dim )

    #...........................................................................
    fg = Funcgradmon( rosen, rosen_der, verbose=1 )
    options = dict( maxiter=maxiter )  # ... 

    min0 = minimize( fg, x0, jac=True, method=method, options=options )
    fg.savez( "0.npz", paramstr="..." )  # to plot or restart

    x0 = fg.restart( 5 )  # = fg.x[5]
    # change params, print them all
    min5 = minimize( fg, x0, jac=True, method=method, options=options )

    fg.savez( "5.npz", paramstr="..." )

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接