2016-09-16 93 views
3

我天真地认为直接在一个foreach循环内并行地调用h2o.gbm是很简单的。但有一个奇怪的错误。在foreach循环中运行h2o算法?

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) 

回答

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进去
  • 开始6个线程在你的[R客户
  • 在每个线程使3个GBM模型(一个后对方)

我放弃了h2o.shutdown()命令,猜测你不打算这样做(当你关闭H2O集群时,你刚删除的模型就会被删除)。我已经强调了你可能想用你的模型做些什么。我也已经给H2O你的机器的所有线程上(即在h2o.init()nthreads=-1),而不仅仅是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螺纹
  • 在每个线程,启动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簇,或将端口设置为环境变量。)

+0

感谢您的解释。所以你的代码和我的唯一区别是'h2o.init(ip =“localhost”,port = 54321 +(i * 2),...)'。通过分配不同的端口,h2o为每个线程创建一个单独的群集。 – horaceT

+0

@horaceT另外'as.h2o()'数据上传必须在for循环中进行。 (我也把'library(h2o)'放到了foreach循环中,不过我不确定这是否是必需的。)(正如注意到代码不会工作,直到端口错误被修复,无论如何。) –

+0

我有没有测试它,但我只是想了解这个概念。调用'h2o.init(...)'创建一个集群,每个集群连接到一个且只有一个线程。我不能在同一集群内运行多个线程。这是它应该如何工作? – horaceT