Как удалить тренировочные данные из моделей party ::: ctree? - PullRequest
7 голосов
/ 22 августа 2011

Я создал несколько моделей ctree (от 40 до 80), которые я хочу оценивать довольно часто.

Проблема заключается в том, что объекты модели очень большие (40 моделей требуют более 2,8 ГБ памяти), и мне кажется, что они сохранили обучающие данные, возможно, в виде имя модели @ данные и имя модели @ ответы,а не только информация, имеющая отношение к предсказанию новых данных.

Большинство других обучающих пакетов R имеют настраиваемые параметры, включать ли данные в объект модели, но я не смог найти никаких подсказок в документации.Я также пытался назначить пустые объекты ModelEnv с помощью

modelname@data <- new("ModelEnv")

, но это не повлияло на размер соответствующего файла RData.

Кто-нибудь знает, действительно ли ctree хранит обучающие данные и как удалить все данные из моделей ctree, которые не имеют отношения к новым прогнозам, чтобы я мог разместить многие из них в памяти?

Большое спасибо,

Stefan


Спасибо за ваш отзыв, который уже был очень полезен.

Я использовал dput и str, чтобы глубже взглянуть наобъект и обнаружил, что в модель не включены данные обучения, но есть слот responses, который, кажется, имеет метки обучения и имена строк.В любом случае, я заметил, что у каждого узла есть весовой вектор для каждой обучающей выборки.Через некоторое время проверки кода я немного погуглил и нашел следующий комментарий в журнале party NEWS:

         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).

Оказывается, в пакете party есть функция C дляуберите эти веса, называемые R_remove_weights, со следующим определением:

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,5МБ до 1,5 МБ.

Что странно, так это то, что соответствующие файлы 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.5Объект MB занимает почти 100 МБ пространства:

$ 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

Есть идеи, что может вызвать это?

Ответы [ 2 ]

5 голосов
/ 25 августа 2011

Я нашел решение проблемы под рукой, поэтому я пишу этот ответ, если кто-то может столкнуться с той же проблемой. Я опишу свой процесс, так что он может быть немного бессвязным, так что терпите меня.

Понятия не имею, я думал об обнулении слотов и удалении весов, чтобы получить как можно меньше объектов и, по крайней мере, сэкономить память, если не будет найдено исправлений. Поэтому я удалил @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,6 МБ для использования модели требуется только 43 КБ. Теперь я должен быть в состоянии разместить столько, сколько я хочу в моем пространстве кучи 3G. Ура!

1 голос
/ 22 августа 2011

То, что вы ищете, это удалить слоты. Предостережение: это может быть довольно опасно, учитывая, как party функции работают с объектом.

Тем не менее, взгляните на slotNames(yourModel). Вы также можете попробовать object.size(slot(yourModel), slotNameOfInterest), чтобы проверить размер различных слотов. Вы можете легко создать отсортированную таблицу, чтобы быть уверенным в размерах объектов в каждом слоте.

В любом случае, слот для data - это ModelEnvFormula (я назову это "MEF") объект. Вы можете создать фиктивный MEF: dummyMEF <- ModelEnvFormula(1 ~ 1), а затем присвоить его data: slot(yourModel, "data") <- dummyMEF.

Это обстреляет этот конкретный слот. Вы должны посмотреть, есть ли другие слоты, которые вызывают головные боли с точки зрения хранения - функция object.size() поможет. Я согласен с тем, что было бы неплохо иметь возможность пропустить тренировочные данные из модельного объекта.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...