为什么插入符号列车占用了这么多记忆?

问题描述 投票:20回答:3

当我只使用glm进行训练时,一切正常,我甚至没有接近耗尽记忆力。但是当我运行train(..., method='glm')时,我的内存耗尽。

这是因为train为交叉验证的每次迭代存储了大量数据(或者无论trControl程序是什么)?我正在看trainControl,我找不到如何防止这种...任何提示?我只关心性能总结和预测的响应。

(我知道它与存储参数调整网格搜索的每次迭代中的数据无关,因为我认为没有glm的网格。)

r memory glm r-caret
3个回答
36
投票

这个问题有两个问题。 i)train不只是通过glm()拟合模型,它将引导该模型,因此即使使用默认值,train()也会执行25个引导样本,再加上问题ii)是问题的(或)来源,和ii)train()简单地调用glm()函数的默认值。那些默认值是存储模型框架(model = TRUE的参数?glm),其中包括模型框架样式中的数据副本。 train()返回的对象已经在$trainingData中存储了数据的副本,而"glm"中的$finalModel对象也有实际数据的副本。

在这一点上,简单地使用glm()运行train()将生成25份完全扩展的model.frame和原始数据,这些数据在重采样过程中都需要保存在内存中 - 无论是同时还是连续保存,都不会立即从快速查看代码,因为重新采样发生在lapply()调用中。还将有25份原始数据。

重新采样完成后,返回的对象将包含2个原始数据副本和model.frame的完整副本。如果您的训练数据相对于可用的RAM很大或者包含许多要在model.frame中扩展的因素,那么您可以轻松地使用大量内存来携带数据副本。

如果您将model = FALSE添加到您的火车通话中,这可能会有所不同。这是使用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)

然后

> 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

因此返回的对象存在大小差异,并且训练期间的内存使用将更低。降低多少将取决于train()的内部是否在重新采样过程中将model.frame的所有副本保留在内存中。

train()返回的对象也明显大于glm()返回的对象 - 正如@DWin在下面的评论中提到的那样。

为了更进一步,可以更仔细地研究代码,或者发送电子邮件的维护者Max Kuhn来询问减少内存占用的选项。


30
投票

加文的答案是现场。我建立了易于使用的功能,而不是速度或效率[1]

首先,当您拥有大量预测变量时,使用公式界面可能会成为一个问题。这是R Core可以解决的问题;公式方法需要保留一个非常大但稀疏的terms()矩阵,并且R具有有效处理该问题的包。例如,当n = 3,000和p = 2,000时,3树随机森林模型对象的大小是1.5倍,使用公式界面时需要花费23倍的时间(282s vs 12s)。

其次,您不必保留训练数据(请参阅returnData中的trainControl()参数)。

此外,由于R没有任何真正的共享内存基础结构,Gavin对于保留在内存中的数据副本的数量是正确的。基本上,为每个重新采样创建一个列表,lapply()用于处理列表,然后仅返回重新采样的估计值。另一种方法是顺序制作数据的一个副本(对于当前重新采样),执行所需的操作,然后重复其余的迭代。问题是I / O和无法进行任何并行处理。 [2]

如果你有一个大数据集,我建议使用非公式接口(即使实际模型,如glm,最终使用公式)。此外,对于大型数据集,train()保存重新采样索引以供resamples()和其他函数使用。你也可以删除它们。

杨 - 通过str(data)了解更多关于数据的信息会很好,所以我们可以理解维度和其他方面(例如,多层次的因素等)。

我希望有帮助,

马克斯

[1]我不应该尽我们所能尽可能少地使用尽可能少的模型。 “子模型”技巧用于许多模型,例如pls,gbm,rpart,earth和许多其他模型。此外,当模型具有公式和非公式接口(例如,lda()earth()时,我们默认为非公式接口。

[2]每隔一段时间我就会有疯狂的冲动重启train()功能。使用foreach可能会解决其中一些问题。


1
投票

我认为上面的答案有点过时了。插入符号和caretEnsemble包现在包含trainControl'trim'中的附加参数。 Trim最初设置为FALSE,但将其更改为TRUE将显着减小模型大小。对于尽可能小的模型尺寸,您应该将其与returnData = FALSE结合使用。如果您正在使用模型集合,则还应在greedy / stack集合trainControl中指定这两个参数。

对于我的情况,一个1.6gb的模型缩小到~500mb,同时在整体控制中使用两个参数,并且还使用贪婪的整体控制中的参数进一步缩小到〜300mb。

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))
© www.soinside.com 2019 - 2024. All rights reserved.