scipy.optimiser.leastsq avec des contraintes liées

je cherche une routine d'optimisation au sein de scipy / numpy qui pourrait résoudre un problème non linéaire de type moindres carrés (par exemple, ajustement d'une fonction paramétrique à un grand ensemble de données) mais incluant des limites et des contraintes (par exemple, minima et maxima pour les paramètres à optimiser). En ce moment, j'utilise la version python de mpfit (traduit de idl...): ce n'est évidemment pas optimal bien que cela fonctionne très bien.

une routine efficace en python/scipy/etc pourrait être ont ! Toute contribution est la bienvenue ici :-)

merci!

22
demandé sur DrSAR 2012-03-26 23:38:35

4 réponses

scipy.optimiser.least_squares dans scipy 0.17 (janvier 2016) poignées de limites; l'utiliser, pas ce hack.


les contraintes liées peuvent facilement être quadratiques, et minimisé par leastsq avec le reste.

Disons que vous voulez minimiser une somme de 10 carrés Σ f_i (p)^2, donc votre func(p) est un vecteur 10 [f0 (p) ... f9 (p)],

et aussi vouloir 0 <= p_i <= 1 pour 3 paramètres.

Considérons la" fonction tubulaire " max( - p, 0, p-1 ), qui est 0 à 0 .. 1 et positif à l'extérieur, comme un \_____/ baignoire.

Si nous donnons leastsq le vecteur de 13-long

[ f0(p), f1(p), ... f9(p), w*tub(p0), w*tub(p1), w*tub(p2) ]

avec w = say 100, il minimisera la somme des carrés du lot: les bacs restreindront 0 <= p < = 1. En général, lo <= p < = hi est similaire.

Le code suivant est juste un wrapper qui fonctionne leastsq avec par exemple un tel vecteur de 13-long à minimiser.

# leastsq_bounds.py
# see also test_leastsq_bounds.py on gist.github.com/denis-bz

from __future__ import division
import numpy as np
from scipy.optimize import leastsq

__version__ = "2015-01-10 jan  denis"  # orig 2012


#...............................................................................
def leastsq_bounds( func, x0, bounds, boundsweight=10, **kwargs ):
    """ leastsq with bound conatraints lo <= p <= hi
    run leastsq with additional constraints to minimize the sum of squares of
        [func(p) ...]
        + boundsweight * [max( lo_i - p_i, 0, p_i - hi_i ) ...]

    Parameters
    ----------
    func() : a list of function of parameters `p`, [err0 err1 ...]
    bounds : an n x 2 list or array `[[lo_0,hi_0], [lo_1, hi_1] ...]`.
        Use e.g. [0, inf]; do not use NaNs.
        A bound e.g. [2,2] pins that x_j == 2.
    boundsweight : weights the bounds constraints
    kwargs : keyword args passed on to leastsq

    Returns
    -------
    exactly as for leastsq,
http://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.leastsq.html

    Notes
    -----
    The bounds may not be met if boundsweight is too small;
    check that with e.g. check_bounds( p, bounds ) below.

    To access `x` in `func(p)`, `def func( p, x=xouter )`
    or make it global, or `self.x` in a class.

    There are quite a few methods for box constraints;
    you'll maybe sing a longer song ...
    Comments are welcome, test cases most welcome.

"""
    # Example: test_leastsq_bounds.py

    if bounds is not None  and  boundsweight > 0:
        check_bounds( x0, bounds )
        if "args" in kwargs:  # 8jan 2015
            args = kwargs["args"]
            del kwargs["args"]
        else:
            args = ()
#...............................................................................
        funcbox = lambda p: \
            np.hstack(( func( p, *args ),
                        _inbox( p, bounds, boundsweight ))) 
    else:
        funcbox = func
    return leastsq( funcbox, x0, **kwargs )


def _inbox( X, box, weight=1 ):
    """ -> [tub( Xj, loj, hij ) ... ]
        all 0  <=>  X in box, lo <= X <= hi
    """
    assert len(X) == len(box), \
        "len X %d != len box %d" % (len(X), len(box))
    return weight * np.array([
        np.fmax( lo - x, 0 ) + np.fmax( 0, x - hi )
            for x, (lo,hi) in zip( X, box )])

# def tub( x, lo, hi ):
#     """ \___/  down to lo, 0 lo .. hi, up from hi """
#     return np.fmax( lo - x, 0 ) + np.fmax( 0, x - hi )

#...............................................................................
def check_bounds( X, box ):
    """ print Xj not in box, loj <= Xj <= hij
        return nr not in
    """
    nX, nbox = len(X), len(box)
    assert nX == nbox, \
        "len X %d != len box %d" % (nX, nbox)
    nnotin = 0
    for j, x, (lo,hi) in zip( range(nX), X, box ):
        if not (lo <= x <= hi):
            print "check_bounds: x[%d] %g is not in box %g .. %g" % (j, x, lo, hi)
            nnotin += 1
    return nnotin
20
répondu denis 2016-01-23 17:31:31

scipy a plusieurs routines d'optimisation contraintes dans scipy.optimiser. La variante des moindres carrés contraints est scipy.optimiser.fmin_slsqp

9
répondu matt 2012-03-26 21:07:46

la capacité de résoudre le problème des moindres carrés non linéaires avec des limites, d'une manière optimale comme le fait mpfit, a longtemps été absent de Scipy.

cette fonctionnalité très demandée a finalement été introduite dans Scipy 0.17, avec la nouvelle fonction scipy.optimiser.least_squares.

cette nouvelle fonction peut utiliser un algorithme de région de confiance approprié pour traiter les contraintes liées, et fait un usage optimal de la nature de la somme des carrés de la fonction non linéaire pour optimiser.

Notes:

scipy.optimize.leastsq optimisation, conçu pour des fonctions lisses, très inefficace, et peut-être instable, lorsque la frontière est franchie.

scipy.optimize.minimizescipy.optimize.fmin_slsqp (comme @matt l'a suggéré), ont le problème majeur de ne pas faire usage de la nature de somme-of-square de la fonction à minimiser. Ces fonctions sont toutes deux conçues pour minimiser les fonctions scalaires (vrai aussi pour fmin_slsqp, malgré le nom trompeur). Ces approches sont moins efficaces et moins précises qu'une approche appropriée.

4
répondu divenex 2016-02-24 12:46:34

jetez un coup d'oeil à: http://lmfit.github.io/lmfit-py/, il devrait résoudre votre problème.

3
répondu tillsten 2017-10-31 13:38:58