Как найти хорошие стартовые значения для функции 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

person Tali    schedule 13.03.2012    source источник


Ответы (3)


Это можно записать с двумя линейными параметрами (.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 = - .lin2 и c = .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"

РЕДАКТИРОВАТЬ: добавлен пробный прогон и комментарий по масштабированию.

person G. Grothendieck    schedule 13.03.2012
comment
С этим я получаю b .lin1 .lin2 3.351e + 00 1.006e + 00 -1.589e + 08, и когда я вычисляю a и c, у меня есть: nls (vec_y ~ expFct2 (vec_x, a, b, c), start = list (a = 1.589e + 08, b = 75, c = -158899999), control = nls.control (maxiter = 200)) У меня есть эта ошибка: Ошибка в nlsModel (формула, mf, start, wts): сингулярный градиент матрица при начальных оценках параметров. Я не понимаю почему - person Tali; 19.03.2012
comment
Обычно при выполнении нелинейной оптимизации вы хотите, чтобы параметры находились примерно в одном диапазоне значений. Добавили пробный прогон, показывающий проблему. Преобразуйте свои параметры, чтобы этого не произошло. Преимущество подхода plinear в том, что он относительно ясно, как преобразовать в линейность, и теперь, когда мы видим, что он дает, мы знаем, что нам нужно преобразовать наши параметры дальше и какие. Винсент уже показал, как это делать. - person G. Grothendieck; 19.03.2012

Подбор трехпараметрической нелинейной модели к четырем точкам данных в любом случае будет довольно сложной задачей, хотя в этом случае данные хорошо обрабатываются. Пункт №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 - параметр масштаба (вы можете видеть, что когда x=b, кривая составляет 1-exp(-1) или примерно 2/3 пути от от минимума до максимума). Либо немного алгебры и исчисления (т. Е. Определение пределов), либо поиграйте с функцией curve() - хорошие способы собрать эту информацию.

person Ben Bolker    schedule 13.03.2012
comment
Спасибо за ваш ответ. Я не знал функции SSasympOff. Но как мне найти значение a, b и c для моей функции? Если я выполняю getInitial (vec_y ~ SSasympOff (vec_x, 0.5, 75, 0.5), data = dt), это не значения для моего уравнения. - person Tali; 19.03.2012

Я изо всех сил пытаюсь найти интерпретацию ваших параметров: 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 
person Vincent Zoonekynd    schedule 13.03.2012
comment
Хорошо, но как отсюда я могу найти начальное значение для своей функции, потому что если я сделаю так: nls (vec_y ~ expFct2 (vec_x, a, b, c), start = list (a = 1.006, b = 3.351, c = 63.257), control = nls.control (maxiter = 200), у меня есть эта ошибка: Ошибка в nlsModel (формула, mf, start, wts): матрица сингулярного градиента при начальных оценках параметров - person Tali; 19.03.2012
comment
Мое предложение состояло в том, чтобы повторно параметризовать вашу функцию, во-первых, чтобы разделить влияние различных параметров, во-вторых, что более важно, чтобы гарантировать, что оптимальные значения, которые мы ищем, имеют одинаковый порядок величины (если одно в 100000000 раз больше, чем другие, следует ожидать проблем). После повторной параметризации оптимизация более чувствительна к начальным значениям. - person Vincent Zoonekynd; 19.03.2012
comment
Как вы получили это a * (1-exp (- (x-c) / abs (b))) из этого a * (1-exp (-x / b)) + c? - person Tali; 19.03.2012
comment
(Поскольку я изменил параметры, мне следовало переименовать их: это действительно сбивает с толку. Давайте использовать простые числа для новых параметров.) Если вы развернете эти выражения, оба a' * (1-exp(-(x-c')/abs(b'))) и a*(1-exp(-x/b)) + c будут иметь форму A + B * exp(C*x). - person Vincent Zoonekynd; 19.03.2012
comment
Большое спасибо, это мне очень помогает - person Tali; 19.03.2012