【问题标题】:Why is caret train taking up so much memory?为什么插入符号火车占用这么多内存?
【发布时间】:2025-12-16 02:50:01
【问题描述】:

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

这是因为train 为交叉验证的每次迭代(或任何 trControl 过程)存储了大量数据吗?我正在查看trainControl,但我找不到如何防止这种情况……有什么提示吗?我只关心绩效总结,也许还有预测的响应。

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

【问题讨论】:

  • 想制作一个可重复的小例子供其他人尝试吗?
  • 这已经有 7 年历史了,大概这个问题是以前修复的版本。这是在哪个版本中发生的,您能确认哪个版本解决了这个问题吗?

标签: r memory glm r-caret


【解决方案1】:

问题有两个方面。 i) train 不只是通过glm() 拟合模型,它会引导该模型,所以即使使用默认值,train() 也会进行 25 次引导样本,再加上问题 ii) 是您问题的(或 a)来源,ii) train() 只需调用glm() 具有 默认值的函数。这些默认设置是存储模型框架(参数model = TRUE?glm),其中包括模型框架样式的数据副本。 train()返回的对象已经存储了$trainingData中的数据副本,$finalModel中的"glm"对象也有实际数据的副本。

此时,简单地使用train() 运行glm() 将生成完全展开的model.frame 的25 个副本原始数据,这些都需要在运行期间保存在内存中重采样过程 - 由于重采样发生在 lapply() 调用中,因此快速查看代码并不能立即清楚地看到这些是同时进行还是连续保存。还将有 25 个原始数据副本。

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

如果您将model = FALSE 添加到您的火车呼叫中,那可能会有所不同。这是一个使用?glm 中的clotting 数据的小示例:

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 在下面的 cmets 中所提到的。

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

【讨论】:

  • 很好的答案(对你来说很典型,加文)。只会添加 glm 对象的大小:&gt; m3= glm(lot1 ~ log(u), data=clotting, family = Gamma) &gt; object.size(m3) 47272 bytes
  • @Dwin 谢谢,好点。我会将该输出添加到答案中,当然要注明归属。
  • 谢谢,我让 Max 在这里添加答案。
【解决方案2】:

Gavin 的回答很到位。我构建函数是为了易于使用,而不是为了速度或效率 [1]

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

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

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

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

Yang - 最好通过str(data) 了解更多有关数据的信息,以便我们了解维度和其他方面(例如,多层次的因素等)。

希望对你有帮助

最大

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

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

【讨论】:

  • 欢迎来到 SO @Max,感谢您提供的信息丰富的答案。我很高兴您写了train() 以方便使用;我最近一直在使用它进行一些随机梯度提升,并且自己编写了一些调整代码,切换到 carettrain()! 是一个启示
  • 我提供了我自己的模型矩阵和响应向量(必要时我可以使用findCorrelation),所以我不使用任何模型的公式接口。子模型技巧是什么?
  • 你提到的那些处理公式内存使用问题的包是什么? “并且 R 具有有效处理该问题的软件包”
  • 正确。尤其是公式接口部分也可能导致内存问题
【解决方案3】:

我认为上面的答案有点过时了。 caret 和 caretEnsemble 包现在在 trainControl 'trim' 中包含一个附加参数。 Trim 最初设置为 FALSE,但将其更改为 TRUE 将显着减小模型大小。对于可能的最小模型尺寸,您应该将它与 returnData=FALSE 结合使用。如果您使用的是模型集成,您还应该在贪婪/堆栈集成 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))

【讨论】: