【问题标题】:How can I speed up the training of my random forest?如何加快随机森林的训练速度?
【发布时间】:2016-09-08 11:28:16
【问题描述】:

我正在尝试训练几个随机森林(用于回归),让它们进行竞争,看看哪些特征选择和哪些参数可以提供最佳模型。

但是培训似乎花费了很多时间,我想知道我是否做错了什么。

我用于训练的数据集(下面称为 train)有 217k 行和 58 列(其中只有 21 个用作随机森林中的预测变量。它们都是 numericinteger,除了布尔值,它属于 character 类。y 输出是 numeric)。

我运行以下代码四次,将值41005002000 赋予nb_trees

library("randomForest")
nb_trees <- #this changes with each test, see above
ptm <- proc.time()
fit <- randomForest(y ~ x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 
    + x10 + x11 + x12 + x13 + x14 + x15 + x16 + x17 + x18 + x19 
    + x20 + x21, 
    data = train, 
    ntree = nb_trees, 
    do.trace=TRUE)
proc.time() - ptm

这是他们每个人训练的时间:

nb_trees | time
4          4mn
100        1h 41mn
500        8h 40mn
2000       34h 26mn

由于我公司的服务器有 12 个内核和 125Go 的 RAM,我想我可以尝试按照 this answer 并行化训练(但是,我使用了 doParallel 包,因为它似乎与 doSNOW 一起永远运行,我不知道为什么。我找不到在哪里看到 doParallel 也可以工作,抱歉)。

library("randomForest")
library("foreach")
library("doParallel")
nb_trees <- #this changes with each test, see table below
nb_cores <- #this changes with each test, see table below
cl <- makeCluster(nb_cores)
registerDoParallel(cl)
ptm <- proc.time()
fit <- foreach(ntree = rep(nb_trees, nb_cores), .combine = combine, .packages = "randomForest") 
    %dopar% {
        randomForest(y ~ x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 
        + x10 + x11 + x12 + x13 + x14 + x15 + x16 + x17 + x18 + x19 
        + x20 + x21,
        data = train, 
        ntree = ntree,
        do.trace=TRUE)}
proc.time() - ptm
stopCluster(cl)

当我运行它时,它比非并行代码花费的时间更短:

nb_trees | nb_cores | total number of trees              | time
1          4          4                                    2mn13s
10         10         100                                  52mn
9          12         108 (closest to 100 with 12 cores)   59mn
42         12         504 (closest to 500 with 12 cores)   I won't be running this one
167        12         2004 (closest to 2000 with 12 cores) I'll run it next week-end

但是,我认为这仍然需要很多时间,不是吗?我知道将树木组合成最终的森林需要时间,所以我没想到它在 12 核时会快 12 倍,但它只快了 ~2 倍......

  • 这正常吗?
  • 如果不是,我可以用我的数据和/或代码做些什么来从根本上减少运行时间吗?
  • 如果不是,我应该告诉负责服务器的人,它应该更快吗?

感谢您的回答。

注意事项:

  • 我是唯一使用此服务器的人
  • 在接下来的测试中,我将删除随机森林中未使用的列
  • 我很晚才意识到我可以通过调用randomForest(predictors,decision) 而不是randomForest(decision~.,data=input) 来提高运行时间,我将从现在开始这样做,但我认为我上面的问题仍然存在。

【问题讨论】:

    标签: r parallel-processing random-forest doparallel parallel-foreach


    【解决方案1】:

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

    我建议看看这三个:

    1. ranger(使用 C++ 编译器)
    2. randomForestSRC(使用 C++ 编译器)
    3. h2o(Java 编译器 - 需要 Java 8 或更高版本)

    此外,这里还有一些额外的阅读资料,可以让您更多地了解选择哪个包: https://arxiv.org/pdf/1508.04409.pdf

    第 8 页显示了基准测试,显示了 ranger 针对 randomForest 对不断增长的数据大小的性能改进 - 由于运行时的线性增长,ranger 的性能要快得多,而不是 randomForest 的非线性增长,以增加树/样本/拆分/特征大小.

    祝你好运!

    【讨论】:

      【解决方案2】:

      randomForest() 函数可以使用“公式接口”或“矩阵接口”来接受数据。众所周知,矩阵接口可以提供更好的性能数据。

      公式界面:

      rf.formula = randomForest(Species ~ ., data = iris)
      

      矩阵界面:

      rf.matrix = randomForest(y = iris[, 5], x = iris[, 1:4])
      

      【讨论】:

      • 谢谢。你知道这是否特定于 randomForest() 或者如果我在 caret 中使用 method = 'rf' 也可以工作?那么method='ranger' 呢?
      • IIRC, caret 使用“矩阵接口”执行方法调用。
      【解决方案3】:

      虽然我喜欢蛮力技术,例如并行化或长时间运行代码,但我更喜欢改进算法以避免不得不使用蛮力技术。

      虽然使用 2000 棵树训练您的随机森林开始变得异常昂贵,但使用较少数量的树进行训练所花费的时间更合理。对于初学者,您可以使用 481632...256512 树进行训练,并仔细观察指标,让您了解模型的稳健性。这些指标包括最佳常数模型(您的森林在数据集上的表现与预测所有输入的中位数的模型的表现如何),以及袋外误差。此外,您可以观察最重要的预测变量及其重要性,以及当您添加更多树时是否开始看到收敛。

      理想情况下,您不必使用数千棵树来构建模型。一旦你的模型开始收敛,添加更多树不一定会使模型恶化,但同时它不会添加任何新信息。通过避免使用过多的树,您可以将原本需要一周时间的计算减少到不到一天。如果除此之外,您还利用了十几个 CPU 内核,那么您可能会在数小时内看到一些东西。

      要查看每次随机森林运行后的变量重要性,您可以尝试以下方法:

      fit <- randomForest(...)
      round(importance(fit), 2)
      

      据我了解,前 5-10 个预测变量对模型的影响最大。如果您注意到通过增加树,这些*预测变量并没有真正改变相对于彼此的位置,并且重要性指标似乎保持不变,那么您可能需要考虑不使用这么多树。

      【讨论】:

      • 感谢您的建议。我知道我可以使用do.trace = TRUE 看到 OOB 错误如何演变(据我所知,仅使用非并行代码),因此我可以看到它如何随着树的数量而演变。是否有类似的参数也可以查看*预测变量如何演变? (这样我就可以只运行一次训练,有 512 棵树)
      • @fmalaussena 我更新了我的答案,请看一下。