cv.glmnetの結果のばらつき


18

cv.glmnet予測子を見つけるために使用しています。私が使用するセットアップは次のとおりです。

lassoResults<-cv.glmnet(x=countDiffs,y=responseDiffs,alpha=1,nfolds=cvfold)
bestlambda<-lassoResults$lambda.min

results<-predict(lassoResults,s=bestlambda,type="coefficients")

choicePred<-rownames(results)[which(results !=0)]

作るために必ず結果が再現可能Iですset.seed(1)。結果は大きく変わります。まったく同じコード100を実行して、結果がどの程度変動するかを確認しました。98/100の実行では、1つの特定の予測子が常に選択されていました(時にはそれだけで); 通常は50/100回、他の予測変数が選択されました(係数はゼロ以外)。

だから、クロス検証が実行されるたびに、おそらくフォールドの最初のランダム化が重要であるため、異なる最良のラムダを選択するだろうと私に言います。他の人はこの問題を見ました(CV.glmnet結果)が、提案された解決策はありません。

私はおそらく、98/100を示すものはおそらく他のすべてと非常に高い相関関係があると考えていますか?LOOCV()を実行するだけで結果安定しますが、\ text {nfold} <nの場合になぜこれらの変数が変動するのか興味があります。fold-size=nnfold<n


1
明確にするために、あなたはset.seed(1)一度あなたがcv.glmnet()100回実行するということですか それは再現性のための素晴らしい方法論ではありません。より良いset.seed()各実行の前に右、または他の実行間foldidsを一定に保ちます。への各呼び出しcv.glmnet()は、sample()N回呼び出しています。したがって、データの長さが変わると、再現性が変わります。
SMCI

回答:


14

ここでのポイントはcv.glmnet、K個のフォールド(「パーツ」)がランダムに選択されることです。

Kフォールド相互検証では、データセットは部分に分割され、部分はK番目の部分を予測するために使用されます(これは、毎回異なる部分を使用して回行われます)。これはすべてのラムダに対して行われ、これは最小の相互検証エラーを与えるものです。KK1KKlambda.min

これが、を使用しても結果が変わらない理由です。各グループは1つで構成されているため、グループにはあまり選択肢がありません。nfolds=nK

cv.glmnet()リファレンスマニュアルから:

また、折り畳みはランダムに選択されるため、cv.glmnetの結果はランダムです。ユーザーは、cv.glmnetを何度も実行し、エラー曲線を平均化することにより、このランダム性を減らすことができます。

### cycle for doing 100 cross validations
### and take the average of the mean error curves
### initialize vector for final data.frame with Mean Standard Errors
MSEs <- NULL
for (i in 1:100){
                 cv <- cv.glmnet(y, x, alpha=alpha, nfolds=k)  
                 MSEs <- cbind(MSEs, cv$cvm)
             }
  rownames(MSEs) <- cv$lambda
  lambda.min <- as.numeric(names(which.min(rowMeans(MSEs))))

MSEは、すべてのラムダ(100回の実行)のすべてのエラーを含むデータフレームで lambda.minあり、平均エラーが最小のラムダです。


私が最も心配しているのは、nの選択が本当に問題になるように見えることです。それほど変動する可能性のある結果を信頼すべきですか?それとも、複数回実行したとしても、それをスケッチとしてチョークで書く必要がありますか?
user4673 14年

1
サンプルサイズに応じて、nを選択する必要があります。したがって、グループごとに少なくとも10個の観測値があります。したがって、サンプルサイズが100より小さい場合は、デフォルトのn(= 10)を減らすことをお勧めします。これは、コードの一部で編集済みの回答を参照してください。エラー曲線。数回試してみると、lambda.minが変わらないことがわかります。
アリス14年

2
私はあなたがそれをやった方法が好きです。私は同じループを持っていますが、最後に1つの例外があります。すべての反復で最も低いMSEとは対照的に、異なる機能がどれくらい頻繁にポップアップするかを調べます。任意のカットポイントを選択し(つまり、50/100の繰り返しを表示)、それらの機能を使用します。好奇心が強い2つのアプローチ。
user4673 14年

1
このrownames(MSEs)<-cv私の場合の lambdaはMSEsよりも長いため(収束のためだと思います...)lambdaerror,sincecv
user4581

