在 foreach 循环中运行 h2o 算法?
Posted
技术标签:
【中文标题】在 foreach 循环中运行 h2o 算法?【英文标题】:run h2o algorithms inside a foreach loop? 【发布时间】:2017-01-24 23:18:25 【问题描述】:我天真地认为在 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】:注意:这不太可能很好地使用 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,并将Xtr
和 Xval
加载到其中
在您的 R 客户端中启动 6 个线程
在每个线程中制作 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 集群,该集群在 localhost 上运行,但在该集群唯一的端口上。 (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 集群,或者在环境变量中设置端口。)
【讨论】:
感谢您的解释。所以你的代码和我的代码之间的唯一区别是h2o.init(ip="localhost", port = 54321 + (i*2),...)
。通过分配不同的端口,h2o 为每个线程创建一个单独的集群。
@horaceT as.h2o()
数据上传也必须进入 for 循环。 (我还将library(h2o)
放在了foreach 循环中,尽管我不确定是否需要这样做。)(如前所述,无论如何,在端口错误修复之前代码将无法工作。)
我没有测试过,但我只是想了解这个概念。调用h2o.init(...)
创建一个集群,每个集群连接到一个且只有一个线程。我不能在同一个集群中运行多个线程。它应该是这样工作的吗?
我发现始终将 H2O 集群视为位于 R 客户端的单独机器(或一组机器)上会有所帮助。 H2O 集群将使用您分配给它的尽可能多的线程。您的 R 客户端只是在远程机器上向该集群发送指令。客户端机器上不会发生计算,因此无论您的 R 客户端是使用单线程还是多线程,都不会让任何事情发生得更快。您可以将多个客户端连接到 H2O 集群,每个客户端都上传、下载、启动模型等。以上是关于在 foreach 循环中运行 h2o 算法?的主要内容,如果未能解决你的问题,请参考以下文章
UIImageView Array ForEach 迭代循环问题
R语言使用system.time函数统计多个函数运行的累加(累计)时间计算h2o包生成的多个算法模型进行特征重要度分析累计耗费的时间
R语言使用system.time函数统计多个函数运行的累加(累计)时间计算h2o包生成的多个算法模型进行特征重要度分析累计耗费的时间