GCPを使って1時間でKerasのAI予測エンジンを作る


# はじめに
本記事は [ぷりぷりあぷりけーしょんず Advent Calendar 2019](https://qiita.com/advent-calendar/2019/ppap) の13日目の記事です。

この記事ではMLエンジニアとして業務を行っている筆者が、普段利用しているGCPのAIPlatformを使って１時間でAI予測エンジンをする方法を紹介します。


# AI Platformとは

最近は、AIを使ったプロダクトを作ろうという企業が増えてきたように感じます。
しかし、AIの予測エンジンを実際につくろうとすると、GPUのリソースを使えるように設定したり、それをスケールさせるようするなど、とても工数がかかると思います。 
これらの問題を解決してくれるサービスが `AIPlatfrom` です。

AIPlatform とは Google Could Platform で提供されているサービスの１つです。 [AIPlatform公式サイト](https://cloud.google.com/ai-platform/?hl=ja)
GPUを使った学習や予測を手軽に実装できるので、機械学習を使うプロダクトを実装するときにとても有効です。
言語としてはPythonがサポートされており、フレームワークは `scikit-learn` , `TensorFlow` , `XGBoost` などを使うことができます。


# GCPで必要なセットアップ
この記事では以下のGCPのサービスを利用します。

- AIPlatform
- GoogleCloudStorage

それぞれのサービスを操作できるアカウントを用意してください。


# Kerasのモデルをデプロイする

Kerasを使って簡単なモデルを作り、デプロイしてみます。

デプロイするには以下の手順が必要になります。

- Kerasのモデルを定義して学習させる。
- 学習したモデルを GCS(GoogleCouldStorage) へアップロードする。
- AIPlatform でモデルの名前空間を定義する。
- AIPlatform で定義したモデルの名前空間とGCSへアップロードしたモデルを紐づける。


例として `入力値x` に対して `x^2` を `回帰予測` するモデルを作って、デプロイしてみます。
以下がサンプルコードです。

`keras_model_deploy.py` 

``` keras_model_deploy.py
from tensorflow.python.keras.models import Sequential, Model
from tensorflow.python.keras.layers import Dense
import tensorflow as tf
import numpy as np


def create_data():
    data_size = 1000 
    x = [i for i in range(data_size)]
    y = [i**2 for i in range(data_size)]
    return x, y


def create_model() -> Model:
    model = Sequential()
    model.add(Dense(32, activation=tf.nn.relu, input_shape=(1,)))
    model.add(Dense(1))
     
    optimizer = tf.train.RMSPropOptimizer(0.001)
    model.compile(loss='mse', optimizer=optimizer, metrics=['mae'])
    return model


def run_train(x: np.ndarray, y: np.ndarray, model: Model) -> Model:
    history = model.fit(
        x, 
        y, 
        batch_size=1000,
        epochs=100,
        verbose=1,
    )
    return model


def save_model(model: Model) -> None:
    tf.keras.experimental.export_saved_model(
        model,
        "gs://your-buckets/models/sample_model",  # 保存するGCSのパスを指定する
        serving_only=False
    )


if __name__ == "__main__":
    x, y = create_data()
    model = create_model()  
    model = run_train(x, y, model)
    print(model.predict([2]))
    save_model(model)
```

`tf.keras.experimental.export_saved_model` を使うと、モデルをGCSへ保存することができます。
ただし、これはパスの指定が `gs://` から始まる場合だけで、普通のパスを指定すると local へ保存されます。

このコードを実行します。  

`output.txt` 

```
Epoch 1/100
1000/1000 [==============================] - 0s 62us/sample - loss: 199351844864.0000 - mean_absolute_error: 332684.8750
Epoch 2/100
1000/1000 [==============================] - 0s 1us/sample - loss: 199338442752.0000 - mean_absolute_error: 332671.3750
Epoch 3/100
1000/1000 [==============================] - 0s 1us/sample - loss: 199328612352.0000 - mean_absolute_error: 332661.5938
Epoch 4/100
1000/1000 [==============================] - 0s 1us/sample - loss: 199320403968.0000 - mean_absolute_error: 332653.3438
Epoch 5/100
1000/1000 [==============================] - 0s 1us/sample - loss: 199313096704.0000 - mean_absolute_error: 332646.0312
Epoch 6/100
1000/1000 [==============================] - 0s 1us/sample - loss: 199306379264.0000 - mean_absolute_error: 332639.2812
Epoch 7/100
1000/1000 [==============================] - 0s 1us/sample - loss: 199300087808.0000 - mean_absolute_error: 332633.0000
Epoch 8/100
1000/1000 [==============================] - 0s 1us/sample - loss: 199294124032.0000 - mean_absolute_error: 332627.0000
Epoch 9/100
1000/1000 [==============================] - 0s 1us/sample - loss: 199288389632.0000 - mean_absolute_error: 332621.2500
・
・
・
Epoch 100/100
1000/1000 [==============================] - 0s 1us/sample - loss: 198860079104.0000 - mean_absolute_error: 332191.8438
[[3.183104]]
```

モデルの精度はお粗末ですが、きちんと予測結果を返すことが確認きました。

ここからは、GCPのコンソール上で操作してデプロイしていきます。( `gcloud` コマンドを利用するとコマンド上から同じ作業ができますが、今回は紹介しません。 )

まずは、　AIPlatform 上にモデルの名前空間を作りましょう。

AIPlatform のモデルのタブへ行き、 `モデルの作成` をクリックします。

<img width="1220" alt="スクリーンショット 2019-12-15 21.07.01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297968/5b7dcdad-f0cf-7478-cdfb-8b342b11b971.png">

名前とリージョンを設定します。

<img width="588" alt="スクリーンショット 2019-12-15 21.07.20.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297968/42779cab-393f-4637-7232-df4c1420b6ba.png">

ログの設定はここで行います。この設定は変更することができないので、注意してください。

次に、モデルのバージョンを振ります。
ここで、GCSへアップロードしたモデルとの紐付けを行います。

モデルを選択して、 `新しいバージョン` をクリックします。

<img width="899" alt="スクリーンショット 2019-12-15 21.13.49.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297968/705a6d28-1f1b-56f8-ce56-dc804e4ffb22.png">

バージョンの名前と、モデルの動作環境を指定したあと、 GCSにアップロードした SavedModelのパスを指定します。
また、使用するリソース(マシンタイプ)の設定もこちらで行います。
用途に応じて必要なリソースを選択してください。

<img width="563" alt="スクリーンショット 2019-12-15 21.15.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297968/d2f6de9d-afbf-bbd7-8393-1a6d22f49a00.png">

全ての設定を指定した後、バージョンを作成するとモデルが AIPlatform 上に設置されます。  
AIPlatform ではモデルを設置すると、そのモデルを使用して予測を行うAPIを提供してくれています。

なので、ここまでの作業で予測のAPIを叩く準備ができました。


** 注意 ** 
AIPlatformでは、モデルを設置した時点で常に GPU のリソースを使い続けるようになります。  
これによって、 `予測のリクエストを投げなくても課金が発生することになりますので、お気をつけください！！` 
使わないモデルは削除することをお勧めします！


# 予測の結果を取得する

AIPlatform上にデプロイしたモデルに対して予測のリクエストを投げて結果を取得したいと思います。

pythonを利用してリクエストを投げる場合には `googleapiclient` を利用してするのが簡単です。

リクエストを投げる時に認証が必要になりますが、 `GOOGLE_APPLICATION_CREDENTIALS` という環境変数にサービスアカウントなどのクレデンシャルファイルのパスを指定すると解決します。 


``` predict.py
from googleapiclient import discovery

project = "your-project-id"
model = "model_sample"

def predict(instances):
    service = discovery.build('ml', 'v1', cache_discovery=False)
    url = f"projects/{project}/models/{model}"

    response = service.projects().predict(
        name=url,
        body={'instances': [instances]}
    ).execute()

    return response


if __name__ == "__main__":
    features = [2]
    prediction = predict(features) 
    print(prediction)
```

`predict` の引数 `name` や、 `body` の詳しい仕様については [こちら](https://cloud.google.com/ml-engine/docs/v1/predict-request?hl=ja) の公式リファレンスをご確認ください。

実行すると、以下のように予測結果を取得することができます。  

`output`  

```
{'predictions': [{'dense_1': [3.183104]}]}
```

これで予測のエンジンを構築することがきました！
(精度はお粗末ですが、、、)


# 終わりに

記事では学習をローカル環境で行いましたが、AIPlatform上でGPUリソースを利用した学習を行うこともできます。
AIを使ったプロダクトを作る際には強力な武器になると思います。

今回は簡単な Keras モデルを使った予測エンジンを１時間で作成してみました。
あなたのためになれば幸いです。