为什么 nls() 给我“初始参数估计时的奇异梯度矩阵”错误?

机器算法验证 r 自习 非线性回归 nls 起始值
2022-02-12 04:52:52

我有一些关于减排和每辆车成本的基本数据:

q24 <- read.table(text = "reductions  cost.per.car
    50  45
    55  55
    60  62
    65  70
    70  80
    75  90
    80  100
    85  200
    90  375
    95  600
    ",header = TRUE, sep = "")

我知道这是一个指数函数,所以我希望能够找到一个符合以下条件的模型:

    model <- nls(cost.per.car ~ a * exp(b * reductions) + c, 
         data = q24, 
         start = list(a=1, b=1, c=0))

但我收到一个错误:

Error in nlsModel(formula, mf, start, wts) : 
  singular gradient matrix at initial parameter estimates

我已经阅读了大量关于我所看到的错误的问题,并且我正在收集问题可能是我需要更好/不同start的值(这initial parameter estimates更有意义)但我不确定,鉴于我拥有的数据,我将如何去估计更好的参数。

3个回答

自动为非线性模型找到好的起始值是一门艺术。(当您可以绘制数据并在视觉上做出一些好的猜测时,一次性数据集相对容易。)一种方法是线性化模型并使用最小二乘估计。

在这种情况下,模型具有形式

E(Y)=aexp(bx)+c

对于未知参数a,b,c. 指数的存在鼓励我们使用对数——但是加上c很难做到这一点。但请注意,如果a是正面的c将小于的最小期望值Y--因此可能比最小的观察值小一点Y. (如果a可能是负数,您还必须考虑c比最大观察值略大Y.)

那么,让我们照顾c通过用作初始估计c0大约是观测值最小值的一半yi. 现在可以在没有那个棘手的附加项的情况下将模型重写为

E(Y)c0aexp(bx).

我们可以记录以下内容:

log(E(Y)c0)log(a)+bx.

这是模型的线性近似。两个都log(a)b可以用最小二乘估计。

这是修改后的代码:

c.0 <- min(q24$cost.per.car) * 0.5
model.0 <- lm(log(cost.per.car - c.0) ~ reductions, data=q24)
start <- list(a=exp(coef(model.0)[1]), b=coef(model.0)[2], c=c.0)
model <- nls(cost.per.car ~ a * exp(b * reductions) + c, data = q24, start = start)

它的输出(对于示例数据)是

Nonlinear regression model
  model: cost.per.car ~ a * exp(b * reductions) + c
   data: q24
        a         b         c 
 0.003289  0.126805 48.487386 
 residual sum-of-squares: 2243

Number of iterations to convergence: 38 
Achieved convergence tolerance: 1.374e-06

收敛看起来不错。让我们绘制它:

plot(q24)
p <- coef(model)
curve(p["a"] * exp(p["b"] * x) + p["c"], lwd=2, col="Red", add=TRUE)

数字

效果很好!

自动化时,您可以对残差执行一些快速分析,例如将它们的极值与 (y) 数据。您可能还需要类似的代码来处理这种可能性a<0; 我把它留作练习。


另一种估计初始值的方法依赖于理解它们的含义,这可以基于经验、物理理论 。我的回答中描述了一个(中等难度)非线性拟合的扩展示例,其初始值可以通过这种方式确定在https://stats.stackexchange.com/a/15769

散点图的可视化分析(以确定初始参数估计)在https://stats.stackexchange.com/a/32832进行了描述和说明。

在某些情况下,会进行一系列非线性拟合,您可以预期解会缓慢变化。在这种情况下,使用以前的解决方案作为下一个解决方案的初始估计通常很方便(并且快速)我记得在https://stats.stackexchange.com/a/63169使用过这种技术(没有评论) 。

这个库能够解决我的 nls 问题singular gradienthttp ://www.r-bloggers.com/a-better-nls/ 一个例子:

library(minpack.lm)
nlsLM(function, start=list(variable=2,variable2=12))

所以......我想我把它误读为指数函数。我所需要的只是poly()

model <- lm(cost.per.car ~ poly(reductions, 3), data=q24)
new.data <- data.frame(reductions = c(91,92,93,94))
predict(model, new.data)

plot(q24)
lines(q24$reductions, predict(model, list(reductions = q24$reductions)))

或者,使用lattice

xyplot(cost.per.car ~ reductions, data = q24,
       panel = function(x, y) {
         panel.xyplot(x, y)
         panel.lines(x, predict(model,list(reductions = x) ))
       }, 
       xlab = "Reductions", 
       ylab = "Cost per car")