2011-07-01 7 views
18

Quando mi alleno solo usando glm, tutto funziona e non mi avvicino nemmeno alla memoria estenuante. Ma quando corro train(..., method='glm'), esaurisco la memoria.Perché il treno a pedale occupa tanta memoria?

Ciò è dovuto al fatto che train memorizza molti dati per ogni iterazione della convalida incrociata (o qualunque sia la procedura trControl)? Sto guardando trainControl e non riesco a trovare come evitare questo ... qualche suggerimento? Mi interessa solo il riepilogo delle prestazioni e forse le risposte previste.

(lo so che non è legato a memorizzare i dati da ogni iterazione della ricerca griglia di parametri a punto perché non c'è griglia per GLM di, credo.)

+1

Fare attenzione a fare un piccolo esempio riproducibile per gli altri da provare? –

risposta

34

Il problema è duplice. i)train non significa solo adattarsi un modello tramite glm(), si bootstrap quel modello, quindi, anche con le impostazioni predefinite, train() farà 25 campioni di bootstrap, che, insieme con il problema ii) è il (o a) l'origine del problema e ii)train() chiama semplicemente la funzione glm() con i valori predefiniti. E quelle di default sono per memorizzare il frame del modello (argomento model = TRUE di ?glm), che include una copia dei dati nello stile del frame del modello. L'oggetto restituito da train() memorizza già una copia dei dati in $trainingData e l'oggetto "glm" in $finalModel ha anche una copia dei dati effettivi.

A questo punto, semplicemente eseguendo glm() usando train() sarà produrre 25 copie della completamente espanso model.framee i dati originali, che saranno tutti bisogno di essere contenuto in memoria durante il processo di ricampionamento - se questi sono detenuti simultaneamente o consecutivamente non è immediatamente chiaro da una rapida occhiata al codice mentre il ricampionamento avviene in una chiamata lapply(). Ci saranno anche 25 copie dei dati grezzi.

Al termine del ricampionamento, l'oggetto restituito conterrà 2 copie dei dati grezzi e una copia completa di model.frame. Se i tuoi dati di allenamento sono ampi rispetto alla RAM disponibile o contengono molti fattori da espandere nello model.frame, potresti facilmente utilizzare enormi quantità di memoria semplicemente trasportando copie dei dati in giro.

Se si aggiunge model = FALSE alla chiamata del treno, ciò potrebbe fare la differenza. Ecco un piccolo esempio utilizzando i dati clotting in ?glm:

clotting <- data.frame(u = c(5,10,15,20,30,40,60,80,100), 
         lot1 = c(118,58,42,35,27,25,21,19,18), 
         lot2 = c(69,35,26,21,18,16,13,12,12)) 
require(caret) 

poi

> m1 <- train(lot1 ~ log(u), data=clotting, family = Gamma, method = "glm", 
+    model = TRUE) 
Fitting: parameter=none 
Aggregating results 
Fitting model on full training set 
> m2 <- train(lot1 ~ log(u), data=clotting, family = Gamma, method = "glm", 
+    model = FALSE) 
Fitting: parameter=none 
Aggregating results 
Fitting model on full training set 
> object.size(m1) 
121832 bytes 
> object.size(m2) 
116456 bytes 
> ## ordinary glm() call: 
> m3 <- glm(lot1 ~ log(u), data=clotting, family = Gamma) 
> object.size(m3) 
47272 bytes 
> m4 <- glm(lot1 ~ log(u), data=clotting, family = Gamma, model = FALSE) 
> object.size(m4) 
42152 bytes 

Quindi c'è una differenza di dimensione nell'uso oggetto e memoria restituita durante l'allenamento sarà inferiore. Quanto inferiore dipenderà dal fatto che le parti interne di train() conservino tutte le copie di model.frame in memoria durante il processo di ricampionamento.

L'oggetto restituito da train() è anche significativamente maggiore di quello restituito da glm() - come indicato da @DWin nei commenti, di seguito.

Per ulteriori informazioni, esaminare il codice più da vicino o inviare a Max Kuhn, manutentore del numero di caret, per informazioni sulle opzioni per ridurre il footprint di memoria.

+1

Buona risposta (tipica per te, Gavin). Aggiungerei solo la dimensione dell'oggetto glm: '> m3 = glm (lotto1 ~ log (u), data = coagulazione, famiglia = gamma) > object.size (m3) 47272 byte –

+0

@Dwin Grazie, e buona punto. Aggiungerò quell'output alla risposta, con attribuzione ovviamente. –

+0

Grazie, ho chiesto a Max di aggiungere una risposta qui. – Yang

27

