在foreach循环中运行h2o算法?

时间:2016-09-16 16:37:38

标签: r foreach h2o gbm doparallel

我天真地以为在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)

1 个答案:

答案 0 :(得分:3)

注意:这不太可能很好地使用R的并行foreach,但我会首先回答你的问题,然后解释原因。 (顺便说一句,当我在这个答案中使用“cluster”时,我指的是一个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集群时,你刚刚制作的模型被删除)。我已经强调了你可能希望在你的模型上做些什么。我已经为你的机器上的所有线程(即nthreads=-1中的h2o.init())提供了H2O,而不仅仅是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()中的a bug有效意味着它忽略了港口。 (解决方法:在命令行上预启动所有6个H2O群集,或在环境变量中设置端口。)