在 R 中更快地获得 randomForest 回归
Posted
技术标签:
【中文标题】在 R 中更快地获得 randomForest 回归【英文标题】:Get randomForest regression faster in R 【发布时间】:2016-04-14 21:12:23 【问题描述】:我必须在 R 中使用随机森林进行回归。我的问题是我的数据框很大:我有 12 个变量和超过 40 万个条目。当我尝试 - 代码写在底部 - 获得 randomForest 回归时,系统需要花费很多小时来处理数据:经过 5、6 小时的计算,我不得不停止操作而没有任何输出。有人可以建议我如何更快地获得它? 谢谢
library(caret)
library(randomForest)
dataset <- read.csv("/home/anonimo/Modelli/total_merge.csv", header=TRUE)
dati <- data.frame(dataset)
attach(dati)
trainSet <- dati[2:107570,]
testSet <- dati[107570:480343,]
output.forest <- randomForest(dati$Clip_pm25 ~ dati$e_1 + dati$Clipped_so + dati$Clip_no2 + dati$t2m_1 + dati$tp_1 + dati$Clipped_nh + dati$Clipped_co + dati$Clipped_o3 + dati$s-s-rd_1 + dati$Clipped_no + dati$Clip_pm10 + dati$sp_1, data=trainSet, ntree=250)
【问题讨论】:
试试ranger
或Rborist
【参考方案1】:
我不认为在单台 PC(2-4 核)上进行并行化是答案。有很多下垂的果实可供采摘。
1) RF 模型的复杂性随着训练样本数量的增加而增加。平均树深度类似于 log(480,000/5)/log(2) = 16.5 个中间节点。在绝大多数示例中,每棵树 2000-10000 个样本就可以了。如果你在 kaggle 上竞争获胜,那么一点额外的表现真的很重要,因为赢家通吃。实际上,您可能不需要它。
2) 不要在 R 代码中克隆您的数据集,并尝试只保留数据集的一份副本(通过引用传递当然可以)。对于这个数据集来说这不是一个大问题,因为即使对于 R,数据集也不是那么大(~38Mb)。
3) 不要将公式接口与 randomForest 算法一起用于大型数据集。它将制作数据集的额外副本。但同样,记忆也不是什么大问题。
4) 使用更快的 RF 算法:extraTrees、ranger 或 Rborist 可用于 R。extraTrees不完全是 RF 算法,但非常接近。
5) 避免使用超过 10 个类别的分类特征。 RF 最多可以处理 32 个,但由于必须评估任何 2^32 个可能的拆分,因此变得非常慢。 extraTrees 和 Rborist 仅通过测试一些随机选择的分割来处理更多类别(效果很好)。 python-sklearn 中的另一种解决方案为每个类别分配了一个唯一的整数,并将该特征作为数字处理。您可以在运行 randomForest 之前使用 as.numeric 转换您的分类特征以执行相同的技巧。
6) 对于更大的数据。将数据集拆分为随机块,并在每个块上训练几棵(~10)棵树。合并森林或分开保存森林。这将略微增加树的相关性。有一些很好的集群实现可以像这样训练。但对于 1-100Gb 以下的数据集则不是必需的,具体取决于树的复杂性等。
#下面我使用解决方案 1-3) 并获得几分钟的运行时间
library(randomForest)
#simulate data
dataset <- data.frame(replicate(12,rnorm(400000)))
dataset$Clip_pm25 = dataset[,1]+dataset[,2]^2+dataset[,4]*dataset[,3]
#dati <- data.frame(dataset) #no need to keep the data set, an extra time in memory
#attach(dati) #if you attach dati you don't need to write data$Clip_pm25, just Clip_pm25
#but avoid formula interface for randomForest for large data sets because it cost extra memory and time
#split data in X and y manually
y = dataset$Clip_pm25
X = dataset[,names(dataset) != "Clip_pm25"]
rm(dataset);gc()
object.size(X) #38Mb, no problemo
#if you were using formula interface
#output.forest <- randomForest(dati$Clip_pm25 ~ dati$e_1 + dati$Clipped_so + dati$Clip_no2 + dati$t2m_1 + dati$tp_1 + dati$Clipped_nh + dati$Clipped_co + dati$Clipped_o3 + dati$s-s-rd_1 + dati$Clipped_no + dati$Clip_pm10 + dati$sp_1, data=trainSet, ntree=250)
#output.forest <- randomForest(dati$Clip_pm25 ~ ., ntree=250) # use dot to indicate all variables
#start small, and scale up slowly
rf = randomForest(X,y,sampsize=1000,ntree=5) #runtime ~15 seconds
print(rf) #~67% explained var
#you probably really don't need to exeed 5000-10000 samples per tree, you could grow 2000 trees to sample most of training set
rf = randomForest(X,y,sampsize=5000,ntree=500) # runtime ~5 minutes
print(rf) #~87% explained var
#regarding parallel
#here you could implement some parallel looping
#.... but is it really worth for a 2-4 x speedup?
#coding parallel on single PC is fun but rarely worth the effort
#If you work at some company or university with a descent computer cluster,
#then you can spawn the process across 20-80-200 nodes and get a ~10-60-150 x speedup
#I can recommend the BatchJobs package
【讨论】:
【参考方案2】:由于您使用的是插入符号,因此您可以使用 method = "parRF"。这是一个并行随机森林的实现。
例如:
library(caret)
library(randomForest)
library(doParallel)
cores <- 3
cl <- makePSOCKcluster(cores)
registerDoParallel(cl)
dataset <- read.csv("/home/anonimo/Modelli/total_merge.csv", header=TRUE)
dati <- data.frame(dataset)
attach(dati)
trainSet <- dati[2:107570,]
testSet <- dati[107570:480343,]
# 3 times cross validation.
my_control <- trainControl(method = "cv", number = 3 )
my_forest <- train(Clip_pm25 ~ e_1 + Clipped_so + Clip_no2 + t2m_1 + tp_1 + Clipped_nh + Clipped_co + Clipped_o3 + s-s-rd_1 + Clipped_no + Clip_pm10 + sp_1, ,
data = trainSet,
method = "parRF",
ntree = 250,
trControl=my_control)
这里也是一个 foreach 实现:
foreach_forest <- foreach(ntree=rep(250, cores),
.combine=combine,
.multicombine=TRUE,
.packages="randomForest") %dopar%
randomForest(Clip_pm25 ~ e_1 + Clipped_so + Clip_no2 + t2m_1 + tp_1 + Clipped_nh + Clipped_co + Clipped_o3 + s-s-rd_1 + Clipped_no + Clip_pm10 + sp_1,
data = trainSet, ntree=ntree)
# don't forget to stop the cluster
stopCluster(cl)
记住我没有种下任何种子。您可能也想考虑这一点。这是一个link 到一个同样并行运行的随机森林包。但我还没有测试过。
【讨论】:
感谢您的回答,但是当我尝试计算 my_forest 时,此代码返回错误:summary.connection(connection) 中的错误:无效连接 R 的并行实现的一个缺点是它必须启动一个新实例并将数据复制到每个实例。如果使用商用硬件,这可能会很快导致内存问题。 @phiver 当必须计算 for 时,我收到 neww 代码错误:反序列化错误(socklist[[n]]):从连接读取错误。 @Lupanoide,尝试重新启动您的 r 会话。另见this post 和this one 感谢 phiver,现在只需 5 分钟即可使用。我希望它会尽快完成,我会更新你【参考方案3】:另外两个答案很好。另一种选择是实际使用专门为高维/大容量数据集构建的更新包。他们使用较低级别的语言(C++ 和/或 Java)运行他们的代码,并且在某些情况下使用并行化。
我建议看看这三个:
ranger(使用 C++ 编译器) randomForestSRC(使用 C++ 编译器) h2o(Java 编译器 - 需要 Java 8 或更高版本) 此外,这里还有一些额外的阅读资料,可以让您更多地了解选择哪个包:https://arxiv.org/pdf/1508.04409.pdf
第 8 页显示了一些基准,显示了 ranger 对 randomForest 的性能改进,以应对不断增长的数据量 - 由于运行时的线性增长,ranger 的速度要快得多,而不是 randomForest 的非线性增长,因为树/样本/拆分/特征大小的增加。
祝你好运!
【讨论】:
以上是关于在 R 中更快地获得 randomForest 回归的主要内容,如果未能解决你的问题,请参考以下文章
R中RandomForest包中的RandomForest函数中的参数'classwt'代表啥?