MLモデルのトレーニングが終わると、次に必ず来る問いがある:どうやってproductionに出すか? 自分もモデルをラップするFlask APIを自前で書いた経験が何度もあって、その都度いろんな問題に直面した――バージョン管理の欠如、スケールの難しさ、ログの混乱。チームがBentoMLを試したとき、ML servingに対する考え方が根本から変わった。
全体像:MLモデルのデプロイ方法
実際には、MLモデルをAPIとして公開する方法はいくつかある。大きく4つのグループに分けられる:
- 自前でAPI実装(Flask/FastAPI) — 最も柔軟だが、すべてを一から自分で処理する必要がある
- BentoML — ML serving専用のフレームワーク、多くのproduction機能が組み込まれている
- TorchServe — PyTorch専用、PyTorchオンリーのチームには最適
- Triton Inference Server(NVIDIA) — 超高性能だが複雑で、GPUクラスターが必要
各アプローチの詳細比較
Flask/FastAPIによる自前実装
Flask/FastAPIは誰でも知っているため、最も一般的な方法だ。モデルをロードしてエンドポイントを書けば完成。だが問題は別のところにある:
- モデルのバージョン管理機能がない――モデルを更新するたびにサービス全体を再デプロイする必要がある
- バッチ処理は自前で実装必須――高負荷時のスループットに大きく影響する
- ヘルスチェックやモニタリングも自前で追加実装が必要
- モデルと依存関係をまとめてパッケージ化する標準的な方法がない
自分もそういったFlaskサービスを6ヶ月間メンテナンスしたことがある。データサイエンティストがモデルを更新するたびに、チーム全員が手動でコーディネートしなければならなかった――ファイルのコピー、サービスの再起動、ログの確認。非常に時間がかかり、インシデントも起きやすかった。
TorchServe
チームがPyTorchのみを使用していて、Metaの公式ソリューションを求めるならTorchServeが選択肢になる。ただし、かなり制約が多い――PyTorchのみ対応、XMLベースの設定(やや古い)、実際のエッジケースに関するドキュメントが不足している。
Triton Inference Server
GPUのパフォーマンスを最大限に引き出す必要があるとき――ダイナミックバッチング、複数バックエンド(TensorRT、ONNX、TF、PyTorch)――Tritonはセットアップの手間に見合う。問題は学習曲線が急で、設定が複雑で、GPUクラスターがなければ明らかにover-engineeringになることだ。
BentoML
BentoMLは「自前のFlask」と「複雑なTriton」のちょうど中間に位置する。フレームワーク非依存(sklearn、PyTorch、TF、XGBoost、ONNXなどをサポート)、バージョン管理の組み込み、アダプティブバッチング、Dockerの自動エクスポートを備えている。複雑なセットアップなしに、80%のproductionユースケースに対応できる。
BentoMLをproductionに選ぶ理由
比較の結果、自分のDevOpsチームが3つの具体的な理由からBentoMLに決めた:
- モデルレジストリが内蔵済み — バージョン管理付きのモデル保存・読み込みが可能で、この用途だけのためにDVCやMLflowを別途用意する必要がない
- Bento = 完全にパッケージ化されたアーティファクト — モデル+コード+依存関係を一つのアーティファクトにまとめ、どこにでもデプロイできる
- Dockerイメージの自動生成 — 1コマンドでproduction-readyなDockerイメージが完成し、複雑なDockerfileを書く必要がない
最もあまり語られないメリットが、実はチームにとって最も重要だった:「新しいモデルはどこに置いた?Pythonのバージョンは?依存関係は何が必要?」というやりとりがなくなった。データサイエンティストがタグ付きでモデルを保存し、DevOpsがそのタグを使ってDockerをビルドする。この標準化されたプロセスのおかげで、新しいモデルをリリースするたびに約2時間の節約になった。
Linux上でのBentoMLデプロイ手順
1. インストール
pip install bentoml
# バージョン確認
bentoml --version
2. BentoMLモデルストアへのモデル保存
最初のステップは、トレーニング済みモデルをBentoMLレジストリに登録すること。scikit-learnの例:
import bentoml
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris
# モデルをトレーニング(実際にはすでにモデルがある想定)
iris = load_iris()
clf = RandomForestClassifier(n_estimators=100)
clf.fit(iris.data, iris.target)
# BentoMLモデルストアに保存
saved_model = bentoml.sklearn.save_model(
"iris_classifier",
clf,
signatures={"predict": {"batchable": True}},
metadata={"accuracy": 0.97, "dataset": "iris"}
)
print(f"Model saved: {saved_model.tag}")
# 出力例: iris_classifier:3mxqpfzbs6tpjuqj
モデルは~/bentoml/models/に保存され、タグハッシュによる自動バージョン管理が行われる――ロールバックしたいときはタグを変えるだけでいい。
3. BentoML Serviceの作成
核心部分 ― APIエンドポイントと処理ロジックを定義する:
# service.py
import bentoml
import numpy as np
from bentoml.io import NumpyNdarray, JSON
# レジストリからモデルを読み込む
iris_runner = bentoml.sklearn.get("iris_classifier:latest").to_runner()
svc = bentoml.Service("iris_classifier_service", runners=[iris_runner])
IRIS_CLASSES = ["setosa", "versicolor", "virginica"]
@svc.api(input=NumpyNdarray(shape=(-1, 4), dtype=np.float32), output=JSON())
async def predict(input_data: np.ndarray):
batch_pred = await iris_runner.predict.async_run(input_data)
result = [IRIS_CLASSES[i] for i in batch_pred]
return {"predictions": result}
4. ローカルでのテスト
# 開発サーバーを起動
bentoml serve service:svc --reload
# curlでテスト(別のターミナルで)
curl -X POST http://localhost:3000/predict \
-H "Content-Type: application/json" \
-d '[[5.1, 3.5, 1.4, 0.2]]'
# レスポンス:
# {"predictions": ["setosa"]}
5. Bentoアーティファクトのビルド
bentofile.yamlを作成して依存関係をすべて記述する:
service: "service:svc"
labels:
owner: devops-team
project: iris-api
include:
- "service.py"
python:
packages:
- scikit-learn
- numpy
bentoml build
# 出力:
# Successfully built Bento(tag="iris_classifier_service:7a3bk2...")
# Bento size: 15.2 MB
6. DockerによるProductionへのデプロイ
# BentoをDockerイメージにコンテナ化
bentoml containerize iris_classifier_service:latest
# タグ付けしてレジストリにプッシュ
docker tag iris_classifier_service:latest your-registry.com/iris-api:v1
docker push your-registry.com/iris-api:v1
# コンテナを起動
docker run -p 3000:3000 iris_classifier_service:latest serve
7. systemdを使ったLinuxへの直接デプロイ
Dockerを使わない場合は、systemdサービスを作成してプロセスを管理する:
# /etc/systemd/system/iris-api.service
[Unit]
Description=BentoML Iris Classifier API
After=network.target
[Service]
User=www-data
WorkingDirectory=/opt/iris-api
ExecStart=/opt/iris-api/venv/bin/bentoml serve iris_classifier_service:latest \
--host 0.0.0.0 \
--port 3000 \
--workers 4
Restart=always
RestartSec=5
Environment=BENTOML_HOME=/opt/iris-api/bentoml
[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable iris-api
systemctl start iris-api
systemctl status iris-api
Production運用時の注意点
アダプティブバッチング
BentoMLにはアダプティブバッチング機能がある――複数の小さなリクエストを自動的にバッチにまとめてスループットを向上させる。bentofile.yamlで設定:
runners:
- name: iris_runner
max_batch_size: 100
max_latency_ms: 15
Prometheusによるモニタリング
BentoMLは/metricsにPrometheusメトリクスをあらかじめ公開している。Prometheusの設定に追加するだけですぐに使える:
scrape_configs:
- job_name: 'bentoml'
static_configs:
- targets: ['your-server:3000']
ヘルスチェックエンドポイント
/healthzと/readyzに組み込み済み――何も追加実装せずにKubernetesのliveness/readiness probeとしてそのまま使える。
まとめ
自前で書いたFlaskサービス3つをBentoMLに移行した。新しいモデルをproductionに上げるまでの時間――データサイエンティストから引き渡されてAPIが動くまで――は約3時間から20〜30分に短縮された。主にDockerイメージのビルド時間だ。モデルレジストリのおかげで、新しいモデルに問題が生じたときのロールバックも容易になった。
BentoMLは万能ではない。TensorRTでGPUのパフォーマンスを最大化する必要があるならTritonの方が適している。しかしほとんどのML/DevOpsチームにとって、BentoMLはデプロイ速度とproductionの安定性の間のバランスポイントとして実用的だ。pip install bentoml scikit-learnで今すぐ試して、上記の手順を実行してみてほしい。

