2011-07-01 8 views
18

Cuando entreno solo usando glm, todo funciona, y ni siquiera estoy cerca de agotar la memoria. Pero cuando ejecuto train(..., method='glm'), me quedo sin memoria.¿Por qué el tren de cuidados ocupa tanta memoria?

¿Esto se debe a que train está almacenando una gran cantidad de datos para cada iteración de la validación cruzada (o cualquiera que sea el procedimiento trControl)? Estoy mirando trainControl y no puedo encontrar cómo prevenir esto ... ¿alguna pista? Solo me importa el resumen del rendimiento y quizás las respuestas previstas.

(ya sé que no está relacionado con el almacenamiento de los datos de cada iteración de la red de búsqueda de parámetros de ajuste, porque no hay rejilla de GLM, creo.)

+1

¿Desea hacer un pequeño ejemplo reproducible para que otros lo intenten? –

Respuesta

34

El problema es doble. i)train no sólo encajan un modelo a través de glm(), será bootstrap que el modelo, lo que incluso con los valores predeterminados, train() hará 25 muestras de arranque, que, junto con el problema de ii) es la (o un) fuente de su problema, y ​​ii)train() simplemente llama a la función glm() con sus valores predeterminados. Y esos valores predeterminados son para almacenar el marco del modelo (argumento model = TRUE de ?glm), que incluye una copia de los datos en el estilo de marco del modelo. El objeto devuelto por train() ya almacena una copia de los datos en $trainingData, y el objeto "glm" en $finalModel también tiene una copia de los datos reales.

En este punto, simplemente ejecutando glm() usando train() será producir 25 copias de la completamente expandida model.framey los datos originales, que todos necesitaremos que tendrá lugar en la memoria durante el proceso de remuestreo - si éstas se llevan a cabo al mismo tiempo o consecutivamente no se ve inmediatamente después de un vistazo rápido al código ya que el remuestreo ocurre en una llamada lapply(). También habrá 25 copias de los datos sin procesar.

Una vez que finaliza el remuestreo, el objeto devuelto contendrá 2 copias de los datos brutos y una copia completa de model.frame. Si sus datos de entrenamiento son grandes en relación con la RAM disponible o contiene muchos factores para expandir en el model.frame, entonces podría estar usando grandes cantidades de memoria simplemente llevando consigo copias de los datos.

Si agrega model = FALSE a su llamada de tren, eso puede hacer la diferencia. He aquí un pequeño ejemplo utilizando los datos de clotting?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) 

continuación

> 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 

lo que hay una diferencia de tamaño en el uso de objetos y memoria devuelta durante el entrenamiento será menor. Cuánto más bajo dependerá de si las partes internas de train() guardan todas las copias del model.frame en la memoria durante el proceso de remuestreo.

El objeto devuelto por train() también es significativamente más grande que el devuelto por glm() - como se menciona por @DWin en los comentarios, a continuación.

Para llevar esto más lejos, o estudie el código más de cerca, o envíe un correo electrónico a Max Kuhn, el mantenedor de caret, para consultar las opciones para reducir la huella de memoria.

+1

Buena respuesta (como típica para ti, Gavin). Solo agregaría el tamaño del objeto glm: '> m3 = glm (lot1 ~ log (u), data = coagulación, familia = Gamma) > object.size (m3) 47272 bytes' –

+0

@Dwin Gracias, y bueno punto. Agregaré ese resultado a la Respuesta, con atribución de curso. –

+0

Gracias, le pedí a Max que agregue una respuesta aquí. – Yang

27

La respuesta de Gavin es perfecta. Creé la función para facilitar el uso en lugar de velocidad o eficiencia [1]

Primero, usar la interfaz de fórmula puede ser un problema cuando tienes muchos predictores. Esto es algo que R Core podría arreglar; el enfoque de fórmula requiere una matriz terms() muy grande pero escasa para ser retenida y R tiene paquetes para tratar con eficacia ese problema. Por ejemplo, con n = 3, 000 yp = 2, 000, un objeto de bosque aleatorio de 3 árboles era 1,5 veces más grande y tardaba 23 veces más en ejecutarse cuando se usaba la interfaz de fórmula (282s frente a 12s).

En segundo lugar, no es necesario que guarde los datos de entrenamiento (consulte el argumento returnData en trainControl()).

Además, dado que R no tiene ninguna infraestructura real de memoria compartida, Gavin tiene razón acerca del número de copias de los datos que se conservan en la memoria. Básicamente, se crea una lista para cada remuestreo y se usa lapply() para procesar la lista, luego solo se devuelven las estimaciones remuestreadas. Una alternativa sería hacer secuencialmente una copia de los datos (para la nueva muestra actual), hacer las operaciones requeridas, luego repetir para las iteraciones restantes. El problema es la E/S y la imposibilidad de hacer un procesamiento paralelo. [2]

Si tiene un conjunto de datos grande, sugiero usar la interfaz sin fórmula (aunque el modelo real, como glm, finalmente usa una fórmula). Además, para grandes conjuntos de datos, train() guarda los índices de remuestreo para su uso por resamples() y otras funciones. Probablemente también puedas eliminarlos.

Yang - sería bueno saber más sobre los datos a través de str(data) para que podamos entender las dimensiones y otros aspectos (por ejemplo, factores con muchos niveles, etc.).

Espero que ayude,

Max

[1] no debería que hacen todo lo posible para encajar el menor número de modelos como sea posible cuando podemos. El truco del "submodelo" se utiliza para muchos modelos, como pls, gbm, rpart, earth y muchos otros. Además, cuando un modelo tiene interfaces de la fórmula y no fórmula (por ejemplo. lda() o earth(), que por defecto la interfaz no fórmula.

[2] De vez en cuando consiguen el impulso loco para reiniciar la función train() . Usar foreach podría resolver algunos de estos problemas.

+0

Bienvenido a SO @Max, y gracias por la respuesta informativa. Me alegra que hayas escrito 'train()' para facilitar su uso; Lo he estado usando recientemente para aumentar el gradiente estocástico y haber escrito algún código de ajuste. ¡Fue una revelación cambiar a ** caret ** y 'train()'! –

+0

Estoy suministrando mi propio modelo de matriz y vector de respuesta (necesariamente para que pueda usar 'findCorrelation'), así que no uso la interfaz de fórmula de ningún modelo. ¿Cuál es el truco del submodelo? – Yang

+1

¿Qué paquetes son los que ha mencionado para tratar el problema de uso de memoria de la fórmula? "y R tiene paquetes para lidiar efectivamente con ese problema" – Eduardo

1

Creo que las respuestas anteriores están un poco desactualizadas. Los paquetes caret y caretEnsemble ahora incluyen un parámetro adicional en trainControl 'trim'. Trim inicialmente está configurado en FALSE pero lo cambia VERDADERO disminuirá significativamente el tamaño del modelo. Debe usar esto en combinación con returnData = FALSE para los tamaños de modelo más pequeños posibles. Si está usando un conjunto de modelos, también debe especificar estos dos pares Amperadores en el conjunto codicioso/pila trainControl.

En mi caso, un modelo de 1.6 gb redujo a ~ 500 mb con ambos parámetros en el control de conjunto y se redujo a ~ 300 mb también utilizando los parámetros en el codicioso control de conjunto.

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)) 
Cuestiones relacionadas