5

私の問題を説明するために再現可能な例で更新してください

私の最初の質問は、「R での信頼領域反射最適化アルゴリズムの実装」でした。ただし、再現可能な例を作成する途中で(@Benのアドバイスに感謝します)、私の問題はMatlabの1つの関数であることに気付きましたlsqnonlin私が持っているほとんどの場合に十分です(つまり、良い開始値を選択する必要がないことを意味します)が、Rにはそのような1対1の関数はありません。さまざまな最適化アルゴリズムが、さまざまなケースで適切に機能します。アルゴリズムが異なれば、到達する解も異なります。この背後にある理由は、R の最適化アルゴリズムが Matlab の信頼領域反射アルゴリズムより劣っているということではなく、R が自動微分を処理する方法にも関連している可能性があります。この問題は、実際には 2 年前に中断された作業が原因です。当時、パッケージoptimxの作成者の 1 人である John C Nash 教授は、Matlab lsqnonlin が R の最適化関数/アルゴリズムよりも優れたパフォーマンスを発揮する理由かもしれません。

以下の例は、私が遭遇したいくつかの問題を示しています (より再現可能な例が追加されています)。サンプルを実行するには、まず を実行しますinstall_github("KineticEval","zhenglei-gao")。パッケージmkinとその依存関係をインストールする必要があり、さまざまな最適化アルゴリズム用に他のパッケージを多数インストールする必要がある場合もあります。

