H2O on a Multi-Node Cluster

以前、[sparkling-waterをちょろちょろと動かしてたんですが](http://qiita.com/pyr_revs/items/f866a5223f722ba44a19)、Version上がったり諸々でいろいろとついていくのが面倒な感じでした。一方、h2oの本体であるところのh2o.jarを使えば、比較的楽にクラスタまで立ちそう。ということで、以下を参考に、AWSのEC2上で、h2o.aiなクラスタを立ててみました。

[H2O on a Multi-Node Cluster](http://0xdata.com/docs/master/deploy/cluster/)


# EC2のインスタンスを起動、の前にセキュリティグループを作っておく
インスタンスを起動するときに注意が必要なのはセキュリティグループ。クラスタとして動かすには、同じセキュリティグループに入れて、セキュリティグループ内での通信を可能にしてやる必要があります。

ということで、先にセキュリティグループを作っておきます。

EC2のメニューからセキュリティグループ->作成と進んで、「名前:h2o-cluster・その他デフォルト」のままでセキュリティグループを作成。
![sec1.png](https://qiita-image-store.s3.amazonaws.com/0/69525/f582a3f2-97ce-9dbb-59c5-d06f5d721d87.png)

インバウンドルールの編集で、「タイプ: すべてのトラフィック、送信元: カスタムIP、送信元のValue: h2o-clusterのセキュリティグループID(sg-xxxxxxx)」、となるルールを追加。
![sec2.png](https://qiita-image-store.s3.amazonaws.com/0/69525/0478500b-8091-054d-7a78-89bbcbe8ce76.png)
(本当は54321と54322のTCP/UDPだけ開ければいいんですが、まあすべてのトラフィックでも問題ないかと)

あとは、普通にインスタンスをリクエストして、セキュリティグループの設定のところだけ「既存のセキュリティグループを選択」 -> h2o-clusterを選べばOKです。

# flatfile.txtを準備
インスタンスを起動すると情報がいろいろと見れますけど、
![sec3.png](https://qiita-image-store.s3.amazonaws.com/0/69525/b6d33fde-d246-358e-268c-a4ad5ebf5a94.png)

そん中から、各インスタンスのプライベートIPを拾って、以下のようなテキストファイルを作っておきます。
172.xxx.xxx.xxx:54321
172.yyy.yyy.yyy:54321
172.zzz.zzz.zzz:54321


# h2oクラスタの起動

h2o.jarを起動していきます。全てのインスタンスで同じ作業が必要。

## h2o のダウンロード
まずはh2oをダウンロードして解凍。[ダウンロード情報のページ](http://h2o.ai/download/)で最新versionを確認してください。

```console
sudo yum -y update
wget -q http://h2o-release.s3.amazonaws.com/h2o/rel-shannon/25/h2o-3.0.0.25.zip
unzip h2o-3.0.0.25.zip
cd h2o-3.0.0.25
```

## train/testデータの取得
train/testデータをS3から引っ張ってくる。S3じゃなくてSCPやSFTPで送り込んでもいいんですが、全インスタンスで同じディレクトリに同じ名前のファイルを置いてやる必要があるので、そこだけ注意です。

```console
export AWS_ACCESS_KEY_ID=MY_KEY
export AWS_SECRET_ACCESS_KEY=MY_SEC_KEY
aws s3 cp s3://mybucket/mydatafolder /home/ec2-user/h2o-3.0.0.25 --recursive
```

## flatfile.txtの作成
flatfile.txtを、h2o.jarのあるフォルダに作る。まあviで普通に上のステップで作った情報を流し込めばよいですね。

```console
vi flatfile.txt
172.xxx.xxx.xxx:54321
172.yyy.yyy.yyy:54321
172.zzz.zzz.zzz:54321
```

## h2o.jarの起動
h2o.jarをnohup付きで起動

```console
nohup java -jar h2o.jar -flatfile flatfile.txt > out.log 2>&1 &
```

ここで起動したプロセスを止めるには`ps -A | grep java`で出てきたidを`kill`。


# H2O FlowでBuild Model -> Predict
起動したクラスタを扱うのに、一番お手軽なのはH2O FlowのWeb UIですかね。h2o.jarを起動すると一緒にflowも起動されています。

Flowにアクセスするには、SSHでトンネル掘って、ブラウザで`http://[ip_address]:54321`へGO。SSHのトンネリングについては、以下をご参考に。

[「Amazon EMR クラスターでホストされているウェブサイトの表示」(とSparkのデバッグ)]
(http://qiita.com/pyr_revs/items/5e7212581b536fd436fb)

## Flowの使い方

### Assistance
開くとAssistanceがでているとおもうので、ImportFileをクリックして、
![flow1.png](https://qiita-image-store.s3.amazonaws.com/0/69525/b84794c1-50bb-766d-a0ae-310384780948.png)

### ImportFile
Train/TestファイルをH2Oクラスタへimportして、h2oのframeとして認識させる
![flow2.png](https://qiita-image-store.s3.amazonaws.com/0/69525/f7811e15-8cd2-11f7-22d3-a3363ea7c827.png)


### データのParse
headerの有る無し、各カラムのデータタイプ等々を指定。特にclassificationをする場合でラベルが数値の場合、ここでNumericからEnumに変換する必要があるので注意
![flow3.png](https://qiita-image-store.s3.amazonaws.com/0/69525/7e775a0c-f4f3-ad6d-a45c-bb7dbdef6c0f.png)

### データのステータス
Frame Distribution Summaryを見ると、クラスタ内でのデータの分散されっぷりが見れます。
![flow3_1.png](https://qiita-image-store.s3.amazonaws.com/0/69525/20877a39-ddd8-46e4-5940-39c460a3474f.png)


### Build Model
手法はいろいろ選べます。
![flow5.png](https://qiita-image-store.s3.amazonaws.com/0/69525/3323a042-ef18-3665-b319-9693879d98e1.png)

最低限、TrainするframeとResponse columnを選べばtrainningできます
![flow6.png](https://qiita-image-store.s3.amazonaws.com/0/69525/08b95489-d142-e084-80a4-50c0ce222144.png)
![flow6_1.png](https://qiita-image-store.s3.amazonaws.com/0/69525/ddd39dba-95b7-0291-98c4-59a01241d390.png)

### Predict
TestデータをPredict。build modelが終わったら、predictを押して、
![flow_9.png](https://qiita-image-store.s3.amazonaws.com/0/69525/05da7fcd-117f-6bde-ef23-d608c6eac9a4.png)

testデータのframeを選択
![flow_8.png](https://qiita-image-store.s3.amazonaws.com/0/69525/0264bd2e-49ad-c4dc-d403-001345c1a44e.png)

Predictionの結果。さらにpredictionsのリンクをクリックして詳細に進んで、
![flow_8_1.png](https://qiita-image-store.s3.amazonaws.com/0/69525/e7ac6a57-d709-6def-d57f-e0e8390d0a16.png)

結果をダウンロード
![flow_8_2.png](https://qiita-image-store.s3.amazonaws.com/0/69525/c61a15db-d4a9-4aca-1eaa-74ac4f1de338.png)

### Admin Menu
jobの途中でブラウザを閉じた場合、もう一度つないでAdminメニューからJobsを開くと状況が確認できます。詳細なlogやらCPU usageもここから。
![flow_7.png](https://qiita-image-store.s3.amazonaws.com/0/69525/987b6d32-4417-4c9b-94ee-54e124d07d98.png)

### Split Frame
もちろん、trainデータを分割して、
![flow4.png](https://qiita-image-store.s3.amazonaws.com/0/69525/ccb57fe3-fd8b-1c32-0dc7-334c4bae7d78.png)

trainning_frameとvalidation_frameを設定したうえでBuild modelすることもできます。
![flow4_1.png](https://qiita-image-store.s3.amazonaws.com/0/69525/42ebf57c-43e4-d61c-dfd7-7df1b8941b28.png)



# 終わりに
c4.8xlarge(36core)×3台でクラスタを立てまして、build modelをしてみると、logに「Estimated compute power : 128 GFlops」と出ました。

一方、gpuインスタンスを使った場合、[単精度で1TFlops出る](http://elasticneko.tumblr.com/post/78856570899/aws-gpu-1tflops)とか何とか。

単純に数値比較するのもアレですが、h2oで1TFlopsまでもっていくにはc4.8xlarge 2-30台必要?

コストパフォーマンス的にどうよ感はありますね。。。

まあ、h2oのいいところはいろいろな手法がさくっと使えるところ。DF RandamForestなんかはFlowでやると「Variable Importances」という形でfeatureの重要度なんかも確認できて使い勝手よいですね。Sparkのmllibだとこの辺はまだまだ感があったりなかったり。

Rでローカルマシンでやるとちょっと重いとかそういうデータにはよいんではないでしょうか。Flowを使えばコードも書かずにすみますしねえ。