Python
MachineLearning
GoogleCloudPlatform
airflow
MLflow

Cloud ML Engine に自動で 予測モデルをデプロイするパイプラインを作ってみた

この記事はBrainpad Advent Calendar 2018の7日目の記事です。

自己紹介と機械学習基盤を簡単に

初めまして、えいりんぐーです。
データ分析や機械学習まわりの仕事をしています。

このテの仕事をしていて感じるのは、基盤づくり・環境づくりって大切だということです。とくに、アドホックに分析や予測モデルを作って試すだけなら各々好きなようにやっていいのですが、仕事としてチームで、データ分析とその共有や機械学習サービスの開発・運用をやるには、ある程度定型化された環境というものが必要だと思います。

最近ではDevOpsの文脈から派生して、MLOpsという言葉も聞くようになりまして、もろもろの事業会社さんを見ると、やはりそれぞれ取り組みはあるようです。

私もそれっぽいことなんかやりたいな〜、個人でちょこっと遊べるものないかな〜ということで、今回はGoogle Cloudのサービスの一つである Cloud Machine Learning Engine (以下、ML Engine) に予測モデルを自動でデプロイするパイプラインを作ったお話しをします。

題材はHouse Price Predictionで

主目的は機械学習モデル1の学習とデプロイプロセスをそれっぽく作ることなので、めちゃくちゃリッチにするつもりはありません。とりあえず、これまたお遊びでさわっていたKaggleのHouse Price Predictionのコードがありますので、それを流用します。

筋書きとしては、
1. 既存の予測モデルが稼働している。
2. 新しいデータが取得されたので、データを検証の上、モデルを再学習する
3. 主要な性能指標を評価して、モデルをGoogle Cloud Storageに保存する
4. 保存されたモデルをML Engineにデプロイする
といった流れになっています。

参考資料とリポジトリなど

Airflowは公式のドキュメント、Cloud ML Engineについてはこちらを参考にして進めていきました。また、今回書いたコードはGitHubに公開しています。以降では、このリポジトリをクローンしたディレクトリが、カレントディレクトリだと仮定して話を進めます。

とりあえず開発環境を

Google Cloudのコンソールをポチポチするのも手間なので、認証用のサービスアカウントこそコンソールで作成しますが、それ以降はgcloudコマンドを使います。ただ、Google Cloud SDKはPython2系に依存している一方で、今回使いたいコードはPython3系なので、ローカルではちょっと設定がややこしくなります。

そこで今回は、gcloudコマンドが入ったコンテナーを使います。このイメージはdebian:stretchベースでpython3が入っていませんので、他に必要なモジュールも合わせてインストールします。

Dockerfile
FROM google/cloud-sdk:226.0.0

RUN apt-get -qy update \
    && apt-get -qy install python3 python3-pip

# ~~~ skip ~~~

RUN pip install \
        enum==0.4.7 \
        scipy==1.1.0 \
        scikit-learn==0.19.2 \
        tensorflow==1.12.0 \
    && pip3 install -r requirements.txt

CMD ["python3"]

上記のイメージをビルドして、コンテナー内で作業します。5000ポートはMlflow用、8080ポートはAirflow用です。

docker build -t houseprice:1.0.0 .
docker run -it --rm -p 5000:5000 -p 8080:8080 houseprice:1.0.0 /bin/bash

コンテナー内ではひとまず、GCPの認証と初期設定を行ってください。

一連の作業フローはAirflowに

ジョブ実行ツールには、古き良きcron、DigdagLuigiなどがあります。今回はたまたま使っている人が身近にいたので、Airbnbが開発したAirflowを使います23。Airflowに関しては、誰かが書いてくれるはずです...

Airflowを初期化、起動すればサーバーが立ち上がります

airflow initdb
airflow webserver -p 8080 2>&1 > /airflow/logs/webserver.log &
airflow scheduler 2>&1 > /airflow/logs/scheduler/scheduler.log &

Airflowは記述したジョブをDAGの形で表示・実行できます。
airflow.png

こちらでは、
1. inspection: データの型チェック
2. alignment: 列の並べ替え
3. split: 学習とバリデーションデータの分割
4. train: 学習とモデルや指標の保存
5. prediction: ローカルで予測が実行できることを確認
6. put: Cloud Storageに保存
7. create: ML Engineにバージョンの作成
8. REST: REST API形式で予測の確認
というジョブを順に実行しています。

