Sansan Tech Blog

Sansanのものづくりを支えるメンバーの技術やデザイン、プロダクトマネジメントの情報を発信

【Techの道も一歩から】第18回「アルゴリズムをコンテナ化しWebAPIとして利用可能に」

こんにちは。 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 のエントリーポイントをシェルスクリプトで記述し、trainapiといったように引数で動作状態を変更することで、学習から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株式会社に入社。現在はキーワード抽出など自然言語処理を生かした研究に取り組んでいる。

© Sansan, Inc.