下げメーカーで機械学習モデルを推論サービスへ移行する ~AWS SageMaker with Docker image~

Yuya Sugano
28 min readNov 17, 2019

--

仮想通貨分析作業のためにデータサイエンス用のpythonプラットフォームであるAnacondaをベースとしたDocker環境を使用しています。Anacondaの公式イメージからAnaconda3のDockerイメージを編集して独自イメージを作成しました。機械学習のモデル開発はDockerコンテナのローカル環境で問題ありませんが、実運用にあたって、日々の新しいデータに対する機械学習モデルの再訓練と配置、モデルファイルのアップデートが重荷です。少し古いコンテンツですが、scikit-learnのコンテナイメージを使用して、SageMakerによる機械学習モデルの開発および推論サービスの構築を試しました。コンテナイメージは自前のSageMaker用のDockerイメージを作成し、仮想通貨取引で使用している勾配ブースティングのアルゴリズムを採用します。

開発した機械学習のモデル(Jupyter Notebookなどで開発したもの)を実運用するにあたって複数のペインポイントが発生しています。※『機械学習プロジェクトの典型的な課題』ではより多くの項目が列挙されています

  • 新しいデータ(日次)で機械学習モデルを再構築する手間がある
  • 構築したモデルファイルの管理が面倒くさい
  • 機械学習モデルはローカル環境でしか呼び出せない

包括的な視点で見ると機械学習モデルの開発から実運用へ移行するにあたり以下を考慮しなければいけませんでした。

  • 機械学習モデルの運用方法
  • 推論サービスの提供方法

本稿では機械学習における環境構築から開発・運用や推論を幅広くカバーするAWSのマネージドサービスであるAmazon SageMakerを使用して機械学習のモデルをクラウド環境で再現してみます。機械学習モデルの構築をSageMakerで行い、学習済みモデルを推論サービスのエンドポイントとしてデプロイします。クラウド上のモデル学習や推論サービスの提供は費用がかかることを除けばデメリットはそれほどないでしょう。

SageMakerでは学習用データをS3で管理できます。学習済みのモデルやコードもS3に保存され、学習には提供されているトレーニングアルゴリズムが使用できる他、Apache SparkやカスタムアルゴリズムをDockerイメージにまとめて、ECR(Elastic Conteiner Registry)から呼び出して使用することも可能です。[1]

SageMaker Traning Option

AWS SagemakerはAWSの機械学習サービススタックの中でMLサービスとして分類され、ラベリングやJupyter Notebookの利用から、トレーニングや推論をフルマネージドで提供しています。[2]

最終的には機械学習モデルの運用や推論サービスの提供をすべて自動化して、機械学習モデルの運用のためのCI/CDの環境を構築することを目標とします。

  1. 日次データをS3へ置く(Digdag/バッチ処理など)
  2. S3へのデータ保存をトリガーとして機械学習のモデルを構築
  3. テストデータでモデルの性能を測定およびデプロイ判定
  4. ステージング環境へデプロイ
  5. プロダクション環境へデプロイ

参考となるAWSのサービスによるCI/CDパイプライン構築のレポート記事です。[3]

SageMakerの基本的な使い方は書いていません。ここではSageMakerのscikit-learn Dockerイメージを勾配ブースティング用に加工して、ECR(Elastic Container Registry)へプッシュします。その後にECRへプッシュしたDockerイメージを使用したscikit-learnによるトレーニングを行い、推論エンドポイントとしてデプロイする方法を試します。最初からクラウドで開発することを検討する場合には、SageMakerで環境を固定してしまっても良いかと思います。ディープラーニング系のライブラリもSageMakerではサポートしています(Amazonの回し者ではございません)。

Machine Learning Algorithm Selection

提供されているアルゴリズムを使用するにはSageMaker Python SDKを利用します。[4]

ここで試してみたことです。

  1. scikit-learnをSageMaker Python SDKで試してみる
  2. SageMakerで使用するDockerコンテナを構築
  3. ECR(Elastic Container Registry)へイメージをプッシュ
  4. SageMakerでデータの読み込みと訓練ジョブの実行
  5. 推論サービスのデプロイとテスト

