2011-08-22 9 views
7

Creé varios modelos de ctree (de 40 a 80) que quiero evaluar con bastante frecuencia.Cómo eliminar datos de entrenamiento de party ::: ctree models?

Un problema es que los objetos modelo son muy grandes (40 modelos requieren más de 2.8G de memoria) y me parece que almacenaron los datos de entrenamiento, quizás como nombrede modelo @ datos y nombrede modelo @ respuestas, y no solo las informaciones relevantes para predecir nuevos datos.

La mayoría de los otros paquetes de aprendizaje R tienen opciones configurables para incluir los datos en el objeto modelo, pero no pude encontrar ninguna pista en la documentación. También probé para asignar objetos ModelEnv vacías por

[email protected] <- new("ModelEnv") 

pero no había ningún efecto sobre el tamaño del archivo rdata respectiva.

¿Alguien sabe si ctree realmente almacena los datos de entrenamiento y cómo eliminar todos los datos de los modelos de ctree que son irrelevantes para las nuevas predicciones de modo que pueda encajar muchos de ellos en la memoria?

Muchas gracias,

Stefan


Gracias por su colaboración, que ya era muy útil.

que utilizan dput y str para tomar una mirada más profunda en el objeto y se encontró que no hay datos de entrenamiento se incluye en el modelo, pero hay una ranura responses, que parece tener las etiquetas de formación y rownames. De todos modos, noté que cada nodo tiene un vector de peso para cada muestra de entrenamiento. Después de un tiempo de inspeccionar el código, terminé googlear un poco y encontré el siguiente comentario en el registro de party NOTICIAS:

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

Resulta, hay una función de C en el paquete de fiesta para quitar estos pesos llamados R_remove_weights con la siguiente definición:

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

también funciona bien:

# 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", [email protected], 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") 

Como se puede ver, se reduce el tamaño del objeto subst antialmente, de aproximadamente 2.5MB a 1.5MB.

Lo que es extraño, sin embargo, es que los archivos correspondientes rdata son increíblemente enorme, y no hay impacto en ellos:

$ 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 

de descomprimir el archivo muestra el objeto de ocupar casi 2,5 MB 100 MB de espacio:

$ 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 

¿Alguna idea, qué podría causar esto?

+0

Si pudiera publicar el resultado de 'dput' en un pequeño objeto de muestra que ayudaría. Si usa 'str' puede ver si hay datos de entrenamiento almacenados en el objeto. –

+0

Nota: Retuve la pregunta y eliminé la etiqueta de "fiesta" ya que no está bien definida en SO, ciertamente no está restringida al paquete R. Añadí administración de memoria ya que esa parece ser la principal motivación. – Iterator

Respuesta

5

Encontré una solución al problema en cuestión, así que escribo esta respuesta si alguien puede encontrarse con el mismo problema. Describiré mi proceso, por lo que podría ser un poco divagante, así que tengan paciencia conmigo.

Sin idea, pensé en destruir las ranuras y quitar los pesos para hacer que los objetos sean lo más pequeños posible y al menos guardar algo de memoria, en caso de que no se encuentre ninguna solución. Así que eliminé @data y @responses como un comienzo y la predicción siguió siendo buena sin ellos, sin embargo, no tuvo efecto en el tamaño del archivo .RData.

me la pasó al revés y ha creado y vacío modelo ctree, simplemente pluging el árbol en él:

> library(party) 

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

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

Comprobación del tamaño del objeto original:

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

Ahora, vamos a crear un CTree vacío y copiar el árbol solamente:

## extract the tree only 
> c1Tree <- [email protected] 

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

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

Este nuevo objeto de árbol ahora es mucho más pequeño ER:

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

Sin embargo, no se puede utilizar para predecir:

## predict with the new tree 
> predictions.new <- treeresponse(newCTree, d) 
Error in [email protected]_distr_response(newdata = newdata, ...) : 
    unused argument(s) (newdata = newdata) 

no hemos fijado la @cond_distr_response, que puede provocar el error, por lo que la copia del original, así y tratar de predecir una vez más:

## extract cond_distr_response from original tree 
> cdr <- [email protected]_distr_response 
> [email protected]_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 

Esto funciona perfectamente, pero ahora el tamaño del archivo rdata está de vuelta en su valor original:

$ 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 

Simplemente la impresión de la ranura, muestra que es una función unida a un medio ambiente:

> [email protected]_distr_response 
function (newdata = NULL, mincriterion = 0, ...) 
{ 
    wh <- [email protected]_where(newdata = newdata, mincriterion = mincriterion) 
    response <- [email protected] 
    if (any([email protected]_censored)) { 
     swh <- sort(unique(wh)) 
     RET <- vector(mode = "list", length = length(wh)) 
     resp <- [email protected][[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> 

Así que la respuesta a la pregunta inicial parece ser que los métodos del objeto se unen a un entorno a él, que luego se guarda con el objeto en el archivo RData correspondiente. Esto también podría explicar por qué se cargan varios paquetes cuando se lee el archivo RData.

Por lo tanto, para deshacerse del medio ambiente, no podemos copiar los métodos, pero tampoco podemos predecirlos sin ellos. La solución más bien "sucia" es emular la funcionalidad de los métodos originales y llamar directamente al código C subyacente. Después de buscar el código fuente, esto es posible. Como sugiere el código copiado anteriormente, necesitamos llamar al get_where, que determina el nodo terminal del árbol al que llega la entrada. Luego debemos llamar al R_getpredictions para determinar la respuesta de ese nodo terminal para cada muestra de entrada.La parte difícil es que necesitamos para obtener los datos en el formato correcto y por lo tanto tienen que llamar a la pre-procesamiento de datos incluido en 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([email protected]@formula$response[[2]]))), 
        "~", 
        do.call(str_c, as.list(deparse([email protected]@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([email protected]@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 

Ahora sólo tiene que salvar el árbol extraído y la cadena fórmula para ser capaz de predecir nuevos datos:

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

podemos eliminar aún más los pesos innecesarios como se describe en la pregunta anterior actualizado:

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

Ahora vamos a echar un vistazo a la fi Le tamaños de nuevo:

$ 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 

Finalmente, en lugar de 9,6 m (comprimido), solamente 43K están obligados a utilizar el modelo. Ahora debería poder incluir tantos como quiera en mi espacio de almacenamiento dinámico 3G. ¡Hurra!

+0

+1 Esa es una gran presentación paso a paso. Buen trabajo. – Iterator

1

Lo que estás buscando es eliminar las ranuras. Una advertencia: esto podría ser bastante peligroso dado que las funciones party funcionan con el objeto.

No obstante, eche un vistazo a slotNames(yourModel). También puede probar object.size(slot(yourModel), slotNameOfInterest) para examinar el tamaño de las diferentes ranuras. Puede crear fácilmente una tabla ordenada para asegurarse de los tamaños de los objetos en cada ranura.

En cualquier caso, la ranura para data es un objeto ModelEnvFormula (voy a llamar a esto "MEF"). Puede crear un MEF ficticio: dummyMEF <- ModelEnvFormula(1 ~ 1) y luego asignarlo al data: slot(yourModel, "data") <- dummyMEF.

Eso nukeará esa ranura en particular. Debería echar un vistazo para ver si hay otras ranuras que están causando dolores de cabeza en términos de almacenamiento: la función object.size() lo ayudará. Estoy de acuerdo en que es bueno poder omitir los datos de entrenamiento del objeto modelo.

Cuestiones relacionadas