2016-09-16 141 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