如何为nls函数找到良好的起始值?

时间:2012-03-13 21:45:58

标签: r ggplot2 nls

我不明白为什么我不能拥有这些数据的nls函数。 我尝试了很多不同的起始值,但我总是犯同样的错误。

以下是我一直在做的事情:

expFct2 = function (x, a, b,c)
{
  a*(1-exp(-x/b)) + c  
}
vec_x <- c(77.87,87.76,68.6,66.29)
vec_y <- c(1,1,0.8,0.6)
dt <- data.frame(vec_x=vec_x,vec_y=vec_y)
ggplot(data = dt,aes(x = vec_x, y = vec_y)) +  geom_point() + 
     geom_smooth(data=dt, method="nls", formula=y~expFct2(x, a, b, c),
       se=F, start=list(a=1, b=75, c=-5)

我一直都有这个错误:

Error in method(formula, data = data, weights = weight, ...) : 
  singular gradient

3 个答案:

答案 0 :(得分:9)

在任何情况下,将三参数非线性模型拟合到四个数据点将具有中等挑战性,尽管在这种情况下数据表现良好。第1点是你的c参数(-5)的起始值偏离了。绘制与您的起始参数相对应的曲线图片(见下文)将有助于您理解这一点(因此会认识到您获得的曲线范围从最小c到最大c+a,并且您的数据范围是0.6到1 ......)

然而,即使有更好的起始猜测,我发现自己也在忙于控制参数(即control=nls.control(maxiter=200)),然后发出更多警告 - nls因其稳健性而闻名。所以我尝试了SSasympOff模型,它实现了你想要适合的曲线的自启动版本。

start1 <- list(a=1, b=75, c=-5)
start2 <- list(a=0.5, b=75, c=0.5)  ## a better guess

pfun <- function(params) {
  data.frame(vec_x=60:90,
             vec_y=do.call(expFct2,c(list(x=60:90),params)))
}
library(ggplot2)
ggplot(data = dt,aes(x = vec_x, y = vec_y)) +  geom_point() +
  geom_line(data=pfun(start1))+
  geom_line(data=pfun(start2),colour="red")+
  geom_smooth(data=dt, method="nls", formula=y~SSasympOff(x, a, b, c),
              se=FALSE)

我的一般建议是,如果您在nls之外的geom_smooth 以及构建要添加的曲线,则更容易找出正在发生的事情并解决问题使用predict.nls ...

更一般地说,获得良好起始参数的方法是了解拟合函数的几何形状,以及哪些参数控制曲线的哪些方面。如上所述,c是移位饱和指数曲线的最小值,a是范围,b是比例参数(您可以看到{{1} },曲线是x=b或从最小值到最大值的大约2/3。无论是一些代数和微积分(即限制),还是使用1-exp(-1)函数,都是收集这些信息的好方法。

答案 1 :(得分:8)

这可以使用两个线性参数(.lin1.lin2)和一个非线性参数(b)来编写,如下所示:

a*(1-exp(-x/b)) + c  
= (a+c) - a * exp(-x/b)
= .lin1 + .lin2 * exp(-x/b)

其中.lin1 = a+c.lin2 = -a(所以a = - .lin2c = .lin1 + .lin2)这让我们可以使用"plinear",它只需要指定单个非线性的起始值参数(消除了如何设置其他参数的起始值的问题),并且尽管b=75的起始值远离解决方案的起始值,但会收敛:

nls(y ~ cbind(1, exp(-x/b)), start = list(b = 75), alg = "plinear")

以下是运行的结果,我们可以从.lin2的大小看出问题严重缩放:

> x <- c(77.87,87.76,68.6,66.29)
> y <- c(1,1,0.8,0.6)
> nls(y ~ cbind(1, exp(-x/b)), start = list(b = 75), alg = "plinear")
Nonlinear regression model
  model:  y ~ cbind(1, exp(-x/b)) 
   data:  parent.frame() 
         b      .lin1      .lin2 
 3.351e+00  1.006e+00 -1.589e+08 
 residual sum-of-squares: 7.909e-05

Number of iterations to convergence: 9 
Achieved convergence tolerance: 9.887e-07 
> R.version.string
[1] "R version 2.14.2 Patched (2012-02-29 r58660)"
> win.version()
[1] "Windows Vista (build 6002) Service Pack 2"

编辑:添加了示例运行和对缩放的评论。

答案 2 :(得分:2)

我很难找到对你的参数的解释: a是斜率,b是收敛速度,a + c是极限, 但c本身似乎并不意味着什么。 重新参数化后,问题就会消失。

f <- function (x, a,b,c) a + c * exp(-x/abs(b))
nls(y~f(x, a, b, c), data=dt, start=list(a=1, b=75, c=-5), trace=TRUE)

但是,c的值看起来非常非常高: 这可能是该模型最初未能收敛的原因。

Nonlinear regression model
  model:  y ~ f(x, a, b, c) 
   data:  dt 
         a          b          c 
 1.006e+00  3.351e+00 -1.589e+08 
 residual sum-of-squares: 7.909e-05

Number of iterations to convergence: 9 
Achieved convergence tolerance: 2.232e-06 

这是同一函数的另一个更合理的参数化。

g <- function (x, a,b,c) a * (1-exp(-(x-c)/abs(b)))
nls(y~g(x, a, b, c), data=dt, start=list(a=1, b=75, c=-5), trace=TRUE)

Nonlinear regression model
  model:  y ~ g(x, a, b, c) 
   data:  dt 
     a      b      c 
 1.006  3.351 63.257 
 residual sum-of-squares: 7.909e-05

Number of iterations to convergence: 10 
Achieved convergence tolerance: 1.782e-06