在R中使用`nls.lm`时如何处理边界约束

时间:2013-06-11 09:47:26

标签: r mathematical-optimization nls

我刚才问过this question。我不确定是否应将此作为答案或新问题发布。我没有答案,但我通过在R中使用nls.lm应用Levenberg-Marquardt算法“解决”了这个问题,当解决方案在边界时,我运行信任区域反射算法(TRR,实现)在R)离开它。现在我有了新的问题。

根据我的经验,以这种方式,程序达到最佳状态,并且对起始值不那么敏感。但这只是一种实用的方法,可以摆脱我遇到的问题nls.lm以及R中的其他优化函数。我想知道为什么nls.lm以这种方式表现为边界约束的优化问题和在实践中使用nls.lm时如何处理边界约束。

以下我举例说明了使用nls.lm的两个问题。

  1. 对起始值很敏感。
  2. 当某个参数到达边界时停止。
  3. 可重复的示例:焦点数据集D

    library(devtools)
    install_github("KineticEval","zhenglei-gao")
    library(KineticEval)
    data(FOCUS2006D)
    km <- mkinmod.full(parent=list(type="SFO",M0 = list(ini   = 0.1,fixed = 0,lower = 0.0,upper =Inf),to="m1"),m1=list(type="SFO"),data=FOCUS2006D)
    system.time(Fit.TRR <- KinEval(km,evalMethod = 'NLLS',optimMethod = 'TRR'))
    system.time(Fit.LM <- KinEval(km,evalMethod = 'NLLS',optimMethod = 'LM',ctr=kingui.control(runTRR=FALSE)))
    compare_multi_kinmod(km,rbind(Fit.TRR$par,Fit.LM$par))
    dev.print(jpeg,"LMvsTRR.jpeg",width=480)
    

    LM fit vs TRR fit

    描述模型/系统的微分方程是:

    "d_parent = - k_parent * parent"                                                 
    "d_m1 = - k_m1 * m1 + k_parent * f_parent_to_m1 * parent" 
    

    左边的图是带初始值的模型,中间是使用“TRR”的拟合模型(类似于Matlab lsqnonlin函数中的算法),右边是拟合模型使用带有nls.lm的“LM”。查看拟合参数(Fit.LM$par),您会发现一个拟合参数(f_parent_to_m1)位于边界1。如果我将一个参数M0_parent的起始值从0.1更改为100,那么我使用nls.lmlsqnonlin获得相同的结果。我有很多这样的情况。

    newpars <- rbind(Fit.TRR$par,Fit.LM$par)
    rownames(newpars)<- c("TRR(lsqnonlin)","LM(nls.lm)")
    newpars
    
                       M0_parent   k_parent        k_m1 f_parent_to_m1
    TRR(lsqnonlin)  99.59848 0.09869773 0.005260654       0.514476
    LM(nls.lm)      84.79150 0.06352110 0.014783294       1.000000
    

    除了上述问题之外,经常发生的nls.lm返回的Hessian不可逆(特别是当某些参数在边界上时),因此我无法估计协方差矩阵。另一方面,“TRR”算法(在Matlab中)几乎总是通过计算解点处的雅可比行列来进行估计。我认为这很有用,但我也确定R优化算法(我试过的算法)没有这样做是有原因的。我想通过使用Matlab计算协方差矩阵来获得参数估计的标准误差来了解我是否错了。

    最后一点,我在previous post中声称Matlab lsqnonlin几乎在所有情况下都优于R的优化功能。我错了。如上所示,如果在R中实现,Matlab中使用的“Trust-Region-Reflective”算法实际上更慢(有时慢得多)。然而,它仍然比R的基本优化算法更稳定并且达到了更好的解决方案。

2 个答案:

答案 0 :(得分:1)

首先,我不是Matlab和Optimization的专家,从未使用过R。

我不确定我看到你的实际问题是什么,但也许我可以解释你的困惑:

LM略微增强Gauß-Newton方法 - 对于几个局部最小值的问题,它对初始状态非常敏感。包含边界通常会产生更多的最小值。

TRR类似于LM,但更强大。它具有更好的“跳出”糟糕的局部最小值的能力。它比LM更能表现得更好,但表现更差。实际上解释为什么很难。您需要详细研究算法并查看它们在这种情况下的行为。

我无法解释Matlab和R的实现之间的区别,但是TRR有几个扩展可能是Matlab使用而R没有。 您使用LM和TRR的方法是否比TRR单独交替收敛?

答案 1 :(得分:0)

使用mkin软件包,您可以使用“端口”算法找到参数(据我所知,它也是一种TRR算法),或者使用nls的“Marq”算法.lm在后台。然后你可以使用“正常”的起始值或“坏”的起始值。

library(mkin)
packageVersion("mkin")

最近的mkin版本可以大大加快这个过程,因为如果你的系统上有一个编译器可以从自动生成的C代码编译模型(例如你在Debian / Ubuntu上安装了r-base-dev,或者在Windows上安装了Rtools)

这定义了模型:

m <- mkinmod(parent = mkinsub("SFO", "m1"),
             m1 = mkinsub("SFO"),
             use_of_ff = "max")

您可以检查微分方程是否正确:

cat(m$diffs, sep = "\n")

然后我们拟合四种变体,Port和LM,有或没有M0固定为0.1:

f.Port = mkinfit(m, FOCUS_2006_D)
f.Port.M0 = mkinfit(m, FOCUS_2006_D, state.ini = c(parent = 0.1, m1 = 0))
f.LM = mkinfit(m, FOCUS_2006_D, method.modFit = "Marq")
f.LM.M0 = mkinfit(m, FOCUS_2006_D, state.ini = c(parent = 0.1, m1 = 0),
               method.modFit = "Marq")

然后我们看一下结果:

results <- sapply(list(Port = f.Port, Port.M0 = f.Port.M0, LM = f.LM, LM.M0 = f.LM.M0),
  function(x) round(summary(x)$bpar[, "Estimate"], 5))

                   Port  Port.M0       LM    LM.M0
parent_0       99.59848 99.59848 99.59848 39.52278
k_parent        0.09870  0.09870  0.09870  0.00000
k_m1            0.00526  0.00526  0.00526  0.00000
f_parent_to_m1  0.51448  0.51448  0.51448  1.00000

所以我们可以看到Port算法找到了最好的解决方案(就我所知),即使启动值很差。使用自动生成C代码可以减轻人们对更复杂模型的速度问题。