【问题标题】:Non-linear fitting with nls() is giving me singular gradient matrix at initial parameter estimates. Why?与 nls() 的非线性拟合在初始参数估计时给了我奇异梯度矩阵。为什么?
【发布时间】:2019-05-20 03:10:14
【问题描述】:

这是我第一次尝试在 R 中拟合非线性模型,所以请多多包涵。

问题

我试图理解为什么nls() 给我这个错误:

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

假设

从我在 SO 的其他问题中读到的内容,可能是因为:

  • 我的模型不连续,或者
  • 我的模型过度确定,或者
  • 错误的起始参数值选择

因此,我正在寻求有关如何克服此错误的帮助。我可以更改模型并仍然使用nls(),还是需要使用minpack.lm 包中的nls.lm,就像我在其他地方看到的那样?

我的方法

以下是有关模型的一些详细信息:

  • 模型是一个不连续函数,一种阶梯类型的函数(见下图)
  • 一般来说,模型中的步数可以是可变的,但它们对于特定的拟合事件是固定的

显示问题的 MWE

MWE代码简述

  • step_fn(x, min = 0, max = 1):在区间内返回 1 的函数(minmax] 和 0 否则;对不起这个名字,我现在意识到它不是一个真正的步进函数...... interval_fn() 会我猜更合适。
  • staircase(x, dx, dy)step_fn() 函数的总和。 dxsteps 的宽度向量,即 max - mindy 是每个 stepy 增量。
  • staircase_formula(n = 1L):生成一个formula 对象,该对象表示由函数staircase()(与nls() 函数一起使用)建模的模型。
  • 请注意,我在下面的示例中使用了purrrglue 包。

代码

step_fn <- function(x, min = 0, max = 1) {

  y <- x
  y[x > min & x <= max] <- 1
  y[x <= min] <- 0
  y[x > max] <- 0

  return(y)
}

staircase <- function(x, dx, dy) {

  max <- cumsum(dx)
  min <- c(0, max[1:(length(dx)-1)])
  step <- cumsum(dy)

  purrr::reduce(purrr::pmap(list(min, max, step), ~ ..3 * step_fn(x, min = ..1, max = ..2)), `+`)
}


staircase_formula <- function(n = 1L) {

  i <- seq_len(n)
  dx <- sprintf("dx%d", i)

  min <-
    c('0', purrr::accumulate(dx[-n], .f = ~ paste(.x, .y, sep = " + ")))
  max <- purrr::accumulate(dx, .f = ~ paste(.x, .y, sep = " + "))

  lhs <- "y"
  rhs <-
    paste(glue::glue('dy{i} * step_fn(x, min = {min}, max = {max})'),
          collapse  = " + ")

  sc_form <- as.formula(glue::glue("{lhs} ~ {rhs}")) 

  return(sc_form)
}


x <- seq(0, 10, by = 0.01)
y <- staircase(x, c(1,2,2,5), c(2,5,2,1)) + rnorm(length(x), mean = 0, sd = 0.2)

plot(x = x, y = y)
lines(x = x, y = staircase(x, dx = c(1,2,2,5), dy = c(2,5,2,1)), col="red")


my_data <- data.frame(x = x, y = y)
my_model <- staircase_formula(4)
params <- list(dx1 = 1, dx2 = 2, dx3 = 2, dx4 = 5,
               dy1 = 2, dy2 = 5, dy3 = 2, dy4 = 1)

m <- nls(formula = my_model, start = params, data = my_data)
#> Error in nlsModel(formula, mf, start, wts): singular gradient matrix at initial parameter estimates

非常感谢任何帮助。

