R内存管理建议(caret、模型矩阵、数据框)

7
我在机器学习环境中使用一个普通的8GB服务器处理相对较小的数据集时,内存不足:
> dim(basetrainf) # 这是一个数据框 [1] 58168 118
我唯一采取的预建模步骤是将数据框转换为模型矩阵,这显著增加了内存消耗。这是因为caretcor等仅适用于(模型)矩阵。即使删除具有许多级别的因子,矩阵(mergem如下)仍然相当大。(sparse.model.matrix/Matrix通常支持不好,因此我无法使用它。)
> lsos() Type Size PrettySize Rows Columns mergem matrix 879205616 838.5 Mb 115562 943 trainf data.frame 80613120 76.9 Mb 106944 119 inttrainf matrix 76642176 73.1 Mb 907 10387 mergef data.frame 58264784 55.6 Mb 115562 75 dfbase data.frame 48031936 45.8 Mb 54555 115 basetrainf data.frame 40369328 38.5 Mb 58168 118 df2 data.frame 34276128 32.7 Mb 54555 103 tf data.frame 33182272 31.6 Mb 54555 98 m.gbm train 20417696 19.5 Mb 16 NA res.glmnet list 14263256 13.6 Mb 4 NA
此外,由于许多R模型不支持示例权重,我必须首先对少数类进行过采样,将数据集的大小加倍(因此trainf、mergef、mergem的行数是basetrainf的两倍)。
此时,R使用1.7GB的内存,使我的总内存使用量达到了7.7GB的4.3GB。
接下来我做的事情是:
> m = train(mergem[mergef$istrain,], mergef[mergef$istrain,response], method='rf')
几秒钟后,Linux的内存不足杀手就会杀死rsession。
我可以对数据进行抽样、欠采样等,但这些都不是理想的方法。除了重写caret和我打算使用的各种模型包之外,我还应该怎么做(有何不同)?
值得一提的是,即使在没有剪枝任何因子的情况下,我也从未在其他ML软件(Weka、Orange等)中遇到过这个问题,可能是因为它们支持示例加权和“数据框”支持,在所有模型中都适用。
完整的脚本如下:
library(caret)
library(Matrix)
library(doMC)
registerDoMC(2)
response = 'class'
repr = 'dummy' do.impute = F
xmode = function(xs) names(which.max(table(xs)))
read.orng = function(path) { # 读取头部信息 hdr = strsplit(readLines(path, n=1), '\t') pairs = sapply(hdr, function(field) strsplit(field, '#')) names = sapply(pairs, function(pair) pair[2]) classes = sapply(pairs, function(pair) if (grepl('C', pair[1])) 'numeric' else 'factor')
# 读取数据 dfbase = read.table(path, header=T, sep='\t', quote='', col.names=names, na.strings='?', colClasses=classes, comment.char='')
# 切换响应变量,移除元数据列 df = dfbase[sapply(pairs, function(pair) !grepl('m', pair[1]) && pair[2] != 'class' || pair[2] == response)]
df }
train.and.test = function(x, y, trains, method) { m = train(x[trains,], y[trains,], method=method) ps = extractPrediction(list(m), testX=x[!trains,], testY=y[!trains,]) perf = postResample(ps$pred, ps$obs) list(m=m, ps=ps, perf=perf) }
# 函数:稀疏相关系数 sparse.cor = function(x){ memory.limit(size=10000) n = dim(x)[2] D = dist(t(x), method="cor") hc = hclust(D) groups = cutree(hc, k=round(n/10)) cor.groups = matrix(0, nrow=n, ncol=n) for (i in 1:max(groups)) { members = which(groups == i) if (length(members) > 1) { cors = cor(x[,members]) cor.groups[members, members] = cors } } cor.groups }
# 函数:移除低方差的预测变量 print('remove low variance predictors') mergef = read.orng('data/orng.tab') mergef = mergef[, -which(sapply(mergef, is.numeric) && apply(mergef, 2, sd) < .01)]
# 函数:移除高相关性的预测变量 print('remove high-correlation predictors') merge.cor = (if (repr == 'sparse') sparse.cor else cor)(mergef) mergef = mergef[, -findCorrelation(merge.cor, cutoff=.75)]
# 函数:尝试不同的方法 do.method = function(method) { train.and.test(mergem, mergef[response], mergef$istrain, method) } res.gbm = do.method('gbm') res.glmnet = do.method('glmnet') res.rf = do.method('parRF')

