7

かなり頻繁に評価したいいくつかのctreeモデル(約40から80)を作成しました。

問題は、モデルオブジェクトが非常に大きく(40モデルは2.8G以上のメモリを必要とする)、関連する情報だけでなく、おそらくmodelname@dataおよびmodelname@responsesとしてトレーニングデータを保存しているように見えることです。新しいデータを予測します。

他のほとんどのR学習パッケージには、モデルオブジェクトにデータを含めるかどうかを構成できるオプションがありますが、ドキュメントにヒントが見つかりませんでした。また、空のModelEnvオブジェクトを次の方法で割り当てようとしました

modelname@data <- new("ModelEnv")

ただし、それぞれのRDataファイルのサイズには影響しませんでした。

ctreeが実際にトレーニングデータを保存しているかどうか、および新しい予測に関係のないctreeモデルからすべてのデータを削除して、それらの多くをメモリに収めることができるようにする方法を知っている人はいますか?

どうもありがとう、

ステファン


フィードバックをありがとうございます、それはすでに非常に役に立ちました。

オブジェクトを詳しく調べてみるdputstr、モデルにトレーニングデータが含まれていないことがわかりましたresponsesが、トレーニングラベルと行名があるように見えるスロットがあります。とにかく、各ノードには各トレーニングサンプルの重みベクトルがあることに気づきました。コードをしばらく調べた後、私は少しグーグルしてしまい、partyNEWSログに次のコメントを見つけました。

         CHANGES IN party VERSION 0.9-13 (2007-07-23)