scikit-learnをSageMakerで試してみる

SageMaker Python SDKを使用してSageMakerで用意されているコンテナを使用した訓練や推論サービスのデプロイが行えます。 SageMaker Python SDKをAmazon SageMakerのノートブックで読み込み、sagemaker.sklearn.estimator.sklearnのライブラリを使用してエントリーポイントとなるscikit-lernスクリプトやハイパーパラメータを渡すだけでモデルの訓練が可能で、推論サービスのホスティングもコード1行でできるようになっています。[5]

このサンプルでは以下の簡単2ステップによって、事前に構成されているDockerコンテナを使用したモデルの訓練と推論サービスのデプロイが行えます。対象のデータは scikit-learn の例で頻出するirisデータセット、 load_iris() で決定木を使用したアヤメの3種の分類問題を解くことができます。

SageMakerで事前用意されているものでも、自前で作成したものであってもDockerコンテナへエントリーポイントを渡すことで、環境変数やハイパーパラメータを読み込んで、訓練・推論やモデルの保存を定義するようになっています。エントリーポイントは必須です。データをコンテナへ読み込み、訓練するコードは if __name__ == '__main__': 内へ記述します。推論エンドポイントから使用するためのモデルの読み込みを def model_fn(model_dir): として関数定義します。この例で訓練のみが必要な場合は if __name__ == '__main__': の箇所のみで問題ないです。

SageMaker Python SDKをAmazon SageMakerのノートブックでsagemaker.sklearn.estimator.sklearnを読み込み、 estimator インスタンスを作成します。訓練するには scikit-learn と同様に fit を呼ぶだけです。複数のパラメータ渡す必要があります。

SageMaker scikit estimator arguments
  • entry_point: 訓練や推測に使用するpythonスクリプトへのパス
  • role: ロールのARN
  • train_instance_type(オプション): SageMakerのインスタンスタイプ、GPUはscikit-learnでサポートされないので、SageMaker scikit-learnでも同様にサポートされない
  • sagemaker_session(オプション): SageMakerの訓練に使用するセッション
  • hyperparameters(オプション): 訓練するモデルへ渡すハイパーパラメータ
from sagemaker.sklearn.estimator import SKLearnscript_path = 'scikit_learn_iris.py'sklearn = SKLearn(
entry_point=script_path,
train_instance_type="ml.c4.xlarge",
role=role,
sagemaker_session=sagemaker_session,
hyperparameters={'max_leaf_nodes': 30})
sklearn.fit({'train': train_input})

以下、AWS SageMakerのJupyter Notebookで展開されるノートブック上でモデルの訓練や推論サービスのデプロイ、それを使用した予測や評価までできるようになっています。非常に便利なシステムです。

Amazon SageMaker ノートブック

SageMakerのBatch Transferを使用することでS3のデータ対する非同期推論を行うこともできます。Transformerを作成して、データを渡すだけでこちらも1行で実行ができます。

An efficient batch request

エントリーポイントAmazon SageMakerノートブックは以下の公式リポジトリに更新されています。[6]

SageMakerで使用するDockerコンテナを構築

サンプルではSageMakerで既に用意されているコンテナを使用して訓練や推論、バッチ変換ジョブを行いましたが自作のコンテナを使用する場合、または独自のアルゴリズムによる処理を行いたい場合はDockerコンテナを作成する必要があります。以下のコードを参考に勾配ブースティングのSageMaker Dockerコンテナを構築していきます。

このDockerコンテナでは train スクリプトが呼ばれることで訓練を、 serve スクリプトが呼ばれることで推論サービス用のサーバを起動できます。サンプルではどちらも実行できるように対応していますが、作成するコンテナで訓練の機能だけが必要な場合には、 train の実装だけでも問題ありません。サンプルでは decision_trees というフォルダに各スクリプトを記載してDockerコンテナに追加しています。 trainserveなど各クリプトやモデルファイルを格納するパスとなる /opt/program をコンテナ内で環境変数として設定しておく必要があります。今回のDockerファイルでは、scikit-learnのバージョンをサンプルの 0.20.2 から実際のサービスで使用している 0.21.2 へ変更しました。またベースのイメージを python:3.7 としているため、 trainserve内のshebangを #!/usr/local/bin/python へ変えています。

