kerasを使ったGoogle Cloud MLのメモ

# きっかけ
仕事で機械学習をやり始めたけれど
支給されてるMBPじゃ大規模な学習に時間がかかりすぎるので..

個人的なメモ程度にまとめてあります。
間違えているかもしれないので編集リクエストやコメントお待ちしてます！

# Google Cloud MLの使い方

## 実施環境
```
Python : 2.7.10(venv)  
CloudSDK: 208.0.2  
tensorflow : 1.9.0  
keras : 2.2.0   
```
ひとまずどれも最新版を入れてます。

pythonについてはpython3だとgcloudコマンドが
うまく動かなかったので2.7を使ってます。

---
## 前準備
### 1. Google Cloud SDKをインストールする
[Google Cloud SDK](https://cloud.google.com/sdk/docs/?hl=ja)をダウンロードして初期設定まで完了させる

```bash
$ ./google-cloud-sdk/install.sh  
$ ./google-cloud-sdk/bin/gcloud init  
```
* Regionはus-central1を選択
* asia-northeastの東京リージョンではML Engineが動かなかったので注意 

### 2. リージョンを指定 
```bash
$ REGION=us-central1  
```
* 機械学習を実施するリージョンを指定します
* またバケットのリージョンにもなります

### 3. Cloud StorageにBUCKETを準備する  
```bash
$ PROJECT_ID=$(gcloud config list project --format "value(core.project)")  
$ BUCKET_NAME=${PROJECT_ID}-bucket  
$ gsutil mb -l $REGION gs://$BUCKET_NAME  
```
* PROJECT_ID-bucketという名前でCloud Storageにバケットを作ります

### 4. 学習データをCloudにコピーする  
```bash
$ gsutil cp -r path/to/data gs://$BUCKET_NAME/data  
```
* 学習で使うトレーニングデータとテストデータをバケットにアップロードします
* 完了すれば[クラウドコンソール](https://console.cloud.google.com/storage/browser/)から確認できるようになります

### 5. 学習データの入力先を指定  
```bash
$ TRAIN_DATA=gs://$BUCKET_NAME/data/train.txt  
$ TEST_DATA=gs://$BUCKET_NAME/data/test.txt  
```
* データの拡張子、パスは適宜読み替えてください

### 6. ジョブの名前をつける  
```bash
$ JOB_NAME=training_job_`date'+%Y_%m_%d_%H_%M'`  
```
* ML Engineでは１学習＝１ジョブとして実施されます
* 複数回ジョブを回す場合はジョブの名前を常にユニークなものにしてください
* ここでは日付を末尾に付与してます

### 7. 結果の出力先を指定  
```bash
$ OUTPUT_PATH=gs://$BUCKET_NAME/$JOB_NAME  
```
* Tensorboardやモデルの出力用のパスを指定します
* また、ML Engineで構築されるモデルもこの配下に出力されます

---
## kerasを使ってML Engineで学習する際の最小構成
```
CloudML/
├─ config.yaml
├─ setup.py
└─ trainer/
    ├─ __init__.py
    └─ task.py
```
```python:setup.py
from setuptools import find_packages
from setuptools import setup

REQUIRED_PACKAGES = ['Keras', 'h5py']

setup(
    name='trainer',
    version='0.1',
    install_requires=REQUIRED_PACKAGES,
    packages=find_packages(),
    include_package_data=True,
    description='Keras trainer application'
)
```
* config.yamlと__init\_\_.pyは空で大丈夫です
* task.pyには通常のkerasで書いたコードで大丈夫です
* setup.pyにはクラウド上のマシンに読み込ませたいライブラリを書いておくと勝手にインストールしてくれます
* 今回はkerasを使いたいのでkerasとh5pyをいれておきます。
* 標準で入っているライブラリは以下で確認できます
* [ランタイム バージョン リスト](https://cloud.google.com/ml-engine/docs/tensorflow/runtime-version-list)

**ただしCloud Storageからデータを読み込む場合は
標準ライブラリのopen()では読み込めないので
tensorflowを文頭でimportしてtensorflow.gfile.Open()を使うようにしてください**

```python:例
import tensorflow as tf

def open_file(path):
    data_list = []
    with tf.gfile.Open(path, 'r') as f:
        for data in f.read().split('\n'):
            if(len(data) == 0):
                continue
            data_list.append(data)
        f.close()
    return data_list
```

---
## ローカルでの学習
```bash
gcloud ml-engine local train \
    --module-name trainer.task \
    --package-path trainer/ \
    -- \
    --train_data $TRAIN_DATA \
    --test_data $TEST_DATA \
    --output_model $OUTPUT_PATH \
    --epochs 1
```
* module-name　: 実行するタスクの指定 
* package-path　: タスクフォルダの指定
* クラウドで学習する前にローカルで実行できるのを確認することをおすすめします
* ユーザ指定の引数を使う場合は -- を跨いだ後に指定します（上で言う所の４行目以降）
* ここで問題なく実施できて入ればおそらく大丈夫

--- 
## CPUのみでクラウド学習
```bash
gcloud ml-engine jobs submit training $JOB_NAME \  
    --runtime-version 1.6 \
    --job-dir $OUTPUT_PATH \  
    --module-name trainer.task \  
    --package-path trainer/ \  
    --region $REGION \  
    -- \  
    --train_data $TRAIN_DATA \
    --test_data $TEST_DATA \
    --output_model $OUTPUT_PATH \ 
    --epochs 100  
```
* クラウド上で学習するには追加でいくつか引数が必要になります
* runtime-version : ジョブを実行する環境の指定 
* job-dir　: ジョブの結果を配置するフォルダの指定 
* region　: ジョブの実行するリージョンの指定 

```bash:実行後例
Job [training_job_2018_07_18_10_38] submitted successfully.
Your job is still active. You may view the status of your job with the command

  $ gcloud ml-engine jobs describe training_job_2018_07_18_10_38

or continue streaming the logs with the command

  $ gcloud ml-engine jobs stream-logs training_job_2018_07_18_10_38
jobId: training_job_2018_07_18_10_38
state: QUEUED
```
* このように出ればML Engineで学習が始まったはずです。
* ブラウザで確認すればトレーニング状況がみれます。

---
##　クラウド学習の停止
```
gcloud ml-engine jobs cancel $JOB_NAME
```
*　間違えちゃった時用
*　停止するのに大体５分程度かかります

---
## GPUを使った学習方法

### 一番安いGPU
```
NVIDIA Tesla K80 GPU 1 個
1時間あたり$1.2118 (137円程度 - 2018/07/18 現在)
```
* とりあえずGPUを1枚使って学習させたい 
* 他にも複数枚指定することも可能です

 
### 1. config.yamlに以下を記入する
```
trainingInput:
  scaleTier: BASIC_GPU
```

### 2. tensorflowでGPUを指定する場合
```
import tensorflow as tf

config = tf.ConfigProto(
    gpu_options=tf.GPUOptions(
        visible_device_list="0,1", # specify GPU number
        allow_growth=True
    )
)
sess = tf.Session(config=config)
```

### 3. kerasでGPUを指定する場合
```
import tensorflow as tf
from keras.backend.tensorflow_backend as KTF

config = tf.ConfigProto(
    gpu_options=tf.GPUOptions(
        visible_device_list="0", # specify GPU number
        allow_growth=True
    )
)
KTF.set_session(tf.Session(config=config))
```

* どちらもセッションに指定するのでこの後セッションの設定を変えるような記述がある場合はその変更と合わせて指定してください

### 4. GPUでクラウド学習
```bash
gcloud ml-engine jobs submit training $JOB_NAME \  
    --runtime-version 1.6 \
    --job-dir $OUTPUT_PATH \  
    --module-name trainer.task \  
    --package-path trainer/ \  
    --region $REGION \  
    --config config.yaml
    -- \  
    --train_data $TRAIN_DATA \
    --test_data $TEST_DATA \
    --output_model $OUTPUT_PATH \ 
    --epochs 100  
```

* configにconfig.yamlを指定することでGPUを使った学習が始まります。
* config.yamlにマシンの設定を記入できるようなので凝った設定で行うことも可能な模様
* 詳しい人教えてください..

#　おわり
とりあえずクラウドで学習させることができた記念
このあと作ったモデルをAPI化したりもできるようですがまだ学習が終わらないのでそれは後ほどまとめたいです


# 参考
* [KerasモデルをCloud ML Engineで学習してOnline Predictionしてみた](https://qiita.com/hayatoy/items/cbed2ebb5ae1e7bbc202)
* [Cloud ML EngineでKerasモデルを学習した際、I/O関連で躓いた。その時の対処法。](https://qiita.com/tomonari-t/items/b23e86c899e516c3f68b)
* [クラウドではじめる機械学習：Google Cloud Machine Learningを使ってみよう](https://techblog.gmo-ap.jp/2018/01/29/%E3%82%AF%E3%83%A9%E3%82%A6%E3%83%89%E3%81%A7%E3%81%AF%E3%81%98%E3%82%81%E3%82%8B%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92%EF%BC%9Agoogle-cloud-machine-learning%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6-2/)
* [Tensorboard + Keras + ML Engine from stackoverflow](https://stackoverflow.com/questions/45091099/tensorboard-keras-ml-engine)