scipy.optimize.leastsq со связанными ограничениями - PullRequest
22 голосов
/ 26 марта 2012

Я ищу подпрограмму оптимизации внутри scipy / numpy, которая могла бы решить нелинейную задачу типа наименьших квадратов (например, подгонка параметрической функции к большому набору данных), но включающая в себя границы и ограничения (например, минимумы и максимумы для параметры для оптимизации). В настоящее время я использую версию mpfit для python (перевод с idl ...): это явно не оптимально, хотя работает очень хорошо.

Эффективная подпрограмма в python / scipy / etc может быть замечательной! Любой вклад здесь приветствуется: -)

спасибо!

Ответы [ 4 ]

21 голосов
/ 27 марта 2012

scipy.optimize.least_squares в scipy 0.17 (январь 2016 г.) обрабатывает границы; используйте это, а не этот хак.


Связанные ограничения можно легко сделать квадратичными, и минимизируется leastsq вместе с остальными.
Скажем, вы хотите минимизировать сумму в 10 квадратов и сигма; f_i (р) ^ 2, так что ваш func (p) является 10-вектором [f0 (p) ... f9 (p)],
а также хочу 0 <= p_i <= 1 для 3 параметров. <br> Рассмотрим «функцию ванны» max (- p, 0, p - 1), это 0 внутри 0 .. 1 и положительное снаружи, как \ _____ / ванна.
Если мы дадим leastsq 13-длинный вектор

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

с w = скажем 100, это минимизирует сумму квадратов лота: ванны будут ограничивать 0 <= p <= 1. Генерал lo <= p <= hi похож. <br> Следующий код является просто оболочкой, которая запускает leastsq например, с такой вектор длиной 13 для минимизации.

# 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
9 голосов
/ 27 марта 2012

scipy имеет несколько ограниченных процедур оптимизации в scipy.optimize. Вариант с наименьшими квадратами с ограничениями: scipy.optimize.fmin_slsqp

4 голосов
/ 06 января 2016

Возможность решения нелинейной задачи наименьших квадратов с границами оптимальным способом, как это делает mpfit, давно отсутствовала в Scipy.

Эта столь востребованная функциональность была наконец введена в Scipy 0.17 с новой функцией scipy.optimize.least_squares .

Эта новая функция может использовать правильный алгоритм области доверияиметь дело с ограниченными ограничениями и оптимально использует природу суммы квадратов нелинейной функции для оптимизации.

Примечания:

Решение, предложенное @У Дениса есть главная проблема введения прерывистой «функции ванны».Это делает оптимизацию scipy.optimize.leastsq, разработанную для гладких функций, очень неэффективной и, возможно, нестабильной при пересечении границы.

Использование scipy.optimize.minimize с method='SLSQP' (как предложено @f_ficarola) или scipy.optimize.fmin_slsqp (как предложено @matt), основная проблема состоит в том, чтобы не использовать природу суммы квадратов минимизируемой функции.Обе эти функции предназначены для минимизации скалярных функций (верно также для fmin_slsqp, несмотря на вводящее в заблуждение имя).Эти подходы менее эффективны и менее точны, чем правильный.

3 голосов
/ 28 марта 2012

Посмотрите на: http://lmfit.github.io/lmfit-py/, это должно решить вашу проблему.

...