16

この方法で解決できる最適化問題がありますが、ニュートンラプソン法、または勾配関数をNelder-Mead使用するものを使用して、より高速に、できればより正確な推定を行うために解決したいと思います。/ドキュメントBFGSの例に従ってこのようなグラデーション関数を作成しましたが、開始値で使用すると、移動しない()か、関数が完全に実行されません(、を返します)。これを再現するために少しコードが含まれていることをお詫びしますが、ここに行きます:optimoptimxBFGSoptim()optimx()Error: Gradient function might be wrong - check it!

これは、パラメーター推定値を取得したい関数です(これは、老齢死亡率を平滑化するためのものです。ここで、xは年齢で、80歳から始まります)。

    KannistoMu <- function(pars, x = .5:30.5){
      a <- pars["a"]
      b <- pars["b"]
      (a * exp(b * x)) / (1 + a * exp(b * x))
    }

そして、これが観察された率(死亡、.Dx過剰曝露として定義される.Exp)からそれを推定するための対数尤度関数です:

    KannistoLik1 <- function(pars, .Dx, .Exp, .x. = .5:30.5){
      mu <- KannistoMu(exp(pars), x = .x.)
      # take negative and minimize it (default optimizer behavior)
      -sum(.Dx * log(mu) - .Exp * mu, na.rm = TRUE) 
    }

ファイナルを制約し、ポジティブになるために、私が最適化するexp(pars)ために与えるので、あなたはそこに見えます。log(pars)ab

データ例(1962年の日本の女性、興味があれば):

    .Dx <- structure(c(10036.12, 9629.12, 8810.11, 8556.1, 7593.1, 6975.08, 
      6045.08, 4980.06, 4246.06, 3334.04, 2416.03, 1676.02, 1327.02, 
      980.02, 709, 432, 350, 217, 134, 56, 24, 21, 10, 8, 3, 1, 2, 
      1, 0, 0, 0), .Names = c("80", "81", "82", "83", "84", "85", "86", 
      "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97", 
      "98", "99", "100", "101", "102", "103", "104", "105", "106", 
      "107", "108", "109", "110"))
    .Exp <- structure(c(85476.0333333333, 74002.0866666667, 63027.5183333333, 
      53756.8983333333, 44270.9, 36749.85, 29024.9333333333, 21811.07, 
      16912.315, 11917.9583333333, 7899.33833333333, 5417.67, 3743.67833333333, 
      2722.435, 1758.95, 1043.985, 705.49, 443.818333333333, 223.828333333333, 
      93.8233333333333, 53.1566666666667, 27.3333333333333, 16.1666666666667, 
      10.5, 4.33333333333333, 3.16666666666667, 3, 2.16666666666667, 
      1.5, 0, 1), .Names = c("80", "81", "82", "83", "84", "85", "86", 
      "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97", 
      "98", "99", "100", "101", "102", "103", "104", "105", "106", 
      "107", "108", "109", "110"))

Nelder-Meadこのメソッドでは、次のように機能します。

    NMab <- optim(log(c(a = .1, b = .1)), 
      fn = KannistoLik1, method = "Nelder-Mead",
      .Dx = .Dx, .Exp = .Exp)
    exp(NMab$par) 
    # these are reasonable estimates
       a         b 
    0.1243144 0.1163926

これは私が思いついた勾配関数です:

    Kannisto.gr <- function(pars, .Dx, .Exp, x = .5:30.5){
      a <- exp(pars["a"])
      b <- exp(pars["b"])
      d.a <- (a * exp(b * x) * .Exp + (-a * exp(b * x) - 1) * .Dx) /
        (a ^ 3 * exp(2 * b * x) + 2 * a ^ 2 * exp(b * x) + a)
      d.b <- (a * x * exp(b * x) * .Exp + (-a * x * exp(b * x) - x) * .Dx) /
        (a ^ 2 * exp(2 * b * x) + 2 * a * exp(b * x) + 1)
      -colSums(cbind(a = d.a, b = d.b), na.rm = TRUE)
    }