SageMaker Docker container

requirements.txtにpip3でインストールする各種ライブラリを記載しました。nginx/flask/gevent/gunicornはhttpで提供される推論サービスに必要なライブラリです。geventはgunicornで非同期処理を行えるワーカークラスです。 train による訓練機能のみが必要な場合には、これらのライブラリは不必要です。

The libraries required for a serve script
# requirements.txtnumpy==1.16.2
scipy==1.2.1
scikit-learn==0.21.2
pandas==0.25.3
nginx
flask
gevent
gunicorn

Dockerfileができたら任意の名前を付けてビルドしておきます。今回作ったコンテナイメージはDockerHubへアップロードしてあります。[7]

$ docker build -t sklearn-container .
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
sklearn-container latest b747a4d28d64 About an hour ago 1.26GB

次に gradient_boost というフォルダを作成し、サンプルリポジトリと同様のファイルを実装しました。 predictor.py でFlaskによるアプリケーションを実装していますが、gunicornでこの推論アプリケーションを起動します。 serve は推論のエンドポイントとなるnginxやアプリケーションであるgunicornのプロセスを立ち上げ、管理するためのコードです。

decision_trees file index for example

httpリクエストは8080で受け付ける必要があり、ヘルスチェック用の /ping へのGETリクエストと、推論用の /invocations へのPOSTリクエストを処理できるように実装しておく必要があります。[8]

コンテナの/opt/programに trainserve が配置されますが権限を変更して、実行可能ファイルとしました。

$ docker run -v $(pwd)/test_dir:/opt/ml -it sklearn-container
root@eca2311b75af:/opt/program# ls -al
total 28
drwxr-xr-x 1 root root 4096 Nov 16 05:22 .
drwxr-xr-x 1 root root 4096 Nov 16 05:23 ..
-rw-rw-r-- 1 root root 717 Nov 16 03:13 nginx.conf
-rw-rw-r-- 1 root root 2851 Nov 16 03:11 predictor.py
-rwxrwxr-x 1 root root 2428 Nov 16 03:26 serve
-rwxrwxr-x 1 root root 3689 Nov 16 02:35 train

-rw-rw-r-- 1 root root 203 Nov 16 02:52 wsgi.py

ECR(Elastic Container Registry)へ作成してコンテナイメージをアップロードする前にローカルで訓練や推論のhttpアクセスが正常に動作するかを確認します。サンプルでは簡単なシェルスクリプトでテストされていますが、手打ちでも十分だと思いました。 local_test というディレクトリ配下に test_dir を作成して、/opt/programへとボリュームマウントします。 test_dir 配下の構成はSageMakerが作るディレクトリ構成と同様になるよう設計します。

test_dir directory structure

訓練のテストをします。 ./train_local.sh sklearn-container でコンテナの train を実行することでモデルの学習が進みました。シェルスクリプトの中で呼ばれているのは docker run -v $(pwd)/test_dir:/opt/ml --rm sklearn-container trainです。モデルがtest_dir/model配下に作成されていることが確認できます。

$ ./train_local.sh sklearn-container
Starting the training.
X shape: (150,4)
y shape: (150,1)
Training complete.
$ ls test_dir/model/
gradient-boost-model.pkl

次に serve を実行する ./serve_local.sh sklearn-container をローカルで実行してください。呼ばれているのはdocker run -v $(pwd)/test_dir:/opt/ml --rm sklearn-container serve です。

