使用 H2O 在 R 中进行并行处理

Parallel processing in R with H2O

我正在设置一段代码,使用 foreach.

并行处理我数据中 N 组的一些计算

我的计算涉及调用 h2o.gbm

在我当前的顺序设置中,我使用了大约 70% 的 RAM。

如何在并行代码中正确设置 h2o.init()?我担心当我使用多核时我可能 运行 内存不足。

我的 Windows 10 机器有 12 个内核和 128GB 内存。

像这样的伪代码行得通吗?

library(foreach)
library(doParallel)

#setup parallel backend to use 12 processors
cl<-makeCluster(12)
registerDoParallel(cl)

#loop
df4 <-foreach(i = as.numeric(seq(1,999)), .combine=rbind) %dopar% {
  df4 <- data.frame()
  #bunch of computations
  h2o.init(nthreads=1, max_mem_size="10G")
  gbm <- h2o.gbm(train_some_model)
  df4 <- data.frame(someoutput)
   }

fwrite(df4, append=TRUE)

stopCluster(cl)

您的代码当前设置方式不是最佳选择。我明白你想做什么——并行执行一堆 GBM(每个都在一个单核 H2O 集群上),这样你就可以最大限度地利用你机器上 12 个核心的 CPU 使用率。但是,您的代码将尝试 运行 您的 foreach 循环中的所有 GBM 在同一个单核 H2O 集群上并行运行。您一次只能从单个 R 实例连接到一个 H2O 集群,但是 foreach 循环将创建一个新的 R 实例。

与 R 中的大多数机器学习算法不同,H2O 算法都是多核启用的,因此训练过程已经在算法级别并行化,不需要像 foreach 这样的并行 R 包.

您有几个选择(#1 或 #3 可能是最好的):

  1. 在脚本顶部设置 h2o.init(nthreads = -1) 以使用所有 12 个核心。将 foreach() 循环更改为常规循环并按顺序训练每个 GBM(在不同的数据分区上)。尽管不同的 GBM 是按顺序训练的,但每个 GBM 都将在整个 H2O 集群中完全并行化。
  2. 在脚本顶部设置 h2o.init(nthreads = -1),但保留 foreach() 循环。这应该 运行 您所有的 GBM 一次,每个 GBM 在所有内核上并行化。这可能会使 H2O 集群有点不堪重负(这并不是 H2O 的真正用途)并且可能比#1 慢一点,但是如果不知道数据大小和分区数量就很难说想继续训练。如果您已经将 70% 的 RAM 用于单个 GBM,那么这可能不是最佳选择。
  3. 您可以更新您的代码以执行以下操作(最接近您的原始脚本)。这将保留您的 foreach 循环,在您机器上的不同端口创建一个新的 1 核 H2O 集群。见下文。

更新的 R 代码示例使用鸢尾花数据集和 returns 鸢尾花的预测 class 作为 data.frame:

library(foreach)
library(doParallel)
library(h2o)
h2o.shutdown(prompt = FALSE)

#setup parallel backend to use 12 processors
cl <- makeCluster(12)
registerDoParallel(cl)

#loop
df4 <- foreach(i = seq(20), .combine=rbind) %dopar% {
  library(h2o)
  port <- 54321 + 3*i
  print(paste0("http://localhost:", port))
  h2o.init(nthreads = 1, max_mem_size = "1G", port = port)
  df4 <- data.frame()
  data(iris)
  data <- as.h2o(iris)
  ss <- h2o.splitFrame(data)
  gbm <- h2o.gbm(x = 1:4, y = "Species", training_frame = ss[[1]])
  df4 <- as.data.frame(h2o.predict(gbm, ss[[2]]))[,1]
}

为了判断哪个选项最好,我会在几个数据分区(可能是 10-100)上尝试 运行ning 以查看哪种方法似乎最适合扩展。如果您的训练数据很小,#3 可能比#1 快,但总的来说,我认为#1 可能是最 scalable/stable 的解决方案。

根据 Erin LeDell 的回答,我只想补充一点,在许多情况下,一个体面的实用解决方案可能介于 #1 和 #3 之间。要提高 CPU 利用率并仍然节省 RAM,您可以并行使用多个 H2O 实例,但它们每个都可以使用多个内核,而相对于 运行 仅使用一个内核的更多实例而言,它们不会有太大的性能损失。

我 运行 在 36 核服务器上使用相对较小的 40MB 数据集(24 万行,22 列)进行实验。

  • 案例一:使用全部36核(nthreads=36)估计120个GBM模型(默认 超参数)顺序。

  • 案例2:使用foreach到运行本机4个H2O实例,每个 使用 9 个核心依次估计 30 个 GBM 默认模型(总共 = 120 个估计)。

  • 案例3:使用foreach到运行本机12个H2O实例,每个 使用 3 个内核依次估计 10 个 GBM 默认模型(总共 = 120 个估计)。

在该数据集上使用 36 个核心估计单个 GBM 模型效率非常低。 CPU 案例 1 中的利用率跳跃很大,但平均低于 50%。因此,一次使用多个 H2O 实例绝对有好处。

  • 运行时情况 1:264 秒
  • 运行时情况 2:132 秒
  • 运行时情况 3:130 秒

考虑到从 4 个 H2O 实例到 12 个 H2O 实例的小改进,我什至没有 运行 36 个 H2O 实例,每个实例并行使用一个内核。