我刚才问过this question。我不确定是否应将此作为答案或新问题发布。我没有答案,但我通过在R中使用nls.lm
应用Levenberg-Marquardt算法“解决”了这个问题,当解决方案在边界时,我运行信任区域反射算法(TRR,实现)在R)离开它。现在我有了新的问题。
根据我的经验,以这种方式,程序达到最佳状态,并且对起始值不那么敏感。但这只是一种实用的方法,可以摆脱我遇到的问题nls.lm
以及R中的其他优化函数。我想知道为什么nls.lm
以这种方式表现为边界约束的优化问题和在实践中使用nls.lm
时如何处理边界约束。
以下我举例说明了使用nls.lm
的两个问题。
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)
描述模型/系统的微分方程是:
"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.lm
和lsqnonlin
获得相同的结果。我有很多这样的情况。
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的基本优化算法更稳定并且达到了更好的解决方案。
答案 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代码可以减轻人们对更复杂模型的速度问题。