$ ./serve_local.sh sklearn-container
Starting the inference server with 2 workers.
[2019-11-16 06:56:04 +0000] [9] [INFO] Starting gunicorn 20.0.0
[2019-11-16 06:56:04 +0000] [9] [INFO] Listening at: unix:/tmp/gunicorn.sock (9)
[2019-11-16 06:56:04 +0000] [9] [INFO] Using worker: gevent
[2019-11-16 06:56:04 +0000] [14] [INFO] Booting worker with pid: 14
[2019-11-16 06:56:04 +0000] [15] [INFO] Booting worker with pid: 15
# after sent the invocations
Invoked with 29 records
172.17.0.1 - - [16/Nov/2019:07:25:06 +0000] "POST /invocations HTTP/1.1" 200 141 "-" "curl/7.47.0"

サーバに対してhttp:8080のポートで推論サービスを呼び出すには ./predict.sh ./payload.csvを実行します。curlでcsvデータを推論サービスへ渡して構築した勾配ブースティングのモデルによる予測を出力できます。

$ ./predict.sh ./payload.csv
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> POST /invocations HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.47.0
> Accept: */*
> Content-Type: text/csv
> Content-Length: 2900
> Expect: 100-continue
>
< HTTP/1.1 100 Continue
* We are completely uploaded and fine
< HTTP/1.1 200 OK
< Server: nginx/1.14.2
< Date: Sat, 16 Nov 2019 07:25:06 GMT
< Content-Type: text/csv; charset=utf-8
< Content-Length: 116
< Connection: keep-alive
<
0.0
0.0
0.0
0.0
...

ローカルでSageMaker用のDockerコンテナが動作することを確認できました。このコンテナイメージでは python 3.7.5scikit-learn 0.21.2 を使用した勾配ブースティングによる訓練や推論が行えます。

ECR(Elastic Container Registry)へイメージをプッシュ

SageMakerのJupyter Notebookから作成したDockerコンテナを呼び出して使用するためには、コンテナイメージをECR(Elastic Container Registry)へアップロードする必要があります。サンプルのレポジトリでは build_and_push.sh として一連の流れをシェルスクリプトで行っています。AWS CLIでECRへプッシュするにあたっては以下のサイトを参考にしました。[9]

AWS ECRにログインします。

$ aws ecr get-login --no-include-email
$ docker login -u AWS -p https://<account>.dkr.ecr.<region>.amazonaws.com
Login Succeeded

ECRにリポジトリを新規に作成します。

$ aws ecr create-repository --repository-name "sklearn-container"
{
"repository": {
"repositoryUri": "<account>.dkr.ecr.<region>.amazonaws.com/sklearn-container",
"registryId": "251344623468",
"imageTagMutability": "MUTABLE",
"repositoryArn": "arn:aws:ecr:<region>:<account>:repository/sklearn-container",
"repositoryName": "sklearn-container",
"createdAt": 1573965078.0
}
}
# Describe the existing repository
$ aws ecr describe-repositories

tagを付けて作成したECRのレポジトリへコンテナイメージをプッシュします。削除は aws ecr delete-repository で行えます。

$ docker tag sklearn-container <account>.dkr.ecr.<region>.amazonaws.com/sklearn-container
$ docker push <account>.dkr.ecr.<region>.amazonaws.com/sklearn-container
# If deletion is needed, run this
$ aws ecr delete-repository --repository-name sklearn-container --force

ECRに使用したいコンテナイメージがプッシュできたので、次はSageMakerのJupyter Notebookから実際にこのコンテナを使用して、勾配ブースティングのモデルの訓練や、推論サービスのエンドポイントのデプロイを行ってみます。

SageMakerでデータの読み込みと訓練ジョブの実行

scikit-learnのサンプルではSageMaker Python SDKをAmazon SageMakerのノートブックで読み込み、sagemaker.sklearn.estimator.sklearnから推定子を作成していました。自前のコンテナイメージを使用する場合は sagemaker.estimator.Estimator のインスタンスからECRのリポジトリURI、トレーニングインスタンスなどを呼び出します。サンプルにおいてエントリーポイントして書いたscikit-learnのスクリプトは不要です。

『scikit-learnをSageMakerで試してみる』のノートブックサンプルを変更してECRのリポジトリからコンテナイメージを呼び出して訓練するコードへと書き替えました。

sklearn-container training example
A call of sagemaker.estimator.Estimator

サンプルと同様にirisデータセットをS3に保存して、sagemaker.estimator.Estimator のインスタンスから fit を呼ぶだけです。構築されたモデルはS3のバケットの output というフォルダ配下に保存されます。フォルダがない場合は自動で作成されます。ここまででS3にあるデータを使用した訓練とモデルの構築ができたので、AWS上で推論サービスとしてデプロイしてみます。

推論サービスのデプロイとテスト

推論サービスのデプロイもサンプルと同様に deploy を呼ぶだけです。インスタンスの数やインスタンスタイプなどをパラメータとして渡します。

from sagemaker.predictor import csv_serializer
predictor = clf.deploy(initial_instance_count=1, 'ml.m4.xlarge', serializer=csv_serializer)

推論サービスのエンドポイントとしてSageMaker上で立ち上がったことが確認できました。以下の赤枠がエンドポイントのURLです。

Inference endpoint has been launched

Amazon SageMakerのノートブックでサンプルと同様にテストデータを渡して推論サービスを利用してみます。

import itertools
import pandas as pd
shape = pd.read_csv("./data/iris.csv", header=None)
a = [50*i for i in range(3)]
b = [40+i for i in range(10)]
indices = [i+j for i,j in itertools.product(a,b)]
test_data = shape.iloc[indices[:-1]]
print("test_data: {}".format(test_data.shape))
test_X = test_data.iloc[:,1:]
test_y = test_data.iloc[:,0]
print("test_X: {}".format(test_X.shape))
print("test_y: {}".format(test_y.shape))
print("Predicted values:\n{}".format(predictor.predict(test_X.values).decode('utf-8')))
print("test_y values:\n{}".format(test_y.values))

推論したラベルの値と実際のデータを確認したところ完全一致していました。決定木だけでなく、scikit-learnの勾配ブースティングのモデルにおいても同様にirisデータセットを解くことができました。

predictions = predictor.predict(test_X.values).decode('utf-8')
predictions_array = np.fromstring(predictions, sep=' ') # and turn the prediction into an array
print("Predicted values:\n{}".format(predictions_array))
print("test_y values:\n{}".format(test_y.values))
Prediction by the inference endpoint

注意点として、このコンテナではcsvで訓練用のデータを渡していますが、最初の列が正解ラベルであることを前提としているため、このイメージを使用する場合は常にデータがその処理に適合するようにデータ整形しておく必要があります。以下、サンプルのコンテナと異なる点を箇条書きにしておきます。

  • python3.7.5の採用
  • scikit-learnのバージョン0.21.2への移行
  • python2系から3系による種々修正

コードのリポジトリは以下です。テストしたJupyter Notebookも置いてあります。

https://github.com/yuyasugano/sagemaker-sklearn-container

まとめ

  • Amazon SageMakerは 機械学習モデルの構築、トレーニング、デプロイなどを提供するAWSのサービスである
  • AWSのサービススタックの中で、MLサービスとして分類され、ラベリングやJupyter Notebookによる機械学習モデルの開発から、推論サービスのデプロイまでをフルマネージドで提供する
  • TensorFlow、Apache MXNet、PyTorch、Chainer、Scikit-learn、SparkML、Horovod、Keras、Gluonなどのフレームワークをサポートしている
  • SageMakerで提供されるコンテナの他、自前のコンテナイメージをECR(Elastic Container Registry)へプッシュすることで独自アルゴリズムおよび処理がSageMakerで利用可能となる
  • 決定木を使用するサンプルのコードを参考に、python3.7.5で勾配ブースティングを用いるコンテナを構築し、Amazon SageMakerのノートブックで同様に使用できることを確認した

--

--

Yuya Sugano
Yuya Sugano

Written by Yuya Sugano

Cloud Architect and Blockchain Enthusiast, techflare.blog, Vinyl DJ, Backpacker. ブロックチェーン・クラウド(AWS/Azure)関連の記事をパブリッシュ。バックパッカーとしてユーラシア大陸を陸路横断するなど旅が趣味。

No responses yet