将分布拟合到数据:如何惩罚“坏”参数估计?

时间:2013-11-27 17:26:44

标签: python numpy scipy mathematical-optimization

我正在使用scipy's least-squares optimization来使exponentially-modified gaussian distribution符合一组反应时间测量值。一般情况下,它运行良好,但有时候,优化会偏离轨道并为参数选择一个疯狂的值 - 结果图显然不能很好地拟合数据。一般来说,看起来问题是浮点精度误差引起的 - 我们进入0或inf或nan-land。

我正在考虑做两件事:

  • 使用参数同时将CDF和PDF放入数据中;我有两个公式。 (我使用内核密度估计来近似数据中的PDF。)
  • 以某种方式考虑与初始参数估计的距离(由维基百科页面上的矩量法逼近)。这些估计远非完美,但相当不错,似乎避开了“爆炸浮点”问题。

结合PDF和CDF听起来非常简单;误差的大小甚至会大致相同。但是让初始参数适合:我不太确定它是否是一个好主意 - 但如果是:

  • 我如何处理规模差异?我应该将参数“error”规范化为百分比误差吗?
  • 是否有合理的方法来决定数据估算误差和参数“错误”之间的相对权重?

这些甚至是正确的问题吗?是否有普遍认为的“正确”答案,或者“尝试一些东西,直到找到似乎有用的东西”是一种好方法?

一个示例数据集

根据要求,这是一个数据集,此过程不能很好地运行。我知道只有少数样本,数据不符合分布;我仍然希望我能从优化中获得“合理的”结果。

array([ 450.,  560.,  692.,  730.,  758.,  723.,  486.,  596.,  716.,
        695.,  757.,  522.,  535.,  419.,  478.,  666.,  637.,  569.,
        859.,  883.,  551.,  652.,  378.,  801.,  718.,  479.,  544.])

MLE更新

我有一堆问题让我的MLE估计收敛到“合理”值,直到我发现:如果X包含至少一个nan,np.sum(X) == nan当X是一个numpy数组但是不是当X是熊猫系列时。因此,当参数开始超出界限时,对数似然的总和正在做蠢事。

添加了np.asarray()电话,一切都很棒!

1 个答案:

答案 0 :(得分:1)

这应该是评论,但我的空间不足。

我认为最大似然拟合可能是最合适的方法。已经为scipy.stats中的许多发行版实现了ML方法。例如,您可以通过调用scipy.stats.norm.fit找到正态分布的MLE,并以类似的方式找到指数分布的MLE。结合这两个得到的MLE参数应该为Ex-Gaussian ML拟合提供了一个非常好的起始参数。事实上,我会对你的大部分数据进行成像很正常。如果是这种情况,单独的正态分布的ML参数估计应该给你一个非常好的起始参数。

由于Ex-Gaussian只有3个参数,我认为ML拟合根本不会很难。如果您可以提供当前方法无效的数据集,则可以更容易地显示一个真实示例。

好的,你走了:

>>> import scipy.special as sse
>>> import scipy.stats as sss
>>> import scipy.optimize as so
>>> from numpy import *

>>> def eg_pdf(p, x): #defines the PDF
    m=p[0]
    s=p[1]
    l=p[2]
    return 0.5*l*exp(0.5*l*(2*m+l*s*s-2*x))*sse.erfc((m+l*s*s-x)/(sqrt(2)*s))

>>> xo=array([ 450.,  560.,  692.,  730.,  758.,  723.,  486.,  596.,  716.,
        695.,  757.,  522.,  535.,  419.,  478.,  666.,  637.,  569.,
        859.,  883.,  551.,  652.,  378.,  801.,  718.,  479.,  544.])

>>> sss.norm.fit(xo) #get the starting parameter vector form the normal MLE
(624.22222222222217, 132.23977474531389)

>>> def llh(p, f, x): #defines the negative log-likelihood function
    return -sum(log(f(p,x)))

>>> so.fmin(llh, array([624.22222222222217, 132.23977474531389, 1e-6]), (eg_pdf, xo)) #yeah, the data is not good
Warning: Maximum number of function evaluations has been exceeded.
array([  6.14003407e+02,   1.31843250e+02,   9.79425845e-02])

>>> przt=so.fmin(llh, array([624.22222222222217, 132.23977474531389, 1e-6]), (eg_pdf, xo), maxfun=1000) #so, we increase the number of function call uplimit
Optimization terminated successfully.
         Current function value: 170.195924
         Iterations: 376
         Function evaluations: 681

>>> llh(array([624.22222222222217, 132.23977474531389, 1e-6]), eg_pdf, xo)
400.02921290185645
>>> llh(przt, eg_pdf, xo) #quite an improvement over the initial guess
170.19592431051217
>>> przt
array([  6.14007039e+02,   1.31844654e+02,   9.78934519e-02])

此处使用的优化器(fmin或Nelder-Mead单纯形算法)不使用渐变中的任何信息,并且通常比优化器的工作速度慢得多。似乎指数高斯的负对数似然函数的导数可以容易地以紧密形式写出。如果是这样,利用梯度/导数的优化器将是更好和更有效的选择(例如fmin_bfgs)。

要考虑的另一件事是参数约束。根据定义,sigma和lambda必须是指数高斯的正数。您可以使用约束优化器(例如fmin_l_bfgs_b)。或者,您可以优化:

>>> def eg_pdf2(p, x): #defines the PDF
    m=p[0]
    s=exp(p[1])
    l=exp(p[2])
    return 0.5*l*exp(0.5*l*(2*m+l*s*s-2*x))*sse.erfc((m+l*s*s-x)/(sqrt(2)*s))

由于MLE的函数不变性,此函数的MLE应与原始eg_pdf的MLE相同。除了exp()之外,还可以使用其他转换将(-inf, +inf)投影到(0, +inf)

您还可以考虑http://en.wikipedia.org/wiki/Lagrange_multiplier