¿Por qué el caret train está ocupando tanta memoria?
memory glm (3)
Creo que las respuestas anteriores son un poco anticuadas. Los paquetes caret y caretEnsemble ahora incluyen un parámetro adicional en el ''ajuste'' de trainControl. Inicialmente, el ajuste se establece en FALSO, pero cambiarlo a 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á utilizando un conjunto de modelos, también debe especificar estos dos parámetros en el conjunto de controles codiciosos / apilados.
Para mi caso, un modelo de 1.6 gb se redujo a ~ 500 mb con ambos parámetros en el control de conjunto y se redujo aún más a ~ 300 mb usando también los parámetros en el control de conjunto codicioso.
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))
Cuando entreno usando glm
, todo funciona y ni siquiera me acerco a la memoria agotadora. Pero cuando corro train(..., method=''glm'')
, me quedo sin memoria.
¿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 de control)? Estoy mirando trainControl
y no puedo encontrar cómo prevenir esto ... ¿alguna pista? Solo me importa el resumen de rendimiento y quizás las respuestas predichas.
(Sé que no está relacionado con el almacenamiento de datos de cada iteración de la búsqueda de cuadrícula de ajuste de parámetros porque creo que no hay una cuadrícula para glm''s).
El problema es doble. i) el train
no solo se ajusta a un modelo a través de glm()
, sino que también arrancará ese modelo, por lo que incluso con los valores predeterminados, train()
hará 25 muestras de bootstrap, que, junto con el problema ii) es la fuente (o a ) de su problema, y ii) train()
simplemente llama a la función glm()
con sus valores predeterminados. Y esos valores predeterminados son almacenar el marco del modelo (argumento model = TRUE
de ?glm
), que incluye una copia de los datos en el estilo del 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()
utilizando train()
se producirán 25 copias del model.frame
completamente expandido. model.frame
y datos originales, todos los cuales deberán mantenerse en la memoria durante el proceso de remuestreo, ya sea que se mantengan simultáneamente o De manera consecutiva, no queda claro inmediatamente después de un rápido vistazo al código, ya que el remuestreo ocurre en una llamada lapply()
. También habrá 25 copias de los datos en bruto.
Una vez finalizado el remuestreo, el objeto devuelto contendrá 2 copias de los datos en bruto y una copia completa del model.frame
. Si sus datos de entrenamiento son grandes en relación con la memoria RAM disponible o contienen muchos factores que se pueden expandir en el model.frame
del model.frame
, entonces podría estar usando fácilmente enormes cantidades de memoria que solo llevan copias de los datos.
Si agrega model = FALSE
a su llamada de tren, eso podría marcar la diferencia. Aquí hay un pequeño ejemplo utilizando los datos de clotting
en ?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)
entonces
> 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
Por lo tanto, hay una diferencia de tamaño en el objeto devuelto y el uso de memoria durante el entrenamiento será menor. Cuánto menos dependerá de si las model.frame
internas de train()
mantienen 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 lo menciona @DWin en los comentarios, a continuación.
Para profundizar más en esto, estudie el código más de cerca o envíe un correo electrónico a Max Kuhn, el encargado de la gestión de caret , para preguntar sobre las opciones para reducir la huella de memoria.
La respuesta de Gavin es acertada. Construí la función para facilitar su uso en lugar de para la velocidad o la eficiencia [1]
Primero, usar la interfaz de la fórmula puede ser un problema cuando tienes muchos predictores. Esto es algo que R Core podría arreglar; el enfoque de la fórmula requiere una matriz muy grande pero dispersa de terms()
para ser retenida y R tiene paquetes para tratar efectivamente ese problema. Por ejemplo, con n = 3, 000 yp = 2, 000, un objeto de modelo de bosque aleatorio de 3 árboles fue 1.5 veces más grande en tamaño y tomó 23 veces más tiempo en ejecutarse cuando se usa la interfaz de fórmula (282s vs 12s).
En segundo lugar, no tiene que mantener los datos de entrenamiento (consulte el argumento trainControl()
en trainControl()
).
Además, dado que R no tiene ninguna infraestructura real de memoria compartida, Gavin está en lo cierto respecto al número de copias de los datos que se conservan en la memoria. Básicamente, se crea una lista para cada lapply()
y se utiliza lapply()
para procesar la lista, y luego devolver solo las estimaciones remuestreadas. Una alternativa sería hacer una copia secuencial de los datos (para el nuevo muestreo actual), hacer las operaciones requeridas y luego repetir para las iteraciones restantes. El problema es la E / S y la imposibilidad de realizar cualquier procesamiento paralelo. [2]
Si tiene un conjunto de datos de gran tamaño, sugiero usar la interfaz sin fórmula (aunque el modelo real, como glm, finalmente use una fórmula). Además, para grandes conjuntos de datos, train()
guarda los índices de remuestreo para ser usados por resamples()
y otras funciones. Probablemente podría eliminar esos también.
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 eso ayude,
Max
[1] No debería hacer todo lo posible para ajustar el menor número de modelos posible cuando podamos. El truco del "sub-modelo" se usa para muchos modelos, como pls, gbm, rpart, earth y muchos otros. Además, cuando un modelo tiene interfaces de fórmula y no de fórmula (p. Ej., lda()
o earth()
, se establece el valor predeterminado de la interfaz de no fórmula).
[2] De vez en cuando me da la loca necesidad de reiniciar la función train()
. El uso de foreach
puede solucionar algunos de estos problemas.