実際には、このあたりの書き方・作り方はデータサイエンティストとエンジニアとできちんと設計・開発・検証するところですね。今回は再学習からデプロイまでを一連のジョブに落とし込むだけでよかったので、お手軽に済ませています。

モデルと指標管理はMLflowに

MLflowは機械学習システムをメンテする際の一つのプラットフォームです。MLflowはモデル、パラメータ、性能指標の管理や、エクスペリメントと呼ばれる、まとまった処理の実行ができます。サーバーを起動した初期状態4では次のようになっており、train.pyによって性能指標が生成されているのがわかります5
mlflow.png

実行の詳細を追うと、モデル情報の詳細が記録されているのもわかります。
mlflow.2.png

Google Cloudでは

Google CloudのStorageとML Engineでは下準備として、既に古いモデルがアップロード・デプロイされていると想定します。
gcs.1.png

mlengine.1.png

動かしてみると

Airflowをキックすると、新しいデータによる学習と、作成された新しいモデルの保存・デプロイが進みます。
airflow.2.png

モデルのデプロイは、Airflowのジョブとして次のコマンドを実行するようにしています。

dags.py
t60 = BashOperator(
    task_id='gcloud_ml-engine_create_version',
    depends_on_past=True,
    bash_command='gcloud ml-engine versions \
        create {{ params.version_name }} \
        --model {{ params.model_name }} \
        --origin {{ params.model_dir }} \
        --runtime-version=1.10 \
        --framework "SCIKIT_LEARN" \
        --python-version="3.5"',
    params={ 'version_name': VERSION_NAME,
             'model_name': MODEL_NAME,
             'model_dir': DEPLOYMENTURI },
    dag=dag
)

その結果、ちゃんとモデルはStorageに保存されており、ML Engineにデプロイされていることが分かります。
gcs.2.png

mleigine.2.png

またMLflowのほうにも新しいモデルとその性能指標が記録されているのが分かります。
mlflow.3.png

新しくデプロイされたモデルに対して、curlでポストリクエストすると、予測が帰ってきます。

curl -X POST -H "Content-Type: application/json" \
>    -d @data/prepared/test.json \
>    -H "Authorization: Bearer `gcloud auth print-access-token`" \
>       "https://ml.googleapis.com/v1/projects/${PROJECT_ID}/models/${MODEL_NAME}/versions/v1_0_5:predict"
{"predictions": [165099.27191280792, 184508.264037045, 175908.7052910972, 176725.19047667703, 140080.75051509973, 171560.74454709014, 165292.94862796998, 171263.10326839934, 152066.53775823937, 154048.07134814674]}

無事に予測モデルがデプロイされたのが分かります。

まとめ

今回は、予測モデルを再学習するパイプラインをご紹介しました。
実際には、データベースを設計したり、Airflowのサーバーを切り離したり、学習の過程でCVを回したりと色々と検証しなければならないところは多いですが、個人でお試しで作るにはなかなか楽しめる題材でした。

ML EngineはモデルをデプロイするだけでREST APIを提供できるので、簡単に機械学習システムのバックエンドを作ることができます。そのため、ある程度モデルの再学習の仕組みや管理が定まっているとすると、お手軽手はありましたが、今回作成したようなパイプラインを設計するだけで、機械学習システムの継続的なデプロイに結びつけることができます。フレームワークとしてはscikit-learnだけでなく、TensorFlowやXGBoostも利用できる6ので、みなさんもぜひ試してみていただければと思います。お読みいただきありがとうございました。


  1. といっても中身はお手軽線形モデルですが。 

  2. 楽しいけど、今回扱ったようなお手軽サービスには少々リッチなフレームワークでした (^^; 

  3. Cloud Composerは使わないのか、ですか?ローカルでごちゃごちゃ試したかったのです... 

  4. こういう初期状態であるという下準備をしています。 

  5. 今回はパラメーターチューニングはしていません。 

  6. なお、Cloud ML Engineもランタイムによってサポートするフレームワーク、Pythonとモジュールのバージョンが異なるので注意が必要です。