Search code examples
rfunctioncurve-fittingnls

Convergence Failure: Iteration limit reached without convergence (10)


I have some difficulties getting a specific curve to fit data to an nls model.

This is the formula for the data:

((b1 * ((b2 * x)^b4)) / (1 + ((b2 * x)^b4)))^(b3 / b4)

I use nls2 package with a random algorithm to find the inital values.

library(nls2)

#FORMULA
eq <- y ~ (b1 * ((b2 * x)^b4)) / (1 + ((b2 * x)^b4))^(b3 / b4)

#LIMITS
values <- data.frame(
b1 = c(60, 63)
b2 = c(0, 0.05)
b3 = c(0, 1)
b4 = c(0, 0.9)

fit <- nls2(eq, 
data = .data, 
start = values, 
algorithm = "random", 
control = mls.control(maxiter = 1000))

nls(eq, .data, start = coef(fit), alg = "port", lower = 0)
plot(.data)

The values should be:

b1 = 62.2060
b2 = 0.0438
b3 = 0.9692
b4 = 0.8693

However, when I try to run the codes, I always ended on an error message: Convergence Failure: Iteration limit reached without convergence (10)

How can I avoid the convergence failure error? Any help is highly appreciated. Thank You.


Solution

  • 0. TLDR

    You did not set the lower and upper bound in nls, so you didn't get a converging result. If you set them your will get a result near the boundary. See the code I wrote in the last paragraph.

    Actually, even if you set the boundary, due to the bad data quality(sample size is small and do not consist with you formula), it's hard to fit a optimal value near your true b1,'b2','b3' and b4. See nontechnical reason.

    1. Nontechnical reason of convergence failure

    I think your code is right, and this convergence fail is due to your data quality or your misspecification of formula.

    In general, it's hard for you to estimate 4 parameters with only 6 point. If you have good data which actully fits your model well, nlm will converge. In your case, either your data is wrong or you formula specification bias is huge.

    I draw a plot to show your that:

    Code

    # generate a line using true parameters:b1,b2,b3,b4
    b1 = 62.2060
    b2 = 0.0438
    b3 = 0.9692
    b4 = 0.8693
    x_points = seq(50,420,length.out = 200)
    y_points = (b1 * ((b2 * x_points)^b4)) / (1 + ((b2 * x_points)^b4))^(b3 / b4)
    # plot the function
    plot(x = x_points ,y = y_points, type ='l',col ='black',lwd = 5,
         xlim = c(min(yourdata$x)-5,max(yourdata$x)+5),
         ylim = c(min(yourdata$y)-5,max(yourdata$y)+5))  
    # plot the data your got
    points(yourdata$x,yourdata$y,cex = 2)
    

    Output: enter image description here

    If we generate a data from your formula, we can fit them quite easily, like this:

    ## generate data
    b1 = 62.2060
    b2 = 0.0438
    b3 = 0.9692
    b4 = 0.8693
    x <- runif(6,60,450)
    y <- (b1 * ((b2 * x)^b4)) / (1 + ((b2 * x)^b4))^(b3 / b4)
    data <- data.frame(x,y)
    
    yourdata <- data.frame(x = c(409.56, 195.25, 60.53, 359.56, 188.79, 67.12), 
                           y = c(39.76100, 20.11875, 7.23675, 41.01100, 20.28035, 7.07200))
    
    #FORMULA
    eq <- y ~ (b1 * ((b2 * x)^b4)) / (1 + ((b2 * x)^b4))^(b3 / b4)
    
    #LIMITS
    values <- data.frame(
      b1 = c(60, 63),
      b2 = c(0, 0.05),
      b3 = c(0, 1),
      b4 = c(0, 0.9))
    
    fit <- nls2(eq, 
                  data = data, 
                  start = values, 
                  algorithm = "random", 
                  control = nls.control(maxiter = 1000))
    
    nls(eq, data, start = coef(fit), alg = "port",
        control = nls.control(maxiter = 1000,tol = 1e-05),
        low = c(60,0,0,0),upper =c(63,0.05,1,0.9) ,trace = TRUE)
    plot(x,y)
    

    Output:

    Nonlinear regression model
      model: y ~ (b1 * ((b2 * x)^b4))/(1 + ((b2 * x)^b4))^(b3/b4)
       data: data
         b1      b2      b3      b4 
    62.2060  0.0438  0.9692  0.8693 
     residual sum-of-squares: 3.616e-24
    Algorithm "port", convergence message: absolute function convergence (6)
    

    Alse note that, in the above, I generate only6 numbers to fit the model. If you generate more data, for instance 60, you will have a better convergency!

    2.Technical reason

    After reading the PORT docs, I think that this error can mean

    1. gradient is calculated incorrectly
    2. stopping tolerances are too tight
    3. gradient is discontinous near some iterate

    And all these may have a relationship with you data and training task(your boundary and formula).

    Try code below and you will get a better result:

    Code:

    
    yourdata <- data.frame(x = c(409.56, 195.25, 60.53, 359.56, 188.79, 67.12), 
                           y = c(39.76100, 20.11875, 7.23675, 41.01100, 20.28035, 7.07200))
    
    #FORMULA
    eq <- y ~ (b1 * ((b2 * x)^b4)) / (1 + ((b2 * x)^b4))^(b3 / b4)
    
    #LIMITS
    values <- data.frame(
      b1 = c(60, 63),
      b2 = c(0, 0.05),
      b3 = c(0, 1),
      b4 = c(0, 0.9))
    
    fit <- nls2(eq, 
                  data = yourdata, 
                  start = values, 
                  algorithm = "random", 
                  control = nls.control(maxiter = 1000))
    
    nls(eq, yourdata, start = coef(fit), alg = "port",
        control = nls.control(maxiter = 1000,tol = 1e-05),
        low = c(60,0,0,0),upper =c(63,0.05,1,0.9) ,trace = TRUE)
    plot(x,y)
    

    Outputs:

    Nonlinear regression model
      model: y ~ (b1 * ((b2 * x)^b4))/(1 + ((b2 * x)^b4))^(b3/b4)
       data: yourdata
          b1       b2       b3       b4 
    63.00000  0.00155  0.00000  0.90000 
     residual sum-of-squares: 22.28
    Algorithm "port", convergence message: both X-convergence and relative convergence (5)
    

    As we can see, it converges to the boundary, which means that your data is unconsitant with your settings(formula or boundary).