lsqnonlin基本的に、Matlab 関数のドキュメント ( http://www.mathworks.de/de/help/optim/ug/lsqnonlin.html )で説明されているように、非線形最小二乗曲線近似問題を解決しようとしています。私の場合の曲線は、一連の微分方程式によってモデル化されています。例を挙げてもう少し説明します。私が試した最適化アルゴリズムは次のとおりです。

  • nls.lmレーベンバーグ・マルカート出身のマルク
  • からのポートnlm.inb
  • からのL-BGFS-Boptim
  • からのspgoptimx
  • solnpパッケージのRsolnp

他にもいくつか試しましたが、ここには示していません。

私の質問の要約

  • lsqnonlin私のタイプの非線形最小二乗問題を解決できるMatlabのように、Rには信頼できる関数/アルゴリズムがありますか? (私はそれを見つけることができませんでした。)
  • 単純なケースで、異なる最適化が異なるソリューションに到達する理由は何ですか?
  • lsqnonlinR の機能の優れている点は何ですか? 信頼領域反射アルゴリズムまたはその他の理由?
  • 私のタイプの問題を別の方法で解決するためのより良い方法はありますか? 多分もっと簡単な解決策があるかもしれませんが、私はそれを知りません。

例 1: 単純なケース

最初に R コードを与え、後で説明します。 例 1 のあてはめプロット

ex1 <- mkinmod.full(
  Parent = list(type = "SFO", to = "Metab", sink = TRUE,
                k = list(ini = 0.1,fixed = 0,lower = 0,upper = Inf),
                M0 = list(ini = 195, fixed = 0,lower = 0,upper = Inf),
                FF = list(ini = c(.1),fixed = c(0),lower = c(0),upper = c(1)),
                time=c(0.0,2.8,   6.2,  12.0,  29.2,  66.8,  99.8, 127.5, 154.4, 229.9, 272.3, 288.1, 322.9),
                residue = c( 157.3, 206.3, 181.4, 223.0, 163.2, 144.7,85.0, 76.5, 76.4, 51.5, 45.5, 47.3, 42.7),
                weight = c( 1,  1,   1, 1, 1,   1,  1,     1,     1,     1,     1,     1,     1)),
  Metab = list(type = "SFO",
               k = list(ini = 0.1,fixed = 0,lower = 0,upper = Inf),
              M0 = list(ini = 0, fixed = 1,lower = 0,upper = Inf),
                    residue =c( 0.0,  0.0,  0.0,  1.6,  4.0, 12.3, 13.5, 12.7, 11.4, 11.6, 10.9,  9.5,  7.6),
               weight = c( 1,  1,   1, 1, 1,   1,  1,     1,     1,     1,     1,     1,     1))
  )
ex1$diffs
Fit <- NULL
alglist <- c("L-BFGS-B","Marq", "Port","spg","solnp")
for(i in 1:5) {
  Fit[[i]] <- mkinfit.full(ex1,plot = TRUE, quiet= TRUE,ctr = kingui.control(method = alglist[i],submethod = 'Port',maxIter = 100,tolerance = 1E-06, odesolver = 'lsoda'))
  }
names(Fit) <- alglist
kinplot(Fit[[2]])
(lapply(Fit, function(x) x$par))
unlist(lapply(Fit, function(x) x$ssr))

最後の行からの出力は次のとおりです。

L-BFGS-B     Marq     Port      spg    solnp 
5735.744 4714.500 5780.446 5728.361 4714.499 

「Marq」と「solnp」を除いて、他のアルゴリズムは最適に達しませんでした。さらに、「spg」メソッド (「bobyqa」などの他のメソッドも) は、このような単純なケースに対してあまりにも多くの関数評価を必要とします。さらに、開始値を変更してk_Parent=0.0058、ランダムに選択された の代わりに (そのパラメーターの最適値) を作成すると0.1、「Marq」は最適値を見つけることができなくなります。(以下にコードを示します)。「solnp」が最適なものを見つけられないデータセットもありました。ただし、lsqnonlinMatlab で使用する場合、このような単純なケースでは問題は発生しませんでした。

ex1_a <- mkinmod.full(
  Parent = list(type = "SFO", to = "Metab", sink = TRUE,
                k = list(ini = 0.0058,fixed = 0,lower = 0,upper = Inf),
                M0 = list(ini = 195, fixed = 0,lower = 0,upper = Inf),
                FF = list(ini = c(.1),fixed = c(0),lower = c(0),upper = c(1)),
                time=c(0.0,2.8,   6.2,  12.0,  29.2,  66.8,  99.8, 127.5, 154.4, 229.9, 272.3, 288.1, 322.9),
                residue = c( 157.3, 206.3, 181.4, 223.0, 163.2, 144.7,85.0, 76.5, 76.4, 51.5, 45.5, 47.3, 42.7),
                weight = c( 1,  1,   1, 1, 1,   1,  1,     1,     1,     1,     1,     1,     1)),
  Metab = list(type = "SFO",
               k = list(ini = 0.1,fixed = 0,lower = 0,upper = Inf),
              M0 = list(ini = 0, fixed = 1,lower = 0,upper = Inf),
                    residue =c( 0.0,  0.0,  0.0,  1.6,  4.0, 12.3, 13.5, 12.7, 11.4, 11.6, 10.9,  9.5,  7.6),
               weight = c( 1,  1,   1, 1, 1,   1,  1,     1,     1,     1,     1,     1,     1))
  )

Fit_a <- NULL
alglist <- c("L-BFGS-B","Marq", "Port","spg","solnp")
for(i in 1:5) {
  Fit_a[[i]] <- mkinfit.full(ex1_a,plot = TRUE, quiet= TRUE,ctr = kingui.control(method = alglist[i],submethod = 'Port',maxIter = 100,tolerance = 1E-06, odesolver = 'lsoda'))
  }
names(Fit_a) <- alglist
lapply(Fit_a, function(x) x$par)
unlist(lapply(Fit_a, function(x) x$ssr))

最後の行からの出力は次のとおりです。

L-BFGS-B     Marq     Port      spg    solnp 
5653.132 4866.961 5653.070 5635.372 4714.499 

ここで最適化していることを説明します。上記のスクリプトを実行して曲線を確認した場合は、一次反応を伴う 2 コンパートメント モデルを使用して曲線を記述します。モデルを表す微分方程式は次のex1$diffsとおりです。

                                                             Parent 
                                    "d_Parent = - k_Parent * Parent" 
                                                               Metab 
"d_Metab = - k_Metab * Metab + k_Parent * f_Parent_to_Metab * Parent" 

この単純なケースでは、微分方程式から 2 つの曲線を表す方程式を導き出すことができます。最適化されるパラメータは$M_0,k_p, k_m, c=\mbox{FF_parent_to_Met} $制約付き$M_0>0,k_p>0, k_m>0, 1> c >0$です。

$$
\begin{split}
            y_{1j}&= M_0e^{-k_pt_i}+\epsilon_{1j}\\
            y_{2j} &= cM_0k_p\frac{e^{-k_mt_i}-e^{-k_pt_i}}{k_p-k_m}+\epsilon_{2j}
            \end{split}
$$

したがって、微分方程式を解かずに曲線を当てはめることができます。

BCS1.l <- mkin_wide_to_long(BCS1)
BCS1.l <- na.omit(BCS1.l)
indi <- c(rep(1,sum(BCS1.l$name=='Parent')),rep(0,sum(BCS1.l$name=='Metab')))
sysequ.indi <- function(t,indi,M0,kp,km,C)
  {
    y <- indi*M0*exp(-kp*t)+(1-indi)*C*M0*kp/(kp-km)*(exp(-km*t)-exp(-kp*t));
    y
  }
M00 <- 100
kp0 <- 0.1
km0 <- 0.01
C0 <- 0.1
library(nlme)
result1 <- gnls(value ~ sysequ.indi(time,indi,M0,kp,km,C),data=BCS1.l,start=list(M0=M00,kp=kp0,km=km0,C=C0),control=gnlsControl())
#result3 <- gnls(value ~ sysequ.indi(time,indi,M0,kp,km,C),data=BCS1.l,start=list(M0=M00,kp=kp0,km=km0,C=C0),weights = varIdent(form=~1|name))
## Coefficients:
##          M0           kp           km            C 
## 1.946170e+02 5.800074e-03 8.404269e-03 2.208788e-01 

このようにすると、経過時間はほぼゼロになり、最適に達します。ただし、この単純なケースが常にあるとは限りません。モデルは複雑になる可能性があり、微分方程式を解く必要があります。例 2 を参照

例 2、複雑なモデル

私はずっと前にこのデータセットに取り組んでいましたが、次のスクリプトを自分で実行し終える時間がありませんでした。(実行が完了するまで数時間かかる場合があります。) 例のあてはめプロット

data(BCS2)
ex2 <- mkinmod.full(Parent= list(type = "SFO",to = c( "Met1", "Met2","Met4", "Met5"),
                                 k = list(ini = 0.1,fixed = 0,lower = 0,upper = Inf),
                                 M0 = list(ini = 100,fixed = 0,lower = 0,upper = Inf),
                                 FF = list(ini = c(.1,.1,.1,.1),fixed = c(0,0,0,0),lower = c(0,0,0,0),upper = c(1,1,1,1))),
                    Met1 = list(type = "SFO",to = c("Met3", "Met4")),
                    Met2 = list(type = "SFO",to = c("Met3")),
                    Met3 = list(type = "SFO" ),
                    Met4 = list(type = "SFO", to = c("Met5")),
                    Met5 = list(type = "SFO"),
                    data=BCS2)
ex2$diffs
Fit2 <- NULL
alglist <- c("L-BFGS-B","Marq", "Port","spg","solnp")
for(i in 1:5) {
  Fit2[[i]] <- mkinfit.full(ex2,plot = TRUE, quiet= TRUE,ctr = kingui.control(method = alglist[i],submethod = 'Port',maxIter = 100,tolerance = 1E-06, odesolver = 'lsoda'))
  }
names(Fit) <- alglist
(lapply(Fit, function(x) x$par))
unlist(lapply(Fit, function(x) x$ssr))

これは、次のような警告メッセージが表示される例です。

DLSODA-  At T (=R1) and step size H (=R2), the    
  corrector convergence failed repeatedly     
  or with ABS(H) = HMIN   
In above message, R = 
[1] 0.000000e+00 2.289412e-09

元の質問

Matlab Optimization Toolbox ソルバーで使用される方法の多くは、信頼領域に基づいています。CRAN タスク ビュー ページによると、パッケージtrusttrustOptimminqaのみ に、信頼領域ベースのメソッドが実装されています。ただし、勾配とヘシアンが必要ですtrustminqaでは、私が探しているものではないようです。私の個人的な経験から、Matlab の信頼領域 Reflective アルゴリズムは、R で試したアルゴリズムと比較してパフォーマンスが優れていることが多いため、R でこのアルゴリズムの同様の実装を見つけようとしました。trustOptimbobyqa

ここで関連する質問をしました: R function to search for function

Matthew Plourde が提供した回答はlsqnonlin、Matlab で同じ関数名を持つ関数を提供しますが、信頼領域反射アルゴリズムが実装されていません。Matthew Plourde の回答は一般に、関数を探している R ユーザーにとって非常に役立つと思うので、古いものを編集し、ここで新しい質問をしました。

もう一度検索しましたが、運がありません。同様の matlab 関数を実装する関数/パッケージがまだいくつかあります。そうでない場合は、Matlab 関数を直接 R に翻訳して、自分の目的に使用することが許されるかどうか疑問に思います。

4

1 に答える 1

1

一般的に、質問の見出しだけを見る場合は、単純にFMEパッケージを使用することをお勧めします。しかし、それはあなたの質問のポイントではなく、成功はモデルの設定方法に依存する場合があります.

例で示したタイプの問題 (劣化データをいくつかの変換製品に適合させる)mkinについては、このタイプの問題用の FME への便利なラッパーとしてパッケージを作成しました。それでは、これらの場合に mkin 0.9-29 がどのように機能するかを見てみましょう。mkin では、FME が提供するアルゴリズムのみを使用できます。

例 1

library(mkin)

ex1_data_wide = data.frame(
  time= c(0.0, 2.8, 6.2, 12.0, 29.2, 66.8, 99.8, 127.5, 154.4, 229.9, 272.3, 288.1, 322.9),
  Parent = c(157.3, 206.3, 181.4, 223.0, 163.2, 144.7,85.0, 76.5, 76.4, 51.5, 45.5, 47.3, 42.7),
  Metab = c(0.0, 0.0, 0.0, 1.6, 4.0, 12.3, 13.5, 12.7, 11.4, 11.6, 10.9, 9.5, 7.6))

ex1_data = mkin_wide_to_long(ex1_data_wide, time = "time")

ex1_model = mkinmod(Parent = list(type = "SFO", to = "Metab"),
                    Metab = list(type = "SFO"))

algs = c("L-BFGS-B", "Marq", "Port")

times_ex1 <- list()
fits_ex1 <- list()
for (alg in algs) {
  times_ex1[[alg]] <- system.time(fits_ex1[[alg]] <- mkinfit(ex1_model, ex1_data,
                                                             method.modFit = alg))
}

times_ex1
unlist(lapply(fits_ex1, function(x) x$ssr))

したがって、nls.lm のような Levenberg-Marquardt と Port アルゴリズムの両方が最小値を見つけ、LM の方がはるかに高速です。

$`L-BFGS-B`
       User      System verstrichen 
      2.036       0.000       2.051 

$Marq
       User      System verstrichen 
      0.716       0.000       0.714 

$Port
       User      System verstrichen 
      2.032       0.000       2.030 

L-BFGS-B     Marq     Port 
5742.312 4714.498 4714.498 

mkin に率だけでなく形成分数を使用するように指示すると、

ex1_model = mkinmod(Parent = list(type = "SFO", to = "Metab"),
                    Metab = list(type = "SFO"), use_of_ff = "max")

開始値を使用し、

for (alg in algs) {
  times_ex1[[alg]] <- system.time(fits_ex1[[alg]] <- mkinfit(ex1_model, ex1_data,
    state.ini = c(195, 0),
    parms.ini = c(f_Parent_to_Metab = 0.1, k_Parent = 0.0058, k_Metab = 0.1),
    method.modFit = alg))
}

3 つのアルゴリズムはすべて、同じ解をさらに高速に見つけます。しかし、mkinfit( transform_rates = FALSE, transform_fractions = FALSE) の呼び出しで率と分数の変換をオフにすると、次のようになります。

L-BFGS-B     Marq     Port 
5653.132 4714.498 5653.070 

そのため、パラメーターが内部的に変換される方法に関連しているようです (FME は、範囲を指定するときにもこれを行います)。mkin では、明示的な内部パラメーター変換を行うため、デフォルト設定で最適化されたパラメーターに境界は必要ありません。

例 2

library(mkin)
library(KineticEval) # for the dataset BCS2
data(BCS2)

ex2_data = mkin_wide_to_long(BCS2, time = "time")

ex2_model = mkinmod(Parent = list(type = "SFO", to = paste0("Met", 1:5)),
                    Met1 = list(type = "SFO", to = c("Met3", "Met4")),
                    Met2 = list(type = "SFO", to = "Met3"),
                    Met3 = list(type = "SFO"),
                    Met4 = list(type = "SFO", to = "Met5"),
                    Met5 = list(type = "SFO"))

times_ex2 <- list()
fits_ex2 <- list()

for (alg in algs) {
  times_ex2[[alg]] <- system.time(fits_ex2[[alg]] <- mkinfit(ex2_model, ex2_data,
    method.modFit = alg))
}   

times_ex2
unlist(lapply(fits_ex2, function(x) x$ssr))

ここでも、LM が最も高速ですが、最小値は Port によって検出されます。

$`L-BFGS-B`
       User      System verstrichen 
     75.728       0.004      75.653 

$Marq
       User      System verstrichen 
      6.440       0.004       6.436 

$Port
       User      System verstrichen 
     51.200       0.028      51.180 

L-BFGS-B     Marq     Port 
485.3099 572.9635 478.4379 

以前は常に LM を推奨していましたが、最近、不適切に定義されたパラメーターの開始値によっては、LM が極小値に陥ることがあることもわかりました。1 つの例は、mkin パッケージのmkinfit の最後の単体テストで処理された、Schaefer 07 データtest.mkinfit.schaefer07_complex_exampleです。

これがお役に立てば幸いです。よろしくお願いします。

ヨハネス

PS: github の KineticEval パッケージに関数 lsqnonlin() として信頼領域 Reflective 最適化の純粋な R 実装を追加したことに気付き、信頼領域 Reflective を検索していたときに、この質問を見つけました。

于 2014-07-04T13:44:47.580 に答える