user4581が指摘したように、この関数はの長さのばらつきにより失敗する可能性がありcv.glmnet(...)$lambdaます。私の代替はこれを修正します:stats.stackexchange.com/a/173895/19676
マックスゲニス

9

最近、私は同じ問題に直面しました。データセットで100、200、1000など、CVを何度も繰り返して、最適なとを見つけようとしました(エラスティックネットを使用しています)。しかし、各最小MSEを平均する1000回の反復でそれぞれ3つのcvテストを作成した場合でも、3つの異なる最高(、)カップルが得られます。λααλα

ここでは問題には触れませんが、私の最良の解決策は最小MSEを平均化せず、代わりに各反復の最高係数を抽出し、それらを値の分布(ランダム変数)として扱うことでした。αλ

次に、予測子ごとに次のようになります。

  • 平均係数
  • 標準偏差
  • 5つの数値の要約(中央値、四分位数、最小値と最大値)
  • 時間の割合はゼロとは異なります(つまり、影響力があります)

このようにして、予測子の効果についてかなり堅実な説明を取得します。係数の分布を取得したら、CI、p値などを取得する価値があると思われる統計処理を実行できますが、まだ調査していません。

この方法は、多かれ少なかれ私が考えることができる選択方法で使用できます。


4
ここにコードを投稿してください。
rbm

はい、ここにコードを投稿してください。
smci

4

ラムダの欠落による@Aliceのバグを処理する別のソリューションを追加しますが、@ Max Ghenisのような追加のパッケージは必要ありません。他のすべての答えに感謝します-誰もが役に立つポイントを作ります!

lambdas = NULL
for (i in 1:n)
{
    fit <- cv.glmnet(xs,ys)
    errors = data.frame(fit$lambda,fit$cvm)
    lambdas <- rbind(lambdas,errors)
}
# take mean cvm for each lambda
lambdas <- aggregate(lambdas[, 2], list(lambdas$fit.lambda), mean)

# select the best one
bestindex = which(lambdas[2]==min(lambdas[2]))
bestlambda = lambdas[bestindex,1]

# and now run glmnet once more with it
fit <- glmnet(xy,ys,lambda=bestlambda)

3

アリスの答えはほとんどの場合うまく機能しcv.glmnet$lambdaますが、異なる長さの結果を返すことがあるためにエラーが出る場合があります。 例えば:

rownames <-(tmp、value = c(0.135739830284452、0.12368107787663、のエラー: 'dimnames' [1]の長さは配列エクステントと等しくありません。

OptimLambda以下は一般的なケースで動作するはずであり、mclapply並列処理とループの回避を活用することで高速化されます。

Lambdas <- function(...) {
  cv <- cv.glmnet(...)
  return(data.table(cvm=cv$cvm, lambda=cv$lambda))
}

OptimLambda <- function(k, ...) {
  # Returns optimal lambda for glmnet.
  #
  # Args:
  #   k: # times to loop through cv.glmnet.
  #   ...: Other args passed to cv.glmnet.
  #
  # Returns:
  #   Lambda associated with minimum average CV error over runs.
  #
  # Example:
  #   OptimLambda(k=100, y=y, x=x, alpha=alpha, nfolds=k)
  #
  require(parallel)
  require(data.table)
  MSEs <- data.table(rbind.fill(mclapply(seq(k), function(dummy) Lambdas(...))))
  return(MSEs[, list(mean.cvm=mean(cvm)), lambda][order(mean.cvm)][1]$lambda)
}

1

foldidを明示的に設定すると、ランダム性を制御できます。5倍CVの例

library(caret)
set.seed(284)
flds <- createFolds(responseDiffs, k = cvfold, list = TRUE, returnTrain = FALSE)
foldids = rep(1,length(responseDiffs))
foldids[flds$Fold2] = 2
foldids[flds$Fold3] = 3
foldids[flds$Fold4] = 4
foldids[flds$Fold5] = 5

次に、これらのfoldidを使用してcv.glmnetを実行します。

lassoResults<-cv.glmnet(x=countDiffs,y=responseDiffs,alpha=1,foldid = foldids)

毎回同じ結果が得られます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.