1
你最终是更换了软件,还是在R中找到了解决方案?我很想知道你更有前途的方法是什么,因为我也遇到了类似的问题。我计划使用越来越高规格的EC2机器,因为它们很方便,而且我非常熟悉R(直到我需要实现其他解决方案)。 - lockedoff
1
@lockedoff 我最终只是进行了更多的子采样(我提到的“非理想”解决方法之一 - 这也应该包括“购买更多RAM”)! - Yang
1
我现在能够使用caret相对较快地评估一个350,000 x 30数据框的3x3x3参数网格。当并行运行时(每个核心使用的内存过多),这会使我的8GB四核MacBook Pro崩溃,但昨天我发现它在亚马逊的高内存双倍超大实例上运行非常快(http://aws.amazon.com/ec2/instance-types/ @ 约0.42美元/小时作为竞价实例)。 - lockedoff
3个回答

7
有这么多的数据,重新采样误差估计和随机森林OOB误差估计应该非常接近。尝试使用trainControl(method = "OOB")train()将不会在重新采样的数据集上拟合额外的模型。
此外,像瘟疫一样避免使用公式接口。
你也可以尝试使用bagging。由于每个分裂点没有随机选择预测器,因此您可以通过50-100次重新采样(而不是随机森林所需的更多次数)获得良好的结果。
其他人可能会有不同的看法,但我认为并不总是最佳方法对所有数据进行建模。除非预测空间很大,否则许多数据点将与其他数据点非常相似,并且对模型拟合没有太大贡献(除了额外的计算复杂性和结果对象的占用空间)。caret有一个名为maxDissim的函数,可以帮助稀疏数据(尽管它也不是非常高效)。

6
检查底层的randomForest代码是否没有存储树的森林。也许可以减少tuneLength的值,这样就可以尝试更少的mtry值。
另外,我建议手动拟合一个随机森林模型,看看是否能在我的计算机上拟合这样的模型。如果直接无法拟合,则无法使用caret一次拟合多个模型。
此时,您需要确定是什么原因导致内存膨胀,以及如何控制模型拟合,使其不会失控。因此,要弄清楚caret如何调用randomForest()以及使用了哪些选项。您可能可以关闭其中一些选项(例如存储我之前提到的森林,但也包括变量重要性度量)。确定了mtry的最佳值后,您可以尝试使用所有额外的内容来拟合模型,以帮助解释拟合结果。

我担心你会这么说。randomForest本身确实消耗了大量的内存。ntree=500会出现“错误:无法分配大小为384.7 Mb的向量。” ntree=200可以工作,但几乎用尽了内存。看起来我必须特别处理RF(和其他模型,如GBM),或者放弃R。啊,为什么R中的所有东西都占用了这么多内存?我真的希望我做错了什么或者漏掉了什么。如果我没有听到其他消息,我会将您的答案标记为已接受。 - Yang
1
@Yang,你有使用 keep.forest = FALSE 这个参数吗?如果没有,请加上。另外,你是用公式接口还是正常接口进行拟合的?请确保你正在使用矩阵,而不是数据框。为什么 mergmem 的行数是 basetrainf 的两倍?我知道列数更多的原因,但不知道为什么行数会是两倍。请向我们展示你的操作过程,这样我们就不必猜测了。请编辑你的问题并附上你尝试过的示例代码(实际调用)。 - Gavin Simpson
此时,您可能需要寻求 randomForest() 的作者专家的帮助——人们确实使用 R 处理大型数据集的此类任务。其次,可以尝试对使用的内存进行分析,但这需要使用支持内存分析的编译了 R。在调用代码之前清理您的工作区——如果您不需要 R 工作区中的其他对象,请摆脱它们。此外,尝试减少其他正在使用的 RAM。占据您 2.6GB RAM 的是什么? - Gavin Simpson
我还没有看到有人在randomForest中提到nodesize参数,你可以设置它来控制生成的树的大小。通常情况下,使用这么多数据不需要生成“完整”的树。此外,如果您想对新数据进行预测而不将森林保留在内存中,则可以使用xtest参数(如果您已经有了新数据)。 - joran
此外,在处理大型数据集时,通常不需要对整个数据集进行重新采样以获得randomForest的良好性能。如果将sampsize设置为合理的值,则可能会看到内存使用量显着减少... - joran
显示剩余5条评论

-1
您可以尝试使用ff包,它实现了“在磁盘上进行大数据的内存高效存储和快速访问函数”的功能。

11
请勿随意推荐ff或bigmemory。原帖的问题是有关caret包的帮助,ff和bigmemory都无法与之配合使用。因此,这个答案可能会误导读者,离题了。 - Dirk Eddelbuettel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接