a出力は長さ2のベクトルであり、パラメーターと。に関する変化bです。また、の出力を利用して醜いバージョンに到達しましたderiv()。これは同じ答えを返し、投稿しません(導関数が正しいことを確認するためだけに)。

optim()次のように指定するBFGSと、メソッドとして、推定値は開始値から移動しません。

    BFGSab <- optim(log(c(a = .1, b = .1)), 
      fn = KannistoLik1, gr = Kannisto.gr, method = "BFGS",
      .Dx = .Dx, .Exp = .Exp)
    # estimates do not change from starting values:
    exp(BFGSab$par) 
      a   b 
    0.1 0.1

$counts出力の要素を見ると、KannistoLik1()31回とKannisto.gr()1回だけ呼び出されたことがわかります。$convergenceである0ため、収束したと思います(あまり合理的でないスタートを与えると、それらもそのままになります)。公差などを減らしても何も変わりません。同じ呼び出しを試みた場合optimx()(図には示されていません)、上記の警告を受け取り、オブジェクトは返されません。gr = Kannisto.grで指定しても同じ結果が得られます"CG"。この"L-BFGS-B"方法では、推定と同じ開始値が返されますが、関数と勾配の両方が21回呼び出され、エラーメッセージが表示されることも報告されています。 "ERROR: BNORMAL_TERMINATION_IN_LNSRCH"

optimxこの後の警告と動作は、関数が単に正しくないことを率直に示唆しているため、これを解決する勾配関数の記述方法に若干の詳細があることを期待しています(私は思います)。maxNR()また、パッケージからマキシマイザーを試し、maxLik同様の動作を観察しました(開始値は移動しません)。誰かが私にポインタを与えることができますか?とても感謝しております

[編集]@Vincentは、数値近似からの出力と比較することを提案しました。

    library(numDeriv)
    grad( function(u) KannistoLik1( c(a=u[1], b=u[2]), .Dx, .Exp ), log(c(.1,.1)) )
    [1] -14477.40  -7458.34
    Kannisto.gr(log(c(a=.1,b=.1)), .Dx, .Exp)
     a        b 
    144774.0  74583.4 

とても異なるサイン、そして10倍オフ?それに合わせて勾配関数を変更します。

    Kannisto.gr2 <- function(pars, .Dx, .Exp, x = .5:30.5){
      a <- exp(pars["a"])
      b <- exp(pars["b"])
      d.a <- (a * exp(b * x) * .Exp + (-a * exp(b * x) - 1) * .Dx) /
        (a ^ 3 * exp(2 * b * x) + 2 * a ^ 2 * exp(b * x) + a)
      d.b <- (a * x * exp(b * x) * .Exp + (-a * x * exp(b * x) - x) * .Dx) /
        (a ^ 2 * exp(2 * b * x) + 2 * a * exp(b * x) + 1)
      colSums(cbind(a=d.a,b=d.b), na.rm = TRUE) / 10
    }
    Kannisto.gr2(log(c(a=.1,b=.1)), .Dx, .Exp)
    # same as numerical:
      a         b 
    -14477.40  -7458.34 

オプティマイザーで試してみてください。

    BFGSab <- optim(log(c(a = .1, b = .1)), 
      fn = KannistoLik1, gr = Kannisto.gr2, method = "BFGS",
      .Dx = .Dx, .Exp = .Exp)
    # not reasonable results:
    exp(BFGSab$par) 
      a   b 
    Inf Inf 
    # and in fact, when not exp()'d, they look oddly familiar:
    BFGSab$par
      a         b 
    -14477.40  -7458.34 

