在R中获得randomForest回归更快

时间:2016-01-10 14:40:49

标签: r regression random-forest

我必须在R中使用randomforest进行回归。我的问题是我的数据框很大:我有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$ssrd_1 + dati$Clipped_no + dati$Clip_pm10 + dati$sp_1, data=trainSet, ntree=250)

3 个答案:

答案 0 :(得分:14)

我不认为在单个PC(2-4核心)上并行化就是答案。有很多较低的悬挂水果可供选择。

1)RF模型的复杂性随着训练样本数量的增加而增加。平均树深度类似于log(480,000 / 5)/ log(2)= 16.5个中间节点。在绝大多数例子中,每棵树2000-10000个样本都很好。如果你在单打比赛中争夺胜利,那么额外的一小部分表现真的很重要,因为胜利者需要全部。在实践中,您可能不需要它。

2)不要在您的R代码中克隆您的数据集并尝试仅保留您的数据集的一个副本(通过引用传递当然是好的)。对于这个数据集来说这不是一个大问题,因为即使对于R,数据集也不是那么大(~38Mb)。

3)不要将randomForest算法的公式接口用于大型数据集。它将生成数据集的额外副本。但记忆并不是那么大的问题。

4)使用更快的RF算法: extraTrees ranger Rborist 可用于R. extraTrees 不完全是RF算法,但非常接近。

5)避免超过10个类别的分类特征。 RF可以处理多达32个,但由于必须评估任何2 ^ 32可能的分割,因此变得非常慢。 extraTrees Rborist 仅通过测试一些随机选择的分割来处理更多类别(效果很好)。另一个解决方案如 python-sklearn 中的每个类别都分配了一个唯一的整数,并且该功能被处理为数字。您可以使用as.numeric转换分类功能,然后再运行randomForest以执行相同的操作。

6)更大的数据。在随机块中拆分数据集并在每个块上训练几棵(~10)树。结合森林或分开拯救森林。这将略微增加树的相关性。有一些很好的集群实现来训练像这样。但对于低于1-100Gb的数据集,则不需要,具体取决于树的复杂性等。

#below我使用解决方案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$ssrd_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

答案 1 :(得分:3)

由于您使用的是插入符号,因此您可以使用方法=&#34; parRF&#34;。这是并行randomforest的实现。

例如:

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 + ssrd_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 + ssrd_1 + Clipped_no + Clip_pm10 + sp_1, 
                   data = trainSet, ntree=ntree)

# don't forget to stop the cluster
stopCluster(cl)

记住我没有设置任何种子。您可能也想考虑这一点。这里是一个link到一个也可以并行运行的随机森林包。但我没有测试过这个。

答案 2 :(得分:1)

另外两个答案很好。另一种选择是实际使用专为高维/高容量数据集构建的更新包。他们使用低级语言(C++ 和/或 Java)运行代码,并且在某些情况下使用并行化。

我建议看看这三个:

游侠(使用 C++ 编译器) randomForestSRC(使用 C++ 编译器) h2o(Java 编译器 - 需要 Java 8 或更高版本) 此外,这里还有一些额外的阅读内容,可以让您更多地了解选择哪个套餐:https://arxiv.org/pdf/1508.04409.pdf

第 8 页显示的基准测试显示了 Ranger 与 randomForest 在不断增长的数据大小下的性能改进 - 由于运行时的线性增长,而不是 randomForest 的非线性增长,ranger 的速度更快,因为树/样本/分割/特征大小不断增长。

>

祝你好运!