2012-07-03 61 views
0

我正在爲基於scipy的optimize.leastsq的2D數據編寫一個自動曲線擬合例程,它可以工作。然而,當添加很多曲線時,我會得到非物理結果(例如負振幅)。根據MINUIT的Python參數轉換

我發現此帖子Scipy: bounds for fitting parameter(s) when using optimize.leastsq並試圖根據Cern的Minuit使用參數轉換。在上面提到的問題中,有人提供了一些鏈接到一些python代碼。

code.google.com/p/nmrglue/source/browse/trunk/nmrglue/analysis/leastsqbound.py 

我寫這個最小工作示例(擴展代碼)

""" 
http://code.google.com/p/nmrglue/source/browse/trunk/nmrglue/analysis/leastsqbound.py 
Constrained multivariate Levenberg-Marquardt optimization 
""" 

from scipy.optimize import leastsq 
import numpy as np 
import matplotlib.pyplot as plt #new 

def internal2external_grad(xi, bounds): 
    """ 
    Calculate the internal to external gradiant 

    Calculates the partial of external over internal 

    """ 

    ge = np.empty_like(xi) 

    for i, (v, bound) in enumerate(zip(xi, bounds)): 

     a = bound[0] # minimum 
     b = bound[1] # maximum 

     if a == None and b == None: # No constraints 
      ge[i] = 1.0 

     elif b == None:  # only min 
      ge[i] = v/np.sqrt(v ** 2 + 1) 

     elif a == None:  # only max 
      ge[i] = -v/np.sqrt(v ** 2 + 1) 

     else:  # both min and max 
      ge[i] = (b - a) * np.cos(v)/2. 

    return ge 

def i2e_cov_x(xi, bounds, cov_x): 

    grad = internal2external_grad(xi, bounds) 
    grad = grad = np.atleast_2d(grad) 
    return np.dot(grad.T, grad) * cov_x 


def internal2external(xi, bounds): 
    """ Convert a series of internal variables to external variables""" 

    xe = np.empty_like(xi) 

    for i, (v, bound) in enumerate(zip(xi, bounds)): 

     a = bound[0] # minimum 
     b = bound[1] # maximum 

     if a == None and b == None: # No constraints 
      xe[i] = v 

     elif b == None:  # only min 
      xe[i] = a - 1. + np.sqrt(v ** 2. + 1.) 

     elif a == None:  # only max 
      xe[i] = b + 1. - np.sqrt(v ** 2. + 1.) 

     else:  # both min and max 
      xe[i] = a + ((b - a)/2.) * (np.sin(v) + 1.) 

    return xe 

def external2internal(xe, bounds): 
    """ Convert a series of external variables to internal variables""" 

    xi = np.empty_like(xe) 

    for i, (v, bound) in enumerate(zip(xe, bounds)): 

     a = bound[0] # minimum 
     b = bound[1] # maximum 

     if a == None and b == None: # No constraints 
      xi[i] = v 

     elif b == None:  # only min 
      xi[i] = np.sqrt((v - a + 1.) ** 2. - 1) 

     elif a == None:  # only max 
      xi[i] = np.sqrt((b - v + 1.) ** 2. - 1) 

     else: # both min and max 
      xi[i] = np.arcsin((2.*(v - a)/(b - a)) - 1.) 

    return xi 

def err(p, bounds, efunc, args): 

    pe = internal2external(p, bounds) # convert to external variables 
    return efunc(pe, *args) 

def calc_cov_x(infodic, p): 
    """ 
    Calculate cov_x from fjac, ipvt and p as is done in leastsq 
    """ 

    fjac = infodic['fjac'] 
    ipvt = infodic['ipvt'] 
    n = len(p) 

    # adapted from leastsq function in scipy/optimize/minpack.py 
    perm = np.take(np.eye(n), ipvt - 1, 0) 
    r = np.triu(np.transpose(fjac)[:n, :]) 
    R = np.dot(r, perm) 
    try: 
     cov_x = np.linalg.inv(np.dot(np.transpose(R), R)) 
    except LinAlgError: 
     cov_x = None 
    return cov_x 


def leastsqbound(func, x0, bounds, args =(), **kw): 
    """ 
    Constrained multivariant Levenberg-Marquard optimization 

    Minimize the sum of squares of a given function using the 
    Levenberg-Marquard algorithm. Contraints on parameters are inforced using 
    variable transformations as described in the MINUIT User's Guide by 
    Fred James and Matthias Winkler. 

    Parameters: 

    * func  functions to call for optimization. 
    * x0  Starting estimate for the minimization. 
    * bounds (min,max) pair for each element of x, defining the bounds on 
       that parameter. Use None for one of min or max when there is 
       no bound in that direction. 
    * args  Any extra arguments to func are places in this tuple. 

    Returns: (x,{cov_x,infodict,mesg},ier) 

    Return is described in the scipy.optimize.leastsq function. x and con_v 
    are corrected to take into account the parameter transformation, infodic 
    is not corrected. 

    Additional keyword arguments are passed directly to the 
    scipy.optimize.leastsq algorithm. 

    """ 
    # check for full output 
    if "full_output" in kw and kw["full_output"]: 
     full = True 
    else: 
     full = False 

    # convert x0 to internal variables 
    i0 = external2internal(x0, bounds) 

    # perfrom unconstrained optimization using internal variables 
    r = leastsq(err, i0, args = (bounds, func, args), **kw) 

    # unpack return convert to external variables and return 
    if full: 
     xi, cov_xi, infodic, mesg, ier = r 
     xe = internal2external(xi, bounds) 
     cov_xe = i2e_cov_x(xi, bounds, cov_xi) 
     # XXX correct infodic 'fjac','ipvt', and 'qtf' 
     return xe, cov_xe, infodic, mesg, ier 

    else: 
     xi, ier = r 
     xe = internal2external(xi, bounds) 
     return xe, ier 