Vincentの答えに従って、勾配関数を再スケーリングし、パラメーターを正に保つabs()代わりに使用しました。exp()最新の、よりパフォーマンスの高い目的関数と勾配関数:

    KannistoLik2 <- function(pars, .Dx, .Exp, .x. = .5:30.5){
      mu <- KannistoMu.c(abs(pars), x = .x.)
      # take negative and minimize it (default optimizer behavior)
      -sum(.Dx * log(mu) - .Exp * mu, na.rm = TRUE) 
    }

    # gradient, to be down-scaled in `optim()` call
    Kannisto.gr3 <- function(pars, .Dx, .Exp, x = .5:30.5){
      a <- abs(pars["a"])
      b <- abs(pars["b"])
      d.a <- (a * exp(b * x) * .Exp + (-a * exp(b * x) - 1) * .Dx) /
        (a ^ 3 * exp(2 * b * x) + 2 * a ^ 2 * exp(b * x) + a)
      d.b <- (a * x * exp(b * x) * .Exp + (-a * x * exp(b * x) - x) * .Dx) /
        (a ^ 2 * exp(2 * b * x) + 2 * a * exp(b * x) + 1)
      colSums(cbind(a = d.a, b = d.b), na.rm = TRUE) 
    }

    # try it out:
    BFGSab2 <- optim(
      c(a = .1, b = .1), 
      fn = KannistoLik2, 
      gr = function(...) Kannisto.gr3(...) * 1e-7, 
      method = "BFGS",
      .Dx = .Dx, .Exp = .Exp
    )
    # reasonable:
    BFGSab2$par
            a         b 
    0.1243249 0.1163924 

    # better:
    KannistoLik2(exp(NMab1$par),.Dx = .Dx, .Exp = .Exp) > KannistoLik2(BFGSab2$par,.Dx = .Dx, .Exp = .Exp)
    [1] TRUE

これは私が予想していたよりもはるかに速く解決され、私はいくつかのトリック以上を学びました。ヴィンセントありがとう!

4

1 に答える 1

14

勾配が正しいかどうかを確認するには、それを数値近似と比較します。

library(numDeriv); 
grad( function(u) KannistoLik1( c(a=u[1], b=u[2]), .Dx, .Exp ), c(1,1) ); 
Kannisto.gr(c(a=1,b=1), .Dx, .Exp)

符号が間違っています。アルゴリズムがこの方向に移動しても改善が見られないため、アルゴリズムは移動しません。

数式処理システム(ここではMaxima)を使用して、次の計算を行うことができます。

display2d: false;
f(a,b,x) := a * exp(b*x) / ( 1 + a * exp(b*x) );
l(a,b,d,e,x) := - d * log(f(a,b,x)) + e * f(a,b,x);
factor(diff(l(exp(a),exp(b),d,e,x),a));
factor(diff(l(exp(a),exp(b),d,e,x),b));

結果をコピーしてRに貼り付けるだけです。

f_gradient <- function(u, .Dx, .Exp, .x.=.5:30.5) {
  a <- u[1]
  b <- u[1]
  x <- .x.
  d <- .Dx
  e <- .Exp
  c(
    sum( (e*exp(exp(b)*x+a)-d*exp(exp(b)*x+a)-d)/(exp(exp(b)*x+a)+1)^2 ),
    sum( exp(b)*x*(e*exp(exp(b)*x+a)-d*exp(exp(b)*x+a)-d)/(exp(exp(b)*x+a)+1)^2 )
  )  
}

library(numDeriv)
grad( function(u) KannistoLik1( c(a=u[1], b=u[2]), .Dx, .Exp ), c(1,1) )
f_gradient(c(a=1,b=1), .Dx, .Exp)  # Identical

勾配をやみくもに最適化に入れると、数値的不安定性の問題が発生します。与えられた解決策は(Inf,Inf)...それを防ぐために、勾配を再スケーリングできます(より良い回避策は、指数よりも爆発性の低い変換を使用することです。パラメータが正のままであることを確認してください)。

BFGSab <- optim(
  log(c(a = .1, b = .1)), 
  fn = KannistoLik1, 
  gr = function(...) f_gradient(...) * 1e-3, 
  method = "BFGS",
  .Dx = .Dx, .Exp = .Exp
)
exp(BFGSab$par) # Less precise than Nelder-Mead
于 2012-07-24T02:41:45.037 に答える