mathematical - scipy.optimize python
scipy.optimize.leastsq con restricciones vinculadas (4)
Eche un vistazo a: http://lmfit.github.io/lmfit-py/ , debería resolver su problema.
Estoy buscando una rutina de optimización dentro de scipy / numpy que podría resolver un problema de tipo no lineal de mínimos cuadrados (por ejemplo, ajustar una función paramétrica a un gran conjunto de datos) pero incluyendo límites y restricciones (por ejemplo, mínimos y máximos para los parámetros) optimizado). En este momento estoy usando la versión python de mpfit (traducida de idl ...): esto claramente no es óptimo aunque funciona muy bien.
Una rutina eficiente en python / scipy / etc podría ser genial tenerla! Cualquier entrada es muy bienvenida aquí :-)
¡Gracias!
La capacidad de resolver problemas de mínimos cuadrados no lineales con límites, de una manera óptima como lo hace mpfit, hace tiempo que falta en Scipy.
Esta funcionalidad tan solicitada finalmente se introdujo en Scipy 0.17, con la nueva función scipy.optimize.least_squares .
Esta nueva función puede usar un algoritmo de región de confianza adecuado para manejar restricciones limitadas, y hace un uso óptimo de la naturaleza de suma de cuadrados de la función no lineal para optimizar.
Notas:
La solución propuesta por @denis tiene el problema principal de introducir una "función de tina" discontinua. Esto hace que la optimización scipy.optimize.leastsq
, diseñada para funciones suaves, muy ineficiente y posiblemente inestable, cuando se cruza el límite.
El uso de scipy.optimize.minimize
con method=''SLSQP''
(como @f_ficarola sugerido) o scipy.optimize.fmin_slsqp
(como @matt sugerido), tiene el problema principal de no hacer uso de la naturaleza de suma de cuadrados de la función a ser minimizada. Estas funciones están diseñadas para minimizar las funciones escalares (también para fmin_slsqp, a pesar del nombre engañoso). Estos enfoques son menos eficientes y menos precisos de lo que uno puede ser.
scipy tiene varias rutinas de optimización restringidas en scipy.optimize. La variante de mínimos cuadrados restringidos es scipy.optimize.fmin_slsqp
scipy.optimize.least_squares en scipy 0.17 (enero de 2016) maneja los límites; usa eso, no este truco.
Las limitaciones de límites pueden ser fácilmente cuadráticas y minimizadas por leastsq junto con el resto.
Digamos que quiere minimizar una suma de 10 cuadrados Σ f_i (p) ^ 2, entonces su func (p) es un 10-vector [f0 (p) ... f9 (p)],
y también quiere 0 <= p_i <= 1 para 3 parámetros.
Considere la "función de la cuba" máxima (- p, 0, p - 1), que es 0 dentro de 0 .. 1 y positiva al exterior, como una / _____ / tina.
Si le damos leastsq
el vector de 13 largos
[ f0(p), f1(p), ... f9(p), w*tub(p0), w*tub(p1), w*tub(p2) ]
con w = digamos 100, minimizará la suma de cuadrados del lote: las bañeras limitarán 0 <= p <= 1. General lo <= p <= hi es similar.
El siguiente código es simplemente un contenedor que ejecuta leastsq
con, por ejemplo, un vector de 13 de largo para minimizar.
# 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