运行 foreach 循环中的 h2o 算法?
run h2o algorithms inside a foreach loop?
我天真地认为在 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)
注意:这不太可能很好地使用 R 的并行 foreach,但我会先回答你的问题,然后再解释原因。 (顺便说一句,当我在这个答案中使用 "cluster" 时,我指的是 H2O 集群(即使只是在您的本地机器上),而不是 R "cluster"。)
我已经重写了你的代码,假设目的是要有一个 单个 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 集群,该集群 运行 在本地主机上但在该集群唯一的端口上。 (
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 集群,要么在环境变量中设置端口。)
我天真地认为在 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)
注意:这不太可能很好地使用 R 的并行 foreach,但我会先回答你的问题,然后再解释原因。 (顺便说一句,当我在这个答案中使用 "cluster" 时,我指的是 H2O 集群(即使只是在您的本地机器上),而不是 R "cluster"。)
我已经重写了你的代码,假设目的是要有一个 单个 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 集群,该集群 运行 在本地主机上但在该集群唯一的端口上。 (
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 集群,要么在环境变量中设置端口。)