3

我天真地认为在 foreach 循环内并行多次调用 h2o.gbm 是直接的。但是遇到了一个奇怪的错误。

Error in { : 
         task 3 failed - "java.lang.AssertionError: Can't unlock: Not locked!"

下面的代码

library(foreach)
library(doParallel)
library(doSNOW)

Xtr.hf = as.h2o(Xtr)
Xval.hf = as.h2o(Xval)

cl = makeCluster(6, type="SOCK")
registerDoSNOW(cl)
junk <- foreach(i=1:6, 
            .packages=c("h2o"), 
            .errorhandling = "stop",
            .verbose=TRUE) %dopar% 
{
   h2o.init(ip="localhost", nthreads=2, max_mem_size = "5G") 
   for ( j in 1:3 ) { 
     bm2 <- h2o.gbm(
     training_frame = Xtr.hf,  
     validation_frame = Xval.hf, 
     x=2:ncol(Xtr.hf),
     y=1,          
     distribution="gaussian",
     ntrees = 100,
     max_depth = 3,
     learn_rate = 0.1,
     nfolds = 1)
  }
  h2o.shutdown(prompt=FALSE)    
  return(iname)
}
stopCluster(cl)
4

1 回答 1

3

注意:这不太可能很好地使用 R 的并行 foreach,但我会先回答你的问题,然后解释原因。(顺便说一句,当我在这个答案中使用“集群”时,我指的是 H2O 集群(即使只是在您的本地机器上),而不是 R“集群”。)

我已经重写了您的代码,假设意图是拥有一个H2O集群,所有模型都将在其中制作:

library(foreach)
library(doParallel)
library(doSNOW)
library(h2o)

h2o.init(ip="localhost", nthreads=-1, max_mem_size = "5G") 

Xtr.hf = as.h2o(Xtr)
Xval.hf = as.h2o(Xval)

cl = makeCluster(6, type="SOCK")
registerDoSNOW(cl)
junk <- foreach(i=1:6, 
            .packages=c("h2o"), 
            .errorhandling = "stop",
            .verbose=TRUE) %dopar% 
{
   for ( j in 1:3 ) { 
     bm2 <- h2o.gbm(
     training_frame = Xtr.hf,  
     validation_frame = Xval.hf, 
     x=2:ncol(Xtr.hf),
     y=1,          
     distribution="gaussian",
     ntrees = 100,
     max_depth = 3,
     learn_rate = 0.1,
     nfolds = 1)

   #TODO: do something with bm2 here?

  }
  return(iname)  #???
}
stopCluster(cl)

即大纲形式:

  • 启动 H2O,然后加载XtrXval放入其中
  • 在您的 R 客户端中启动 6 个线程
  • 在每个线程中制作 3 个 GBM 模型(一个接一个)

我放弃了该h2o.shutdown()命令,猜测您不是故意的(当您关闭 H2O 集群时,您刚刚创建的模型会被删除)。我已经强调了你可能想对你的模型做些什么。而且我已经给了 H2O 你机器上的所有线程(即nthreads=-1in h2o.init()),而不仅仅是 2。

可以并行制作 H2O 模型,但这通常不是一个好主意,因为它们最终会争夺资源。最好一次做一个,并依靠 H2O 自己的并行代码将计算分布在集群上。(当集群是单台机器时,这往往非常有效。)

事实上,你已经在 R 中创建了一个并行循环,这让我觉得你错过了 H2O 的工作方式:它是一个用 Java 编写的服务器,而 R 只是一个向它发送 API 调用的轻客户端. GBM 计算不在 R 中完成;它们都是用 Java 代码完成的。

另一种解释代码的方法是运行多个 H2O 实例,即多个 H2O 集群。如果您有一组机器,并且您知道 H2O 算法在多节点集群中的扩展性不是很好,这可能是一个好主意。在一台机器上做这件事几乎肯定是个坏主意。但是,为了争论,这就是你的做法(未经测试):

library(foreach)
library(doParallel)
library(doSNOW)

cl = makeCluster(6, type="SOCK")
registerDoSNOW(cl)
junk <- foreach(i=1:6, 
            .packages=c("h2o"), 
            .errorhandling = "stop",
            .verbose=TRUE) %dopar% 
{
   library(h2o)
   h2o.init(ip="localhost", port = 54321 + (i*2), nthreads=2, max_mem_size = "5G") 

    Xtr.hf = as.h2o(Xtr)
    Xval.hf = as.h2o(Xval)

   for ( j in 1:3 ) { 
     bm2 <- h2o.gbm(
     training_frame = Xtr.hf,  
     validation_frame = Xval.hf, 
     x=2:ncol(Xtr.hf),
     y=1,          
     distribution="gaussian",
     ntrees = 100,
     max_depth = 3,
     learn_rate = 0.1,
     nfolds = 1)

    #TODO: save bm2 here
  }
  h2o.shutdown(prompt=FALSE)    
  return(iname)  #???
}
stopCluster(cl)

现在大纲是:

  • 创建 6 个 R 线程
  • 在每个线程中,启动一个在 localhost 上运行但在该集群唯一的端口上运行的 H2O 集群。(这i*2是因为每个 H2O 集群实际上使用了两个端口。)
  • 将您的数据上传到 H2O 集群(即这将重复 6 次,每个集群一次)。
  • 制作 3 个 GBM 模型,一个接一个。
  • 用这些模型做点什么
  • 杀死当前线程的集群。

如果你的机器上有 12+ 个线程,30+ GB 内存,而且数据比较小,这将与使用一个 H2O 集群并串行制作 12 GBM 模型的效率大致相同。如果没有,我相信情况会更糟。(但是,如果您在 6 台远程机器上预先启动了 6 个 H2O 集群,这可能是一种有用的方法 - 我必须承认我一直想知道如何做到这一点,并且使用并行库直到我从未想到过我看到了你的问题!)

注意:从当前版本(3.10.0.6)开始,我知道上面的代码不起作用,因为有一个错误实际上h2o.init()意味着它忽略了端口。(解决方法:在命令行上预先启动所有 6 个 H2O 集群,或者在环境变量中设置端口。)

于 2016-09-17T17:18:00.513 回答