La risposta di Gavin è azzeccata. Ho costruito la funzione per facilità d'uso piuttosto che per velocità o efficienza [1]

Innanzitutto, l'uso dell'interfaccia formula può essere un problema quando si hanno molti predittori. Questo è qualcosa che R Core potrebbe risolvere; l'approccio formula richiede una matrice molto grande ma sparsa terms() da conservare e R ha pacchetti per affrontare efficacemente quel problema. Ad esempio, con n = 3, 000 e p = 2, 000, un oggetto modello foresta casuale a 3 alberi era 1,5 volte più grande e ha impiegato 23 volte di più per l'esecuzione quando si utilizza l'interfaccia formula (282s vs 12s).

In secondo luogo, non è necessario conservare i dati di allenamento (vedere l'argomento returnData in trainControl()).

Inoltre, poiché R non ha una vera infrastruttura di memoria condivisa, Gavin è corretto sul numero di copie dei dati che vengono conservati in memoria. Fondamentalmente, viene creato un elenco per ogni ricampionamento e viene utilizzato lapply() per elaborare l'elenco, quindi restituire solo le stime ricampionate. Un'alternativa sarebbe quella di creare sequenzialmente una copia dei dati (per il ricampionamento corrente), eseguire le operazioni richieste, quindi ripetere per le restanti iterazioni. Il problema è l'I/O e l'impossibilità di eseguire qualsiasi elaborazione parallela. [2]

Se si dispone di un set di dati di grandi dimensioni, suggerisco di utilizzare l'interfaccia non di formula (anche se il modello attuale, come glm, utilizza eventualmente una formula). Inoltre, per i set di dati di grandi dimensioni, train() salva gli indici di ricampionamento per l'uso da resamples() e altre funzioni. Probabilmente potresti rimuovere anche quelli.

Yang - Sarebbe bello sapere di più sui dati tramite str(data) in modo che possiamo capire le dimensioni e altri aspetti (ad esempio fattori con molti livelli ecc.).

Mi auguro che aiuta,

Max

[1] Io non dovrebbe che noi facciamo di tutto per adattarsi come alcuni modelli come possibile quando possiamo. Il trucco "sotto-modello" è usato per molti modelli, come pls, gbm, rpart, earth e molti altri. Inoltre, quando un modello dispone di interfacce formula e non formula (ad es. lda() o earth(), abbiamo default l'interfaccia non formula.

[2] Ogni tanto un po 'mi viene voglia folle di riavviare il funzionamento train() L'uso di foreach potrebbe aggirare alcuni di questi problemi

+0

Benvenuti in SO @Max e grazie per la risposta informativa. Sono contento che tu abbia scritto 'train()' per facilità d'uso; L'ho usato di recente per aumentare il gradiente stocastico e avendo scritto un po 'di codice di sintonizzazione, è stata una rivelazione passare a ** caret ** e 'train()'! –

+0

Fornisco la mia matrice di modello e il vettore di risposta (necessariamente in modo da poter usare 'findCorrelation'), quindi non uso l'interfaccia di formula di alcun modello. Qual è il trucco sub-modello? – Yang

+1

Quali pacchetti sono quelli che hai menzionato per gestire il problema di utilizzo della memoria della formula? "e R ha pacchetti per affrontare efficacemente quel problema" – Eduardo

1

Penso che le risposte di cui sopra siano un po 'obsolete.I pacchetti di caret e caretEnsemble ora includono un parametro aggiuntivo in trimControl' Trim. 'Trim è inizialmente impostato su FALSE ma modificandolo a TRUE diminuirà sensibilmente la dimensione del modello, dovresti usarlo in combinazione con returnData = FALSE per le taglie più piccole possibili.Se stai usando un ensemble di modelli, dovresti anche specificare questi due valori ametri nel treno di controllo avido/stack ensemble.

Per il mio caso, un modello da 1,6 gb si è ridotto a ~ 500 mb con entrambi i parametri nel controllo dell'ensemble e ulteriormente ridotto a ~ 300 mb utilizzando anche i parametri nel controllo del complesso.

Ensemble_control_A9 <- trainControl(trim=TRUE, method = "repeatedcv", number = 3, repeats = 2, verboseIter = TRUE, returnData = FALSE, returnResamp = "all", classProbs = TRUE, summaryFunction = twoClassSummary, savePredictions = TRUE, allowParallel = TRUE, sampling = "up") 


Ensemble_greedy_A5 <- caretEnsemble(Ensemble_list_A5, metric="ROC", trControl=trainControl(number=2, trim=TRUE, returnData = FALSE, summaryFunction=twoClassSummary, classProbs=TRUE))