o   update `mvt.f'

o   improve the memory footprint of RandomForest objects
    substancially (by removing the weights slots from each node).

R_remove_weightsパーティーパッケージには、次の定義で呼び出されたこれらの重みを削除するC関数が含まれていることがわかりました。

SEXP R_remove_weights(SEXP subtree, SEXP removestats) {
    C_remove_weights(subtree, LOGICAL(removestats)[0]);
    return(R_NilValue);
}

また、正常に動作します。

# cc is my model object

sum(unlist(lapply(slotNames(cc), function (x)  object.size(slot(cc, x)))))
# returns: [1] 2521256
save(cc, file="cc_before.RData")

.Call("R_remove_weights", cc@tree, TRUE, PACKAGE="party")
# returns NULL and removes weights and node statistics

sum(unlist(lapply(slotNames(cc), function (x)  object.size(slot(cc, x)))))
# returns: [1] 1521392
save(cc, file="cc_after.RData")

ご覧のとおり、オブジェクトのサイズが約2.5MBから1.5MBに大幅に削減されています。

ただし、奇妙なのは、対応するRDataファイルがめちゃくちゃ巨大であり、それらに影響がないことです。

$ ls -lh cc*
-rw-r--r-- 1 user user 9.6M Aug 24 15:44 cc_after.RData
-rw-r--r-- 1 user user 9.6M Aug 24 15:43 cc_before.RData

ファイルを解凍すると、2.5MBのオブジェクトが100MB近くのスペースを占有していることがわかります。

$ cp cc_before.RData cc_before.gz
$ gunzip cc_before.gz 
$ ls -lh cc_before*
-rw-r--r-- 1 user user  98M Aug 24 15:45 cc_before

何かアイデア、これを引き起こす可能性があるのは何ですか?

4

2 に答える 2

5

手元にある問題の解決策を見つけたので、誰かが同じ問題に遭遇する可能性がある場合は、この回答を書きます。私のプロセスを説明しますので、少しとりとめのないかもしれませんので、我慢してください。

手がかりがないので、修正が見つからない場合に備えて、オブジェクトをできるだけ小さくし、少なくともメモリを節約するために、スロットを削除してウェイトを削除することを考えました。だから私は削除@data@responses、最初にそれらがなくても予測はうまくいきましたが、.RDataファイルのサイズには影響しませんでした。

私は逆に行き、ツリーをプラグインするだけで、空のctreeモデルを作成しました。

> library(party)

## create reference predictions for the dataset
> predictions.org <- treeresponse(c1, d)

## save tree object for reference
save(c1, "testSize_c1.RData")

元のオブジェクトのサイズを確認する:

$ ls -lh testSize_c1.RData 
-rw-r--r-- 1 user user 9.6M 2011-08-25 14:35 testSize_c1.RData

次に、空のCTreeを作成し、ツリーのみをコピーしてみましょう。

## extract the tree only 
> c1Tree <- c1@tree

## create empty tree and plug in the extracted one 
> newCTree <- new("BinaryTree")
> newCTree@tree <- c1Tree

## save tree for reference 
save(newCTree, file="testSize_newCTree.RData")

この新しいツリーオブジェクトは、はるかに小さくなりました。

$ ls -lh testSize_newCTree.RData 
-rw-r--r-- 1 user user 108K 2011-08-25 14:35 testSize_newCTree.RData

ただし、次の予測には使用できません。

## predict with the new tree
> predictions.new <- treeresponse(newCTree, d)
Error in object@cond_distr_response(newdata = newdata, ...) : 
  unused argument(s) (newdata = newdata)

エラーの原因となる可能性のあるを設定しなかった@cond_distr_responseため、元のをコピーして、もう一度予測してみてください。

## extract cond_distr_response from original tree
> cdr <- c1@cond_distr_response
> newCTree@cond_distr_response <- cdr

## save tree for reference 
save(newCTree, file="testSize_newCTree_with_cdr.RData")

## predict with the new tree
> predictions.new <- treeresponse(newCTree, d)

## check correctness
> identical(predictions.org, predictions.new)
[1] TRUE

これは完全に機能しますが、RDataファイルのサイズは元の値に戻ります。

$ ls -lh testSize_newCTree_with_cdr.RData 
-rw-r--r-- 1 user user 9.6M 2011-08-25 14:37 testSize_newCTree_with_cdr.RData

スロットを印刷するだけで、環境にバインドされた関数であることがわかります。

> c1@cond_distr_response
function (newdata = NULL, mincriterion = 0, ...) 
{
    wh <- RET@get_where(newdata = newdata, mincriterion = mincriterion)
    response <- object@responses
    if (any(response@is_censored)) {
        swh <- sort(unique(wh))
        RET <- vector(mode = "list", length = length(wh))
        resp <- response@variables[[1]]
        for (i in 1:length(swh)) {
            w <- weights * (where == swh[i])
            RET[wh == swh[i]] <- list(mysurvfit(resp, weights = w))
        }
        return(RET)
    }
    RET <- .Call("R_getpredictions", tree, wh, PACKAGE = "party")
    return(RET)
}
<environment: 0x44e8090>

したがって、最初の質問に対する答えは、オブジェクトのメソッドが環境をそれにバインドし、対応するRDataファイルにオブジェクトとともに保存されるということのようです。これは、RDataファイルが読み取られるときに複数のパッケージがロードされる理由も説明している可能性があります。

したがって、環境を取り除くためにメソッドをコピーすることはできませんが、メソッドなしで予測することもできません。かなり「汚い」解決策は、元のメソッドの機能をエミュレートし、基になるCコードを直接呼び出すことです。ソースコードを掘り下げた後、これは確かに可能です。上でコピーしたコードが示すように、を呼び出す必要がありget_whereます。これは、入力が到達するツリーのターミナルノードを決定します。R_getpredictions次に、各入力サンプルについて、そのターミナルノードからの応答を決定するために呼び出す必要があります。トリッキーな部分は、正しい入力形式でデータを取得する必要があるため、ctreeに含まれているデータ前処理を呼び出す必要があることです。

## create a character string of the formula which was used to fit the free
## (there might be a more neat way to do this)
> library(stringr)
> org.formula <- str_c(
                   do.call(str_c, as.list(deparse(c1@data@formula$response[[2]]))),
                   "~", 
                   do.call(str_c, as.list(deparse(c1@data@formula$input[[2]]))))

## call the internal ctree preprocessing 
> data.dpp <- party:::ctreedpp(as.formula(org.formula), d)

## create the data object necessary for the ctree C code
> data.ivf <- party:::initVariableFrame.df(data.dpp@menv@get("input"), 
                                           trafo = ptrafo)

## now call the tree traversal routine, note that it only requires the tree
## extracted from the @tree slot, not the whole object
> nodeID <- .Call("R_get_nodeID", c1Tree, data.ivf, 0, PACKAGE = "party")

## now determine the respective responses
> predictions.syn <- .Call("R_getpredictions", c1Tree, nodeID, PACKAGE = "party")

## check correctness
> identical(predictions.org, predictions.syn)
[1] TRUE

新しいデータを予測できるようにするには、抽出したツリーと数式文字列を保存するだけで済みます。

> save(c1Tree, org.formula, file="testSize_extractedObjects.RData")

上記の更新された質問で説明されているように、不要な重みをさらに削除できます。

> .Call("R_remove_weights", c1Tree, TRUE, PACKAGE="party")
> save(c1Tree, org.formula, file="testSize_extractedObjects__removedWeights.RData")

次に、ファイルサイズをもう一度見てみましょう。

$ ls -lh testSize_extractedObjects*
-rw-r--r-- 1 user user 109K 2011-08-25 15:31 testSize_extractedObjects.RData
-rw-r--r-- 1 user user  43K 2011-08-25 15:31 testSize_extractedObjects__removedWeights.RData

最後に、(圧縮された)9.6Mの代わりに、モデルを使用するために必要なのは43Kだけです。これで、3Gヒープスペースに必要な数だけ収まるようになります。やったー!

于 2011-08-25T13:42:27.860 に答える
1

あなたが探しているのはスロットを取り除くことです。注意点:party関数がオブジェクトとどのように連携するかを考えると、これはかなり危険な場合があります。

それにもかかわらず、を見てくださいslotNames(yourModel)object.size(slot(yourModel), slotNameOfInterest)さまざまなスロットのサイズを調べることもできます。ソートされたテーブルを簡単に作成して、各スロットのオブジェクトのサイズを確認できます。

いずれにせよ、のスロットdataModelEnvFormula(これを「MEF」と呼びます)オブジェクトです。ダミーのMEF:を作成し、それを:に割り当てることができdummyMEF <- ModelEnvFormula(1 ~ 1)ます。dataslot(yourModel, "data") <- dummyMEF

それはその特定のスロットを核にします。ストレージの面で頭痛の種を引き起こしている他のスロットがあるかどうかを確認する必要があります-object.size()関数が支援します。モデルオブジェクトからトレーニングデータを省略できるのは良いことだと私は同意します。

于 2011-08-22T16:05:47.473 に答える