2011-08-22 8 views
7

Ho creato diversi modelli di ctree (da circa 40 a 80) che voglio valutare piuttosto spesso.Come rimuovere i dati di allenamento dai modelli party ::: ctree?

Un problema è che gli oggetti del modello sono molto grandi (40 modelli richiedono più di 2,8 G di memoria) e mi sembra, che hanno memorizzato i dati di allenamento, forse come nome modello @ dati e nome modello @ risposte, e non solo le informazioni rilevanti per prevedere nuovi dati.

La maggior parte degli altri pacchetti di apprendimento R dispone di opzioni configurabili per includere i dati nell'oggetto del modello, ma non sono riuscito a trovare alcun suggerimento nella documentazione. Ho anche cercato di assegnare gli oggetti vuoti ModelEnv da

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

ma non c'era alcun effetto sulle dimensioni del rispettivo file RDATA.

Qualcuno sa se Ctree memorizza realmente i dati di allenamento e come rimuovere tutti i dati dai modelli ctree che sono irrilevanti per le nuove previsioni in modo che possa adattarli a molti di essi in memoria?

Grazie mille,

Stefan


Grazie per il tuo feedback, che era già molto utile.

Ho usato dput e str di dare uno sguardo più approfondito l'oggetto e hanno trovato che nessun dato di formazione è incluso nel modello, ma c'è uno slot responses, che sembra avere le etichette di formazione e rownames. Ad ogni modo, ho notato che ogni nodo ha un vettore di peso per ogni campione di allenamento. Dopo un po 'di ispezionare il codice, ho finito per googling un po' e ho trovato il seguente commento nel registro 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). 

Si scopre, c'è una funzione C nel pacchetto partito per rimuovere questi pesi chiamati R_remove_weights con la seguente definizione:

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

funziona anche bene:

# 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") 

Come si può vedere, si riduce la dimensione dell'oggetto subst antialmente, da circa 2,5 MB a 1,5 MB.

Ciò che è strano, però, è che i file RDATA corrispondenti sono follemente enorme, e non v'è alcun impatto su di loro:

$ 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 

estrazione dal file mostra l'oggetto 2.5MB di occupare quasi 100MB di spazio:

$ 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 

Qualche idea, cosa potrebbe causare questo?

+0

Se si potesse pubblicare il risultato di 'dput' su un piccolo oggetto campione che sarebbe di aiuto. Se usi 'str' puoi vedere se ci sono dati di allenamento memorizzati nell'oggetto. –

+0

Nota: ho riadattato la domanda e rimosso l'etichetta "party" in quanto non è ben definita su SO, certamente non limitata al pacchetto R. Ho aggiunto gestione della memoria in quanto sembra essere la motivazione principale. – Iterator

risposta

5

Ho trovato una soluzione al problema, quindi scrivo questa risposta se qualcuno potrebbe incontrare lo stesso problema. Descriverò il mio processo, quindi potrebbe essere un po 'sconclusionato, quindi abbi pazienza con me.

Senza alcun indizio, ho pensato agli slot per il nuking e alla rimozione dei pesi per ottenere gli oggetti il ​​più piccoli possibile e almeno di risparmiare un po 'di memoria, nel caso in cui non venga trovata alcuna correzione. Così ho rimosso @data e @responses come inizio e la previsione è andata ancora bene senza di loro, ma nessun effetto sulla dimensione del file .RData.

ho l'andato il contrario e ha creato e il modello vuoto Ctree, appena pluging l'albero in esso:

> library(party) 

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

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

Controllo delle dimensioni dell'oggetto originale:

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

Ora, cerchiamo di crea un CTree vuoto e copia solo l'albero:

## 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") 

Questo nuovo oggetto ad albero ora è molto piccolo er:

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

Tuttavia, essa non può essere usato per predire:

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

non abbiamo impostato il @cond_distr_response, che potrebbe causare l'errore, in modo da copiare quello originale come bene e cercare di prevedere ancora:

## 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 

Questo funziona perfettamente, ma ora la dimensione del file RDATA è tornato al suo valore originale:

$ 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 

stampare semplicemente slot, dimostra di essere una funzione legata a un 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> 

Quindi la risposta alla domanda iniziale sembra essere che i metodi dell'oggetto legano un ambiente ad esso, che viene quindi salvato con l'oggetto nel file RData corrispondente. Questo potrebbe anche spiegare il motivo per cui diversi pacchetti vengono caricati quando viene letto il file RData.

Quindi, per eliminare l'ambiente, non possiamo copiare i metodi, ma non possiamo neanche prevedere senza di essi. La soluzione piuttosto "sporca" è quella di emulare la funzionalità dei metodi originali e chiamare direttamente il codice C sottostante. Dopo qualche ricerca nel codice sorgente, questo è davvero possibile. Come suggerisce il codice sopra riportato, dobbiamo chiamare get_where, che determina il nodo terminale dell'albero raggiunto dall'input. Abbiamo quindi bisogno di chiamare R_getpredictions per determinare la risposta da quel nodo terminale per ogni campione di input.La parte difficile è che abbiamo bisogno di ottenere i dati nel formato di input a destra e quindi chiamare la pre-elaborazione dei dati inclusi nel 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 

Ora solo bisogno di salvare l'albero estratto e la stringa formula per essere in grado di prevedere nuovi dati:

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

possiamo rimuovere ulteriormente i pesi non necessari, come descritto nella domanda aggiornato sopra:

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

Ora diamo uno sguardo alla fi le dimensioni ancora:

$ 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 

Infine, invece di (compresso) 9.6M, solo 43K sono tenuti a utilizzare il modello. Ora dovrei essere in grado di adattarne quanti ne voglio nel mio spazio di memoria 3G. Evviva!

+0

+1 Questa è una grande presentazione passo dopo passo. Bel lavoro. – Iterator

1

Quello che stai cercando è rimuovere gli slot. Una parola di cautela: questo potrebbe essere piuttosto pericoloso dato come le funzioni party funzionano con l'oggetto.

Tuttavia, dare un'occhiata a slotNames(yourModel). Puoi anche provare object.size(slot(yourModel), slotNameOfInterest) per esaminare la dimensione di diversi slot. È possibile creare facilmente una tabella ordinata per essere sicuri delle dimensioni degli oggetti in ogni slot.

In ogni caso, lo slot per data è un oggetto ModelEnvFormula (che chiamerò questo "MEF"). È possibile creare un MEF fittizio: dummyMEF <- ModelEnvFormula(1 ~ 1) e assegnarlo a data: slot(yourModel, "data") <- dummyMEF.

Che nuke quel particolare slot. Si dovrebbe dare un'occhiata per vedere se ci sono altri slot che stanno causando mal di testa in termini di archiviazione - la funzione object.size() sarà di aiuto. Sono d'accordo che è bello poter omettere i dati di allenamento dall'oggetto del modello.

Problemi correlati