こんにちは。 DSOC R&D グループの高橋寛治です。
最近はスマブラSPの研究活動に熱心に取り組んでおり、少しずつ成果が出始めておりホッとしているところです。
さて、研究開発活動では何かしらのモデルやアルゴリズムを開発し、それを共有します。 共有する際に、環境の違いにより動作しなかったり、そもそもの利用方法がわからなかったりします。
こういった問題に対して、Docker コンテナにより WebAPI として提供することで、環境を問わず、かつ利用方法も明確にする方法について紹介します。
API提供するコンテナの作成
アルゴリズムを組み込むための Docker コンテナを作成します。
ディレクトリ構成
$ tree . ├── Dockerfile ├── api.py └── requirements.txt
Dockerfile
FROM python:3.6 WORKDIR /app COPY requirements.txt . RUN pip install -r requirements.txt COPY . . CMD ["hug", "-f", "api.py"]
requirements.txt
hug==2.4.1
api.py
import hug @hug.get("/") def health_check(): return { "message": "Good." }
hug は Python で簡単に API を提供するためのフレームワークです。 デコレータで任意のメソッドについてラップするだけで、APIエンドポイントとして動作します。 アルゴリズムのAPI化においてラップするだけで動作するというのは魅力的です。
ファイルを準備したら、コンテナをビルドして動かしてみましょう。
$ docker build -t tech_blog_18 Sending build context to Docker daemon 4.096kB Step 1/6 : FROM python:3.6 ... Successfully tagged tech_blog_18:latest $ docker run -it --rm -p 8000:8000 tech_blog_18 ... Serving on :8000...
さてコンテナが稼働中ですので、実際に動作確認をしてみます。
curl
コマンドで localhost:8000
を叩くことで、メッセージが返ってくるはずです。
$ curl -XGET "http://localhost:8000" {"message": "Good."}
WebAPI としての動作をしていることが確認できました。 この関数内で任意のアルゴリズムを動作させることで、コンテナ化します。
アルゴリズムを組み込んでみる
scikit-learn が用意している hand-written digits データセットを使って学習済みモデルを準備します。 APIではこのモデルを読み込み、APIとして提供します。
train.py
# 公式マニュアルに記載のコードから最低限必要な部分を抜き出しています from sklearn import datasets, svm, metrics from sklearn.externals import joblib digits = datasets.load_digits() n_samples = len(digits.images) data = digits.images.reshape((n_samples, -1)) classifier = svm.SVC(gamma=0.001) classifier.fit(data[:n_samples // 2], digits.target[:n_samples // 2]) expected = digits.target[n_samples // 2:] predicted = classifier.predict(data[n_samples // 2:]) print("Classification report for classifier %s:\n%s\n" % (classifier, metrics.classification_report(expected, predicted))) print("Dump svc.pkl") joblib.dump(classifier, 'svc.pkl')
これを実行すると、分類の評価結果が表示された後に svc.pkl
が保存されます。
この保存したモデルをAPIで読み込みます。
requirements.txt
hug==2.4.1 requests==2.21.0 scikit-learn==0.20.3
api.py
import hug from sklearn.externals import joblib @hug.get("/") def health_check(): return { "message": "Good." } classifier = joblib.load("svc.pkl") @hug.post("/digit/recognize") def recognize_digt(data): return classifier.predict([data])[0]
joblib
モジュールによりダンプしていたモデルを読み込みます。
そしてPOSTリクエストでデータを受け取って推論した結果をクライアントに返します。
クライアントからは学習時と同様のデータをAPIリクエストします。 APIリクエストのサンプルファイルを書いてみます。 サンプルリクエストがあることで、入出力が明確になります。
sample_api_requiest.py
import json import random import requests from sklearn import datasets digits = datasets.load_digits() n_samples = len(digits.images) data = digits.images.reshape((n_samples, -1)) response = requests.post( "http://localhost:8000/digit/recognize", data=json.dumps({"data": random.choice(data).tolist()}), headers={'Content-Type': 'application/json'} ) print(response.json())
同じデータセットについて同じ加工をしてから、 requests
モジュールにより POST で JSON を投げています。
さて、それでは実行してみましょう。
$ docker build -t tech_blog_18 Sending build context to Docker daemon 4.096kB Step 1/6 : FROM python:3.6 ... Successfully tagged tech_blog_18:latest $ docker run -it --rm -p 8000:8000 tech_blog_18 ... Serving on :8000...
別のターミナルでサンプルリクエストを投げてみます。
$ python sample_api_request.py
4
正解かどうかはわかりませんが、「4」であると返って来ました。
このように、アルゴリズムをコンテナ化することで、他の環境で容易に動作させることができます。
ここから発展させていくと、Docker のエントリーポイントをシェルスクリプトで記述し、train
とapi
といったように引数で動作状態を変更することで、学習からAPI提供までを一つのコンテナで行うことができます。
最終的なディレクトリ構成はこのようになります。
$ tree . ├── Dockerfile ├── api.py ├── requirements.txt ├── sample_api_request.py ├── svc.pkl └── train.py
hug だけでは並列処理など頼りないため、 gunicorn を使うのもいいでしょう。
簡単な使い方ですが、 gunicorn -w 2 api:__hug_wsgi__ -b 0.0.0.0:8000
というように、ワーカ数2、0.0.0.0:8000
で起動、対象のPython処理部分は api.pyの __hug_wsgi__
と設定することで利用できます。
hug はインポートしていれば WSGI を __hug_wsgi__
で提供していますので、簡単に使えます。
ポータビリティを重視する
自分が開発したアルゴリズムが他の環境で簡単に動かせることで、その良さを使ってもらって示すことができます。
最近、実際にコンテナ化して API からアルゴリズムを読み込むという枠組みで、インターン生が開発されたアルゴリズムのポータビリティを担保することができました。
アルゴリズムの開発初期からコンテナ化を意識しておくことで、連携速度は爆速になると思いますので、コンテナを活用していくのはいいのではないかと考えています。
執筆者プロフィール
高橋寛治 Sansan株式会社 DSOC (Data Strategy & Operation Center) R&Dグループ研究員
阿南工業高等専門学校卒業後に、長岡技術科学大学に編入学。同大学大学院電気電子情報工学専攻修了。在学中は、自然言語処理の研究に取り組み、解析ツールの開発や機械翻訳に関連する研究を行う。大学院を卒業後、2017年にSansan株式会社に入社。現在はキーワード抽出など自然言語処理を生かした研究に取り組んでいる。