【问题讨论】:

    标签: r nonlinear-optimization nls


    【解决方案1】:

    我假设您得到了一个长度为 len 的观察向量,如您的示例中绘制的那样,并且您希望确定 k 跳跃和 k 跳跃大小。 (或者也许我误解了你;但你并没有真正说出你想要达到的目标。) 下面我将使用本地搜索绘制一个解决方案。我从你的示例数据开始:

    x <- seq(0, 10, by = 0.01)
    y <- staircase(x,
                   c(1,2,2,5),
                   c(2,5,2,1)) + rnorm(length(x), mean = 0, sd = 0.2)
    

    解决方案是跳跃的位置尺寸的列表。请注意,我使用向量来存储这些数据,因为当你有 20 次跳跃时,定义变量会变得很麻烦。

    一个示例(随机)解决方案:

    k <- 5   ## number of jumps
    len <- length(x)
    
    sol <- list(position = sample(len, size = k),
                size = runif(k))
    
    ## $position
    ## [1]  89 236 859 885 730
    ## 
    ## $size
    ## [1] 0.2377453 0.2108495 0.3404345 0.4626004 0.6944078
    

    我们需要一个目标函数来计算解决方案的质量。我还定义了一个简单的辅助函数stairs,供目标函数使用。 目标函数abs_diff 计算拟合序列(由解定义)和y 之间的平均绝对差。

    stairs <- function(len, position, size) {
        ans <- numeric(len)
        ans[position] <- size
        cumsum(ans)
    }
    
    abs_diff <- function(sol, y, stairs, ...) {
        yy <- stairs(length(y), sol$position, sol$size)
        sum(abs(y - yy))/length(y)
    }
    

    现在是本地搜索的关键组成部分:用于改进解决方案的邻域函数。邻域函数需要一个解决方案并稍微改变它。在这里,它会选择一个 position 或一个 size 并稍微修改它。

    neighbour <- function(sol, len, ...) {
        p <- sol$position
        s <- sol$size
    
        if (runif(1) > 0.5) {
            ## either move one of the positions ...
            i <- sample.int(length(p),  size = 1)
            p[i] <- p[i] + sample(-25:25, size = 1)
            p[i] <- min(max(1, p[i]), len)        
        } else {
            ## ... or change a jump size
            i <- sample.int(length(s), size = 1)
            s[i] <- s[i] + runif(1, min = -s[i], max = 1)
        }
    
        list(position = p, size = s)
    }
    

    一个示例调用:这里新解决方案的第一个跳转大小发生了变化。

    ## > sol
    ## $position
    ## [1]  89 236 859 885 730
    ## 
    ## $size
    ## [1] 0.2377453 0.2108495 0.3404345 0.4626004 0.6944078
    ## 
    ## > neighbour(sol, len)
    ## $position
    ## [1]  89 236 859 885 730
    ## 
    ## $size
    ## [1] 0.2127044 0.2108495 0.3404345 0.4626004 0.6944078
    

    我仍然要运行本地搜索。

    library("NMOF")
    sol.ls <- LSopt(abs_diff,
                    list(x0 = sol, nI = 50000, neighbour = neighbour),
                    stairs = stairs,
                    len = len,
                    y = y)
    

    我们可以绘制解:拟合线显示为蓝色。

    plot(x, y)
    lines(x, stairs(len, sol.ls$xbest$position, sol.ls$xbest$size),
          col = "blue", type = "S")
    

    【讨论】:

    • 感谢您的解决方案!我不知道你的这个包裹。看起来很有趣。它是否提供有关找到的解决方案的统计信息?例如拟合优度、估计的置信区间等。
    • 不,Local Search 是一种通用的优化方法;它不对底层模型做任何假设。如果您想进行推理,您可能需要查看有关结构中断的文献。
    【解决方案2】:

    改用 DE:

    library(NMOF)
     yf= function(params,x){
       dx1 = params[1]; dx2 = params[2]; dx3 = params[3]; dx4 = params[4];
       dy1 = params[5]; dy2 = params[6]; dy3 = params[7]; dy4 = params[8]
       dy1 * step_fn(x, min = 0, max = dx1) + dy2 * step_fn(x, min = dx1, 
                   max = dx1 + dx2) + dy3 * step_fn(x, min = dx1 + dx2, max = dx1 + 
                   dx2 + dx3) + dy4 * step_fn(x, min = dx1 + dx2 + dx3, max = dx1 + 
                   dx2 + dx3 + dx4)
     }
    
     algo1 <- list(printBar = FALSE,
                   nP  = 200L,
                   nG  = 1000L,
                   F   = 0.50,
                   CR  = 0.99,
                   min = c(0,1,1,4,1,4,1,0),
                   max = c(2,3,3,6,3,6,3,2))
    
     OF2 <- function(Param, data) { #Param=paramsj data=data2
       x <- data$x
       y <- data$y
       ye <- data$model(Param,x)
       aux <- y - ye; aux <- sum(aux^2)
       if (is.na(aux)) aux <- 1e10
       aux
     }
    
     data5 <- list(x = x, y = y,  model = yf, ww = 1)
     system.time(sol5 <- DEopt(OF = OF2, algo = algo1, data = data5))
     sol5$xbest
     OF2(sol5$xbest,data5)
    
     plot(x,y)
     lines(data5$x,data5$model(sol5$xbest, data5$x),col=7,lwd=2)
    
    #>  sol5$xbest
    #[1]   1.106396  12.719182  -9.574088  18.017527   3.366852   8.721374 -19.879474   1.090023
    #>  OF2(sol5$xbest,data5)
    #[1] 1000.424
    

    【讨论】:

      猜你喜欢
      • 2014-08-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-11
      • 2023-04-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多