mclapply
非常に大きな計画行列X
(〜10GB)と応答ベクトルのフィッティング手順をモデル化するための交差検証を並列化するために使用しようとしていますy
。X
次元がn-by-p
:であるとしましょうn=1000, p=1,000,000
。X
は非常に巨大であるため、オブジェクトとしてバックアップさbig.matrix
れ、ディスクに格納され、R パッケージのメソッドを使用してアクセスされますbigmemory
。
4 分割クロス検証のワークフローは次のとおりです。
cv.ind
長さ のインデックス ベクトルを設定しますn
。これは 1 ~ 4 の一連の数値を格納し、 のどの観測値がX
CV のどのフォールドに属するかを示します。- 4 コアをセットアップします。i 番目のコアで、i 番目のフォールド CV の対応するトレーニングおよびテスト サブマトリックスをコピーします。
- 各コアの折り目ごとにモデルを当てはめます。
- 結果を収集し、返します。
交差検証関数は次のようになります。
cv.ncvreg <- function(X, y, ncore, nfolds=5, seed, cv.dir = getwd(),
cv.ind) {
## some more setup ...
## ...
## ...
## pass the descriptor info to each core ##
xdesc <- describe(X)
## use mclapply instead of parLapply
fold.results <- parallel::mclapply(X = 1:nfolds, FUN = cvf, XX=xdesc, y=y,
cv.dir = cv.dir, cv.ind=cv.ind,
cv.args=cv.args,
mc.set.seed = seed, mc.silent = F,
mc.cores = ncore, mc.preschedule = F)
## return results
}
R 関数cvf
は各コアで実行されます。i 番目のフォールドのトレーニング/テスト行列を 2 つのbig.matrix
オブジェクトとしてコピーし、モデルに適合させ、いくつかの統計を計算し、結果を返します。
cvf <- function(i, XX, y, cv.dir, cv.ind, cv.args) {
## argument 'XX' is the descriptor for big.matrix
# reference to the big.matrix by descriptor info
XX <- attach.big.matrix(XX)
cat("CV fold #", i, "\t--Copy training-- Start time: ", format(Sys.time()), "\n\n")
## physically copy sub big.matrix for training
idx.train <- which(cv.ind != i) ## get row idx for i-th fold training
deepcopy(XX, rows = idx.train, type = 'double',
backingfile = paste0('x.cv.train_', i, '.bin'),
descriptorfile = paste0('x.cv.train_', i, '.desc'),
backingpath = cv.dir)
cv.args$X <- attach.big.matrix(paste0(cv.dir, 'x.cv.train_', i, '.desc'))
cat("CV fold #", i, "\t--Copy training-- End time: ", format(Sys.time()), "\n\n")
cat("CV fold #", i, "\t--Copy test-- Start time: ", format(Sys.time()), "\n\n")
## physically copy remaining part of big.matrix for testing
idx.test <- which(cv.ind == i) ## get row idx for i-th fold testing
deepcopy(XX, rows = idx.test, type = 'double',
backingfile = paste0('x.cv.test_', i, '.bin'),
descriptorfile = paste0('x.cv.test_', i, '.desc'),
backingpath = cv.dir)
X2 <- attach.big.matrix(paste0(cv.dir, 'x.cv.test_', i, '.desc'))
cat("CV fold #", i, "\t--Copy test-- End time: ", format(Sys.time()), "\n\n")
# cv.args$X <- XX[cv.ind!=i, , drop=FALSE]
cv.args$y <- y[cv.ind!=i]
cv.args$warn <- FALSE
cat("CV fold #", i, "\t--Fit ncvreg-- Start time: ", format(Sys.time()), "\n\n")
## call 'ncvreg' function, fit penalized regression model
fit.i <- ncvreg(X=cv.args$X, y=cv.args$y, family=cv.args$family,
penalty = cv.args$penalty,lambda = cv.args$lambda, convex = cv.args$convex)
# fit.i <- do.call("ncvreg", cv.args)
cat("CV fold #", i, "\t--Fit ncvreg-- End time: ", format(Sys.time()), "\n\n")
y2 <- y[cv.ind==i]
yhat <- matrix(predict(fit.i, X2, type="response"), length(y2))
loss <- loss.ncvreg(y2, yhat, fit.i$family)
pe <- if (fit.i$family=="binomial") {(yhat < 0.5) == y2} else NULL
list(loss=loss, pe=pe, nl=length(fit.i$lambda), yhat=yhat)
}
ここまでは、計画行列X
が大きすぎない場合 (n=1000, p=100,000
サイズが ~1GB の場合)、コードは非常にうまく機能します。しかし、p=1,000,000
それによって のサイズX
が ~10GB になると、各コアでのモデル フィッティング手順が無限に実行されます!!!!! (次の部分):
#...
cat("CV fold #", i, "\t--Fit ncvreg-- Start time: ", format(Sys.time()), "\n\n")
## call 'ncvreg' function, fit penalized regression model
fit.i <- ncvreg(X=cv.args$X, y=cv.args$y, family=cv.args$family,
penalty = cv.args$penalty,lambda = cv.args$lambda, convex = cv.args$convex)
# fit.i <- do.call("ncvreg", cv.args)
cat("CV fold #", i, "\t--Fit ncvreg-- End time: ", format(Sys.time()), "\n\n")
#...
注:
- 生のマトリックス (10GB) で 'ncvreg()' を 1 回実行すると、約 2.5 分かかります。
for
ループではなくループを使用してクロス検証を順次実行するmclapply
と、コードは正常に機能し、各フォールド 'ncvreg()' のモデル フィッティングも正常に機能します (~2 分) が、プロセス全体には ~25 分かかります。- 最初は同じ問題で「parLapply」を試しました。ここの理由により、「mclapply」に切り替えました。
- 各コアのデータ コピー ステップ (
deepcopy
部分) は適切に機能し、トレーニング データ セットとテスト データ セットをディスクにコピーしてファイルでバックアップするのに約 2 分かかります。 - CPU 使用率を監視しようとしました。以下は 1 つのスクリーンショットです。左の図でわかるように、4 つのセッションのそれぞれが最大 25% の CPU 使用率を占めていますが、1 つのプロセスでは、kernel_taskが最大 100% の CPU を使用しています。時間が経過すると、kernel_taskは 150% の CPU を使用することさえあります。さらに、CPU 履歴パネル (右下) は、赤色の領域が緑色の領域を支配しているため、ほとんどの CPU 使用率がユーザーではなくシステムによるものであることを示しています。
私の質問:
- モデル フィッティング プロセスを並列に実行すると、時間がかかるのはなぜですか? 考えられる理由は何ですか?
- big.matrix の CV 手順を並列化する正しい方向に進んでいますか? 別の方法はありますか?
ここで私の問題を解決するのに役立つ洞察に感謝します。前もって感謝します!!