🎓 レベル:標準 | 重要度:A(必須)
📎 前提:モデルレジストリとモデルのライフサイクル | 関連:オンライン推論サービング
要点(BLUF)
- モデルのデプロイには3パターンあります:バッチ(定期一括・高スループット)、オンライン(リクエスト毎・低遅延)、ストリーミング(イベント駆動・準リアルタイム)。
- 選択を決めるのは予測の鮮度要件と遅延要件です。「予測がいつ必要か」「どれだけ待てるか」で一意に近く決まります。アルゴリズムは同じでも、配信形態が違えば設計も運用も別物になります。
- 迷ったらまずバッチ。バッチで足りるならオンラインの複雑さ(常時稼働・低遅延・スケール)を背負う必要はありません。要件がオンラインを要求したときに初めてオンライン推論サービングへ進みます。
1. 3つのパターン
| パターン | 実行契機 | 遅延 | 鮮度 | 例 |
|---|---|---|---|---|
| バッチ | スケジュール(夜次等) | 分〜時間 | 古くてよい | 全顧客の解約スコアを毎晩計算 |
| オンライン | リクエスト毎 | ミリ秒 | 最新が必要 | 申込時の与信判定 |
| ストリーミング | イベント到着 | 秒 | 準リアルタイム | 取引ごとの不正検知 |
2. 図解:3パターンのデータフロー
flowchart TB
subgraph バッチ
B1["スケジューラ起動"] --> B2["全データを一括読込"]
B2 --> B3["まとめて推論"]
B3 --> B4["結果をテーブルに書き出し"]
end
subgraph オンライン
O1["リクエスト到着"] --> O2["特徴量取得"]
O2 --> O3["即時推論"]
O3 --> O4["低遅延で応答"]
end
subgraph ストリーミング
S1["イベントストリーム"] --> S2["イベント毎に推論"]
S2 --> S3["結果を下流へ発行"]
end
3. 選択の判断軸
flowchart TB
Q1{"予測は即時に必要か"} -->|"いいえ 事前計算で足りる"| BATCH["バッチ推論"]
Q1 -->|"はい"| Q2{"きっかけはリクエストかイベントか"}
Q2 -->|"ユーザーのリクエスト"| ONLINE["オンライン推論"]
Q2 -->|"流れてくるイベント"| STREAM["ストリーミング推論"]
- バッチで足りるか? 予測をあらかじめ計算してテーブルに置けば済むなら、それが最も安く堅い。
- リクエスト駆動か? ユーザー操作に応じて「その場の入力」で予測するならオンライン。
- イベント駆動か? 絶え間なく流れるイベントに反応するならストリーミング(特徴量ストアのオンライン側やデータドリフトとコンセプトドリフトの検知と相性が良い)。
4. 動く最小例:同じモデルを3パターンで動かす
同一モデルを、(a) バッチ(全件一括)、(b) オンライン(1件ずつ)、(c) ストリーミング(イベント列を逐次)で動かし、スループットと1件あたり遅延の違いを見ます。
import time
import numpy as np
from sklearn.linear_model import LogisticRegression
rng = np.random.default_rng(0)
Xtr = rng.normal(0, 1, (500, 4))
ytr = (Xtr[:, 0] - Xtr[:, 1] > 0).astype(int)
model = LogisticRegression().fit(Xtr, ytr)
data = rng.normal(0, 1, (2000, 4)) # 推論対象
# (a) バッチ:全件まとめて
t0 = time.perf_counter()
_ = model.predict(data)
batch_ms = (time.perf_counter() - t0) * 1000
# (b) オンライン:1件ずつ(リクエスト毎を模擬)
t0 = time.perf_counter()
for x in data[:200]:
_ = model.predict(x.reshape(1, -1))
online_ms_per = (time.perf_counter() - t0) * 1000 / 200
# (c) ストリーミング:イベント列を小ロットで逐次
t0 = time.perf_counter()
for i in range(0, 2000, 50):
_ = model.predict(data[i:i+50])
stream_ms = (time.perf_counter() - t0) * 1000
print(f"(a) バッチ2000件一括 : {batch_ms:.2f} ms 合計")
print(f"(b) オンライン1件あたり : {online_ms_per:.3f} ms/件")
print(f"(c) ストリーミング小ロット : {stream_ms:.2f} ms 合計")
print(f"1件あたり: バッチ={batch_ms/2000:.4f} ms オンライン={online_ms_per:.4f} ms")
出力例(数値は環境依存):
(a) バッチ2000件一括 : 0.15 ms 合計
(b) オンライン1件あたり : 0.052 ms/件
(c) ストリーミング小ロット : 2.10 ms 合計
1件あたり: バッチ=0.0001 ms オンライン=0.0519 ms
出力の意味:同じモデルでも、バッチは1件あたりの単価が圧倒的に安い(2000件をまとめて行列演算するため1件0.0001ms)。オンラインは1件ずつ呼ぶので1件あたりの固定オーバーヘッドが数百倍大きく(0.05ms程度)、その代わり「今この入力に即答できる」。ストリーミングは中間で、小ロットでまとめつつ低遅延を保ちます。遅延と単価はトレードオフで、要件がどちらを要求するかでパターンが決まります(数値は実行環境で変動します)。
5. 運用の勘所
- まずバッチを検討する:常時稼働サーバ・低遅延・スケールという重い要件を、本当に必要なときだけ背負う。
- 鮮度と遅延を要件として明文化する:「予測は何分前まで古くてよいか」「応答は何ミリ秒以内か」を決めれば、パターンは自ずと決まる。
- 同じモデル・同じ変換器を全パターンで共有する:配信形態が違ってもスキューを出さない(学習推論スキューの防止)。
- ハイブリッドもあり:頻出ケースはバッチで事前計算してキャッシュ、稀なケースだけオンライン、という併用も有効。
なぜそうするのか
デプロイパターンを要件から選ぶのは、過剰設計を避けるためです。オンラインサービングは魅力的に見えますが、24/7 稼働・低遅延保証・オートスケールという運用負荷を伴います。バッチで足りる問題にオンラインを使うのは、コストとリスクの無駄遣いです。逆に、即時性が必要な問題をバッチで回すと価値を逃します。要件(鮮度・遅延)が設計を決める、という順序を守ります。
⚠️ よくある落とし穴
- 何でもオンラインにする:バッチで足りる予測にオンラインの複雑さを背負い込む。コスト・障害点が増える。
- バッチの鮮度を過信する:夜次バッチでは日中の変化に追従できない。鮮度要件を確認する。
- パターンごとに変換を別実装する:配信形態が違うとスキューが入りやすい。変換器を共有する。
- ストリーミングを安易に選ぶ:イベント基盤・状態管理の運用負荷が高い。本当にイベント駆動が要るか問う。
対応 lab
- なし(概念ノート)。オンラインの実装は オンライン推論サービング の
labs/04_serving、バッチの単価優位は レイテンシとスループットのトレードオフ で深掘り。
関連ノート
- モデルレジストリとモデルのライフサイクル(配信元の Production モデル)
- オンライン推論サービング(オンラインの実装)
- リリース戦略(シャドー・カナリア・A-Bテスト)(新モデルの安全な切替)
- レイテンシとスループットのトレードオフ(遅延と単価の詳細)
- 第4章 デプロイとサービング 目次
- MLOps・AI基盤 全体目次