# new below 

def _evaluate(x, p): 
    ''' 
    Linear plus Lorentzian curve 
    p = list with three parameters ([a, b, I, Pos, FWHM]) 
    ''' 
    return p[0] + p[1] * x + p[2]/(1 + np.power((x - p[3])/(p[4]/2), 2)) 


def residuals(p, y, x): 

    err = _evaluate(x, p) - y 
    return err 


if __name__ == '__main__': 
    data = np.loadtxt('constraint.dat') # read data 

    p0 = [5000., 0., 500., 2450., 3] #Start values for a, b, I, Pos, FWHM 
    constraints = [(4000., None), (-50., 20.), (0., 2000.), (2400., 2451.), (None, None)] 

    p, res = leastsqbound(residuals, p0, constraints, args = (data[:, 1], data[:, 0]), maxfev = 20000) 
    print p, res 

    plt.plot(data[:, 0], data[:, 1]) # plot data 
    plt.plot(data[:, 0], _evaluate(data[:, 0], p0)) # plot start values 
    plt.plot(data[:, 0], _evaluate(data[:, 0], p)) # plot fit values 
    plt.show() 

那情節輸出,其中綠色是啓動條件和紅色的擬合結果: plot results

這是正確的用法? External2internal轉換隻是在邊界外拋出一個nan。 leastsq似乎能夠處理這個問題?

我上傳了擬合數據here。只需粘貼到名爲constraint.dat的文本文件中即可。

回答

0

已經有一個現有的流行約束列弗-MAR代碼

http://adsabs.harvard.edu/abs/2009ASPC..411..251M

與實施蟒蛇

http://code.google.com/p/astrolibpy/source/browse/mpfit/mpfit.py

我建議不要推倒重來。

+0

謝謝你指出這一點。我之前沒有找到任何有關mpfit的參考,它看起來正是我試圖達到的目標。但是,一個最小的工作例子會是怎樣的呢?用法與scipy的leastsq不同。看起來x和y值沒有通過,而是參考?mpfit.py中的例子也不起作用。 – user334287

+0

下面是一個例子,它絕對有效: http://code.google.com/p/astrolibpy/source/browse/mpfit/tests/test_mpfit.py –

+0

我用你鏈接的testfile的第一個例子來獲取解決方案在運行。我最初有點困惑,因爲他們使用起始值_p0 = [1,1] _但在參數字典_parinfo_中他們使用3.2和1.78。另外,_p0_實際上在通話中不是必需的。 – user334287

0

繼sega_sai的答案,我使用mpfit.py

import matplotlib.pyplot as plt 
from mpfit import mpfit 
import numpy as np 

def _evaluate(p, x): 
    ''' 
    Linear plus Lorentzian curve 
    p = list with three parameters ([a, b, I, Pos, FWHM]) 
    ''' 
    return p[0] + p[1] * x + p[2]/(1 + np.power((x - p[3])/(p[4]/2), 2)) 

def residuals(p, fjac = None, x = None, y = None, err = None): 
    status = 0 
    error = _evaluate(p, x) - y 
    return [status, error/err] 

if __name__ == '__main__': 
    data = np.loadtxt('constraint.dat') # read data 
    x = data[:, 0] 
    y = data[:, 1] 
    err = 0 * np.ones(y.shape, dtype = 'float64') 
    parinfo = [{'value':5000., 'fixed':0, 'limited':[0, 0], 'limits':[0., 0.], 'parname':'a'}, 
       {'value':0., 'fixed':0, 'limited':[0, 0], 'limits':[0., 0.], 'parname':'b'}, 
       {'value':500., 'fixed':0, 'limited':[0, 0], 'limits':[0., 0.], 'parname':'I'}, 
       {'value':2450., 'fixed':0, 'limited':[0, 0], 'limits':[0., 0.], 'parname':'Pos'}, 
       {'value':3., 'fixed':0, 'limited':[0, 0], 'limits':[0., 0.], 'parname':'FWHM'}] 
    fa = {'x':x, 'y':y, 'err':err} 
    m = mpfit(residuals, parinfo = parinfo, functkw = fa) 
    print m 

想出了這個最小的工作示例的擬合結果是:

mpfit.py: 3714.97545, 0.484193283, 2644.47271, 2440.13385, 22.1898496 
leastsq: 3714.97187, 0.484194545, 2644.46890, 2440.13391, 22.1899295 

所以結論:這兩種方法的工作,都允許的約束。但是,因爲mpfit來自非常成熟的來源,我更相信它。如果可用的話,它也承認錯誤值。

0

嘗試lmfit-PY - https://github.com/newville/lmfit-py

  1. 它還通過scipy.optimize.leastsq使用列文伯格 - 馬夸特(LM)算法。不確定性是可以的。

  2. 它允許您不僅限制您的擬合參數的界限,而且還可以在它們之間使用數學表達式而不必修改您的擬合函數。

  3. 忘記在擬合函數中使用那些可怕的p [0],p [1] ...。只需通過Parameters類使用擬合參數的名稱即可。