はじめに
機械学習プロジェクトで学習フェーズが完了すると、そのモデルを活用するフェーズに移ります。ローカルPC上で手動で推論するのであれば悩む事は無いですが、システムとして組み込むとなると、サービス提供のアーキテクチャを考える必要があります。そこでBentoMLというModel ServingのOSSに注目してみました。
BentoMLとは
BentoMLは機械学習モデルを管理・提供するためのフレームワークです。様々なMLフレームワークで生成されたモデルを、統一的な手順・インターフェースによって提供するもので、モデル学習後から推論機能の提供までの準備や手続きを簡単にしてくれます。
また関連機能として、Kubernetes環境へのデプロイをサポートするYatai、AWS、Azure、GCP等のクラウド環境へのデプロイをサポートするbentoctlというOSSも合わせて公開されています。
今回はBentoMLの基本的な機能を中心に整理しようと思います。
前提条件とインストール方法
BentoMLはLinux/UNIX、Windows、MacOSをサポートしています。前提条件として Python 3.7 以上が必要となります。また、今回の話ではDockerも利用します。
インストールはpipで行います。
pip install bentoml
pip install "bentoml[all]"
基本的な作業プロセス
大まかなプロセスとしては以下のようになります。
Step1. 学習済モデルの管理
学習が完了したモデルをMentoMLモデルストアに格納します。
Step2. 推論サービスの作成
外部からAPIとして呼び出すことのできる推論サービスをプログラミングします。
Step3. ビルド
学習済モデル、推論サービスプログラム、その他必要な設定ファイルやデータファイルを取り纏めて1つのアーカイブファイルを作成します。このアーカイブをBento
と言います。
Step4. デプロイ
BentoからDockerイメージを生成してDockerコンテナとしてデプロイします。それ以外にも、Kubernetesやクラウドへのデプロイもサポートしています。
以下では公式ドキュメントにあるチュートリアルをベースに各ステップの内容を整理しています。
Step1. 学習済モデルの管理
BentoMLでは、まず、学習済モデルをBentoML model store
に保存します。保存はsave_model
メソッドで行います。
import bentoml
from sklearn import svm
from sklearn import datasets
# 学習データセットのロード
iris = datasets.load_iris()
X, y = iris.data, iris.target
# モデルの学習
clf = svm.SVC(gamma='scale')
clf.fit(X, y)
# BentoML model storeへの保存
saved_model = bentoml.sklearn.save_model("iris_clf", clf)
print(f"Model saved: {saved_model}")
上記の例では、モデル名"iris_clf"とscikit-learnで学習されたモデルを渡しています。保存したモデルにはバージョン識別のためのモデルタグ(例: iris_clf:2uo5fkgxj27exuqj
)が自動的に割り振られます。このタグはBentoML model storeに格納された学習済モデルを操作する際の識別子になります。
また最新バージョンのモデルに対してはiris_clf:latest
という指定も可能です。
モデル保存時の追加情報
save_model
メソッドでのモデル保存時に、追加の情報やオブジェクトを付与することもできます。
- ラベル
labels
: モデルを管理しやすくするためにユーザー定義したラベル。例: 開発チーム名、ステージ名(dev,prd) - メタデータ
metadata
: モデル関連情報を付加するユーザー定義したメタデータ。例: 精度、データセットバージョン - カスタムオブジェクト
custom_objects
: モデルと一緒に保存されるユーザー定義の追加Pythonオブジェクト、定義ファイル等。
保存されたモデルの取り出し
モデルオブジェクトの取り出しはload_model
メソッドを使います。
import bentoml
from sklearn.base import BaseEstimator
model: BaseEstimator = bentoml.sklearn.load_model("iris_clf:latest")
また、ラベル、メタデータ、カスタムオブジェクトの情報を取り出すにはget
メソッドを使います。
import bentoml
bento_model: bentoml.Model = bentoml.models.get("iris_clf:latest")
print(bento_model.tag)
print(bento_model.custom_objects)
print(bento_model.info.metadata)
print(bento_model.info.labels)
BentoML model storeの管理
BentoML model storeの実体は、デフォルトではローカルPCのホームディレクトリの~/bentoml/models
ディレクトリになります。保存された学習済モデルはbentoml models
コマンドを通じて管理します。
# 保存されたモデルの一覧確認
bentoml models list
# モデル情報の確認
bentoml models get <モデルタグ名>
# モデルの削除
bentoml models delete <モデルタグ名>
Step2. 推論サービスの作成
外部から推論が実行できるためにBentoMLサービスを作成します。これはmodel runnerとAPIで構成されます。構成イメージを図にすると下記のようになります。
チュートリアルにも記載されていますが、コード化すると下記のようになります。
import numpy as np
import bentoml
from bentoml.io import NumpyNdarray
iris_clf_runner = bentoml.sklearn.get("iris_clf:latest").to_runner()
svc = bentoml.Service("iris_classifier", runners=[iris_clf_runner])
@svc.api(input=NumpyNdarray(), output=NumpyNdarray())
def classify(input_series: np.ndarray) -> np.ndarray:
result = iris_clf_runner.predict.run(input_series)
return result
model runnerの定義
model runnerは学習済モデルを取り込んで推論ができるようにしたユニットと捉えればよいです。model runnerはBentoML model storeから学習済モデルをget
メソッドで取り出して、to_runner()
メソッドを呼び出すことで取得できます。
iris_clf_runner = bentoml.sklearn.get("iris_clf:latest").to_runner()
ちなみに、model runnerは推論ができるユニットですので、これだけでも動作テストは可能です。以下のようなロジックで実行できます。
iris_clf_runner = bentoml.sklearn.get("iris_clf:latest").to_runner()
# runnerを初期化してこのプロセス内で呼び出せる状態にします
iris_clf_runner.init_local()
# 推論の実行
iris_clf_runner.predict.run([[5.9, 3., 5.1, 1.8]])
APIの定義
APIは外部のクライアントから呼び出すことができるロジックのユニットになります。
@svc.api(input=NumpyNdarray(), output=NumpyNdarray()) # 入出力の定義
def classify(input_array: np.ndarray) -> np.ndarray:
# == 必要に応じてビジネスロジックや前処理ロジックを記述します ==
result = iris_clf_runner.predict.run(input_array) # 推論の呼び出し
# == 必要に応じて後処理ロジックを記述します ==
return result
- APIは1つの関数で定義します。その関数にデコレーターを付与します。
- デコレーターではAPIの入出力を定義します。この定義では
IO descriptors
というものを使用します。上記例では入出力ともにnumpy.ndarray
の型を指定しています。その他の IO descriptors についてはこちらを参照してください。 - 関数の中でmodel runnerを呼び出すことで推論を実行します。また、推論の前後で前処理や後処理などのロジック追加も可能です。
推論サービスの起動
推論サービスのpythonプログラムを作成したら、実際にサービスとして起動することができます。
bentoml serve service:svc
- デフォルトではlocalhost:3000でサーバが起動します。
- デフォルトでは、エンドポイント=API関数名になります。(エンドポイントはデコレーターで独自に指定することも可能です。)
(ターミナルで実行)外部からの推論サービスの呼び出し例
curl -X POST \ -H "content-type: application/json" \ --data "[[5.9, 3, 5.1, 1.8]]" \ http://127.0.0.1:3000/classify
Step3. ビルド
Step2までで推論サービスの実行ができるようになりました。これを別のシステム環境でも動作できるように1つのアーカイブファイルに纏めることを行います。具体的にはBento
と呼ばれるBentoML独自のアーカイブファイルとして纏めます。この中にはソースコード、学習済モデル、データファイル、設定ファイルなど、サービスを動作せるのに必要なファイルが、まさに"お弁当🍱"のように敷き詰められたものです。
ビルドファイル
ビルドをするにあたってはビルドの定義ファイルbentofile.yaml
を作成します。こちらはチュートリアルと同様の定義ファイルです。
service: "service:svc"
labels:
owner: bentoml-team
stage: dev
include:
- "*.py" # Bentoに含めるファイルのパターン
python:
packages: # サービスに必要なpythonパッケージ
- scikit-learn
- pandas
上記のビルドファイルは見ただけでも分かるごくシンプルなのものですが、実際には様々なオプションが用意されていますので詳細は公式ドキュメントを確認してください。
ビルドの実行
ビルドはbentoml build
コマンドで実行します。
bentoml build
このコマンドはbentofile.yamlやservice.pyが存在するディレクトリ(=ビルドコンテキスト)で実行します。そこが異なる場合は、そのパスを指定します。また、ビルドファイルを別名で指定する場合は-f
オプションで指定します。
# 書式: bentoml build [OPTIONS] [BUILD_CTX]
bentoml build -f ./src/my_project_a/bento_fraud_detect.yaml ./src/
ビルドが成功すると下記メッセージが表示されます。生成されたBentoには一意のBentoタグが自動的に付けられます。
Building BentoML service "iris_classifier:viwdyfu54cv4qaav" from build context "/home/ubuntu/direction3/08_bentoml/quickstart".
Packing model "iris_clf:42c6hfu3r2bsmaav"
Locking PyPI package versions.
WARNING: the legacy dependency resolver is deprecated and will be removed in future versions of pip-tools. The default resolver will be changed to 'backtracking' in pip-tools 7.0.0. Specify --resolver=backtracking to silence this warning.
██████╗░███████╗███╗░░██╗████████╗░█████╗░███╗░░░███╗██╗░░░░░
██╔══██╗██╔════╝████╗░██║╚══██╔══╝██╔══██╗████╗░████║██║░░░░░
██████╦╝█████╗░░██╔██╗██║░░░██║░░░██║░░██║██╔████╔██║██║░░░░░
██╔══██╗██╔══╝░░██║╚████║░░░██║░░░██║░░██║██║╚██╔╝██║██║░░░░░
██████╦╝███████╗██║░╚███║░░░██║░░░╚█████╔╝██║░╚═╝░██║███████╗
╚═════╝░╚══════╝╚═╝░░╚══╝░░░╚═╝░░░░╚════╝░╚═╝░░░░░╚═╝╚══════╝
Successfully built Bento(tag="iris_classifier:viwdyfu54cv4qaav").
Bento storeの管理
生成されたBentoはBento store
で管理されます。BentoML storeの実体は、デフォルトではローカルPCのホームディレクトリの~/bentoml/bentos
ディレクトリになります。保存されたBentoはbentomlのコマンドを通じて管理します。
# 保存されたBentoの一覧確認
bentoml list
# Bento情報の確認
bentoml get <Bentoタグ名>
# Bentoの削除
bentoml delete <Bentoタグ名>
Step4. デプロイ
最後のステップのデプロイです。BentoMLでは、Dockerコンテナとしてデプロイ、Yatai
のサービスを通じてKubernetes環境にデプロイ、bentoctl
を利用してクラウド環境にデプロイ、の3パターンがあります。ここではDockerコンテナでのデプロイを記載します。
Dockerの場合は手順としては簡単で、BentoMLのCLIを使ってDockerイメージを生成し、DockerコマンドでDockerコンテナを起動する、流れになります。
# Bento storeに格納されているBentoの確認
bentoml list
# Dockerイメージの生成
bentoml containerize iris_classifier:latest
Dockerイメージが生成されてdocker image
コマンドで確認すると、REPOSITORYがBentoのサービス名、TAGがBentoの識別子になっていることが確認できます。
最後はdocker run
でDockerコンテナを実行します。
docker run -p 3000:3000 iris_classifier:fm454nu24odx4aav
おわりに
今回はBentoMLの基本的な機能と操作方法を整理しましたが、BentoMLでバッチ推論や非同期推論のサービスも定義できますし、他のMLOps関連のフレームワークとの連携も可能です。更に進んだ利用方法については今後調べていこうと思っています。