2026年Python AIモデルサービング:NVIDIA Triton本番ガイド
2026年、まだFlaskでmodel.predict()をラップして本番運用しているなら、そろそろ目を覚ますべきです。AIモデルのデプロイは、Dockerに詰め込むだけでは済まない——同時実行、レイテンシ、GPU利用率、マルチモデルスケジューリング、動的バッチ処理、すべてが本番環境の生命線です。
その中で、NVIDIA Triton Inference Server(旧TensorRT Inference Server)は、本番レベルのモデルサービングのトップランナーとして確固たる地位を築いています。大企業の推薦システム、NLPサービス、スタートアップのビジョンAI製品のいずれにおいても、Tritonは2026年のデファクトスタンダードです。
なぜか?比較表を見てみましょう:
| 機能 | Triton Inference Server | TorchServe | TF Serving | vLLM |
|---|---|---|---|---|
| マルチフレームワーク | ✅ PyTorch/TF/ONNX/TensorRT/Python | ❌ PyTorchのみ | ❌ TFのみ | ❌ LLMのみ |
| 動的バッチ処理 | ✅ ネイティブ対応 | ⚠️ 限定 | ⚠️ 限定 | ✅ PagedAttention |
| マルチモデル同時実行 | ✅ モデルインスタンススケジューリング | ❌ 単モデル中心 | ❌ 単モデル中心 | ❌ 単モデル |
| GPU共有 | ✅ MIG/タイムスライス | ❌ | ❌ | ⚠️ 限定 |
| gRPC+REST | ✅ デュアルプロトコル | ⚠️ REST中心 | ✅ | ✅ |
| モデルホットリロード | ✅ | ⚠️ | ✅ | ❌ |
| 本番対応度 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
結論は明確です:複数のモデルを同時にサーブし、動的バッチ処理が必要で、GPUの性能を最大限引き出したいなら——Tritonが唯一の全能選手です。
Tritonコアアーキテクチャ:理解すべき3つの柱
Tritonのアーキテクチャは3つのコア概念を中心に展開します。これらを理解することが第一歩です:
1. Model Repository(モデルリポジトリ)
モデルリポジトリはディレクトリ構造で、Tritonはこれを通じてモデルを発見・ロードします:
model_repository/
├── bert_ner/
│ ├── config.pbtxt # モデル設定
│ └── 1/
│ └── model.onnx # バージョン1のモデルファイル
├── resnet50/
│ ├── config.pbtxt
│ └── 1/
│ └── model.plan # TensorRT最適化済みエンジン
└── sentence_transformer/
├── config.pbtxt
└── 1/
└── model.py # Python Backendスクリプト
各モデルディレクトリにはconfig.pbtxt設定ファイルとバージョンサブディレクトリが必須です。これはTritonの厳格な要件です。
2. Backend(推論バックエンド)
Tritonは複数のバックエンドをサポートしています。2026年の主要なもの:
| バックエンド | 適用場面 | パフォーマンス |
|---|---|---|
| TensorRT | GPU推論、極限最適化 | 🚀🚀🚀🚀🚀 |
| ONNX Runtime | GPU/CPU汎用 | 🚀🚀🚀🚀 |
| PyTorch (LibTorch) | 高速イテレーション、デバッグ容易 | 🚀🚀🚀 |
| Python | カスタムロジック、前後処理 | 🚀🚀 |
| TensorFlow SavedModel | TFエコシステム | 🚀🚀🚀 |
実践的アドバイス: 訓練はPyTorch、デプロイはONNXに変換してからTensorRTに変換——これが2026年のゴールデンパスです。
3. Dynamic Batching(動的バッチ処理)
これはTritonの最も強力な機能の一つです。複数のリクエストが同時に到着した場合、Tritonは自動的にそれらを1つのバッチに結合してGPUに送り、推論完了後に結果を分割して返却します。これにより:
- 単一リクエストのレイテンシはほぼ増加しない
- スループットは5-20倍向上
- GPU利用率は30%から90%以上に跳ね上がる
設定例:
dynamic_batching {
preferred_batch_size: [4, 8, 16, 32]
max_queue_delay_microseconds: 5000
}
完全セットアップガイド:ゼロから本番まで
環境準備
# Tritonイメージをプル(2026年最新版)
docker pull nvcr.io/nvidia/tritonserver:24.01-py3
# Pythonクライアントをインストール
pip install tritonclient[all]
# モデルエクスポートツールをインストール
pip install onnx onnxruntime torch tensorflow
ONNX形式へのモデルエクスポート
import torch
import torch.onnx
from transformers import AutoModelForSequenceClassification, AutoTokenizer
model_name = "bert-base-uncased"
model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=10)
tokenizer = AutoTokenizer.from_pretrained(model_name)
model.eval()
dummy_input = tokenizer("This is a test sentence for export", return_tensors="pt")
input_ids = dummy_input["input_ids"]
attention_mask = dummy_input["attention_mask"]
torch.onnx.export(
model,
(input_ids, attention_mask),
"model_repository/bert_ner/1/model.onnx",
input_names=["input_ids", "attention_mask"],
output_names=["logits"],
dynamic_axes={
"input_ids": {0: "batch_size", 1: "seq_len"},
"attention_mask": {0: "batch_size", 1: "seq_len"},
"logits": {0: "batch_size"}
},
opset_version=17
)
print("ONNXモデルエクスポート完了")
モデル設定の作成
config_content = """name: "bert_ner"
backend: "onnxruntime"
max_batch_size: 32
input [
{
name: "input_ids"
data_type: TYPE_INT64
dims: [-1]
},
{
name: "attention_mask"
data_type: TYPE_INT64
dims: [-1]
}
]
output [
{
name: "logits"
data_type: TYPE_FP32
dims: [10]
}
]
dynamic_batching {
preferred_batch_size: [4, 8, 16, 32]
max_queue_delay_microseconds: 5000
}
instance_group [
{
count: 2
kind: KIND_GPU
gpus: [0]
}
]
optimization {
execution_accelerators {
gpu_execution_accelerator: [
{
name: "tensorrt"
parameters {
key: "max_workspace_size_bytes"
value: "1073741824"
}
}
]
}
}"""
with open("model_repository/bert_ner/config.pbtxt", "w") as f:
f.write(config_content)
Tritonサーバーの起動
docker run --gpus all --rm -p 8000:8000 -p 8001:8001 -p 8002:8002 \
-v $(pwd)/model_repository:/models \
nvcr.io/nvidia/tritonserver:24.01-py3 \
tritonserver --model-repository=/models
- ポート8000:HTTP REST API
- ポート8001:gRPC API
- ポート8002:Prometheusメトリクス
Pythonクライアント推論
import tritonclient.grpc as grpc_client
import numpy as np
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
triton_client = grpc_client.InferenceServerClient(url="localhost:8001")
text = "NVIDIA Triton is the best model serving solution in 2026"
inputs = tokenizer(text, return_tensors="np", padding="max_length", max_length=128, truncation=True)
input_ids = grpc_client.InferInput("input_ids", inputs["input_ids"].shape, "INT64")
input_ids.set_data_from_numpy(inputs["input_ids"])
attention_mask = grpc_client.InferInput("attention_mask", inputs["attention_mask"].shape, "INT64")
attention_mask.set_data_from_numpy(inputs["attention_mask"])
result = triton_client.infer(model_name="bert_ner", inputs=[input_ids, attention_mask])
logits = result.as_numpy("logits")
predicted_label = np.argmax(logits, axis=-1)
print(f"予測ラベル: {predicted_label}")
マルチモデルデプロイ実践
Tritonのキラー機能:単一インスタンスで複数モデルを同時サーブ、GPUリソースをインテリジェントにスケジューリング。
シナリオ:NLP + CV + 推薦モデルを同時デプロイ
import os
models_config = {
"bert_classifier": {
"backend": "onnxruntime",
"max_batch_size": 32,
"input_shapes": {"input_ids": [-1], "attention_mask": [-1]},
"output_shapes": {"logits": [10]},
"instance_count": 2,
"gpu": 0
},
"resnet50_detector": {
"backend": "tensorrt",
"max_batch_size": 64,
"input_shapes": {"images": [3, 224, 224]},
"output_shapes": {"predictions": [1000]},
"instance_count": 1,
"gpu": 0
},
"deepfm_recommender": {
"backend": "onnxruntime",
"max_batch_size": 128,
"input_shapes": {"user_features": [64], "item_features": [64]},
"output_shapes": {"score": [1]},
"instance_count": 2,
"gpu": 0
}
}
def generate_config(name, cfg):
inputs_str = ""
for iname, idims in cfg["input_shapes"].items():
dims_str = ", ".join(str(d) for d in idims)
inputs_str += f""" {{
name: "{iname}"
data_type: TYPE_FP32
dims: [{dims_str}]
}}\n"""
outputs_str = ""
for oname, odims in cfg["output_shapes"].items():
dims_str = ", ".join(str(d) for d in odims)
outputs_str += f""" {{
name: "{oname}"
data_type: TYPE_FP32
dims: [{dims_str}]
}}\n"""
config = f"""name: "{name}"
backend: "{cfg['backend']}"
max_batch_size: {cfg['max_batch_size']}
input [
{inputs_str}]
output [
{outputs_str}]
dynamic_batching {{
preferred_batch_size: [4, 8, 16, 32]
max_queue_delay_microseconds: 5000
}}
instance_group [
{{
count: {cfg['instance_count']}
kind: KIND_GPU
gpus: [{cfg['gpu']}]
}}
]"""
return config
for model_name, cfg in models_config.items():
model_dir = f"model_repository/{model_name}/1"
os.makedirs(model_dir, exist_ok=True)
config_path = f"model_repository/{model_name}/config.pbtxt"
with open(config_path, "w") as f:
f.write(generate_config(model_name, cfg))
print(f"✅ {model_name} 設定生成完了")
マルチモデルクライアントルーター
import tritonclient.grpc as grpc_client
from dataclasses import dataclass
from typing import Dict, Any
@dataclass
class TritonModelRouter:
url: str = "localhost:8001"
def __post_init__(self):
self.client = grpc_client.InferenceServerClient(url=self.url)
def infer(self, model_name: str, inputs: Dict[str, Any]) -> Dict[str, Any]:
triton_inputs = []
for name, (data, datatype) in inputs.items():
infer_input = grpc_client.InferInput(name, data.shape, datatype)
infer_input.set_data_from_numpy(data)
triton_inputs.append(infer_input)
result = self.client.infer(model_name=model_name, inputs=triton_inputs)
return {name: result.as_numpy(name) for name in result.get_output_names()}
def health_check(self) -> bool:
try:
return self.client.is_server_live()
except Exception:
return False
router = TritonModelRouter()
print(f"Tritonサーバー状態: {'正常' if router.health_check() else '異常'}")
よくある5つの落とし穴
落とし穴1:モデル設定のdims間違い
config.pbtxtのdimsはモデルの実際の入出力次元と厳密に一致させる必要があります。動的次元には-1を使用します。1つの数字が間違っているだけで、Tritonはモデルロード失敗を報告し、エラーメッセージが全く無関係な箇所を指すことがあります。
落とし穴2:動的バッチ処理パラメータの未チューニング
preferred_batch_sizeとmax_queue_delay_microsecondsは設定して終わりではありません。batch_sizeが大きすぎるとレイテンシが急増、小さすぎるとスループットが上がりません。delayが長すぎるとユーザーが待ちきれず、短すぎるとバッチが埋まりません。perf_analyzerでベンチマーク後にチューニングが必須です。
落とし穴3:Python BackendでのCPU集約的操作
Python Backendの実行はTritonのメインスレッドプールで行われます。重いCPU計算(複雑な後処理など)を行うと、他のリクエストがブロックされます。CPU集約的なロジックはC++バックエンドに移すか、後処理をクライアントにオフロードしてください。
落とし穴4:モデルバージョン管理の混乱
Tritonは複数バージョンのモデル共存をサポートしますが、古いバージョンをクリーンアップしないとディスクが溢れます。また、デフォルトのversion_policyは全バージョンをロードするため、メモリも溢れます。本番では必ずversion_policy: { specific: { versions: [1] } }を設定してください。
落とし穴5:gRPCとRESTの無計画な混用
gRPCのシリアライズオーバーヘッドはRESTよりはるかに小さく、特に大きなテンソル転送時に顕著です。内部サービス間の呼び出しにはgRPC、外部APIにはRESTを使用する——これが2026年の標準プラクティスです。
10のエラートラブルシューティング
| # | エラーメッセージ | 原因 | 解決策 |
|---|---|---|---|
| 1 | model not found |
モデルディレクトリ名とconfigのnameが不一致 | ディレクトリ名=設定nameを確認、大文字小文字区別あり |
| 2 | invalid model configuration |
config.pbtxtの構文エラー | --model-control-mode=explicitで個別ロードして調査 |
| 3 | CUDA out of memory |
GPU VRAM不足 | instance_countを削減、またはMIGパーティション有効化 |
| 4 | inference request timeout |
リクエストタイムアウト | --response-timeoutを増加、モデルがスタックしていないか確認 |
| 5 | unable to create backend |
バックエンドライブラリ不足 | Dockerイメージに該当バックエンドが含まれているか確認 |
| 6 | shape mismatch in input |
クライアント入力shapeがconfigと不一致 | 実際のshapeを出力してconfigのdimsと比較 |
| 7 | model version not available |
指定バージョンが存在しない | バージョンディレクトリの存在確認、version_policy確認 |
| 8 | shared memory registration failed |
共有メモリ領域が未作成 | create_shared_memory_regionを呼び出してから登録 |
| 9 | dynamic batching disabled |
configで未有効化 | config.pbtxtにdynamic_batching {}ブロックを追加 |
| 10 | TensorRT acceleration failed |
ONNX→TRT変換失敗 | opsetバージョンの互換性確認、opsetダウングレードまたはTRT無効化 |
パフォーマンス最適化実践
perf_analyzerによるベンチマーク
# パフォーマンス分析ツールをインストール
pip install tritonclient[all]
# ベースラインテスト
perf_analyzer -m bert_ner -b 1 --concurrency-range 1:32:1 \
--input-data zero --shape input_ids:128 --shape attention_mask:128
# 動的バッチ処理効果テスト
perf_analyzer -m bert_ner -b 32 --concurrency-range 1:64:4 \
--input-data zero --shape input_ids:128 --shape attention_mask:128
主要最適化手法
| 手法 | スループット向上 | レイテンシ影響 | 実装難易度 |
|---|---|---|---|
| ONNX→TensorRT変換 | 2-5x | 低減 | ⭐⭐ |
| 動的バッチ処理 | 5-20x | 微増 | ⭐ |
| マルチインスタンスデプロイ | 線形スケーリング | 低減 | ⭐ |
| 半精度(FP16)推論 | 1.5-2x | 低減 | ⭐⭐ |
| 共有メモリ転送 | 1.2-1.5x | 低減 | ⭐⭐⭐ |
| MIGマルチインスタンスGPU | インスタンス数に比例 | 微増 | ⭐⭐⭐⭐ |
| リクエストキュー優先度 | シナリオ依存 | P99低減 | ⭐⭐ |
大テンソル転送の共有メモリアクセラレーション
import tritonclient.grpc as grpc_client
import numpy as np
client = grpc_client.InferenceServerClient(url="localhost:8001")
shm_region_name = "infer_shm"
input_data = np.random.randn(32, 128).astype(np.int64)
handle = client.create_shared_memory_region(shm_region_name, input_data.nbytes, 0)
client.register_shared_memory(shm_region_name, 0, input_data.nbytes)
input_tensor = grpc_client.InferInput("input_ids", input_data.shape, "INT64")
input_tensor.set_data_from_numpy(input_data, shared_memory=handle)
result = client.infer(model_name="bert_ner", inputs=[input_tensor])
client.unregister_shared_memory(shm_region_name)
おすすめツール
モデルデプロイの過程で毎日使っているツール:
- JSONフォーマッター:Tritonのconfig.pbtxtはJSONではありませんが、モデルメタデータやモニタリングAPIのレスポンスはすべてJSON — フォーマッターで設定問題を素早く特定できます
- Base64エンコーダー:REST APIで画像/音声などのバイナリデータを転送する際、Base64エンコードは必須 — このツールでワンクリック対応
- ハッシュ計算:モデルファイルの整合性検証、キャッシュキー生成 — ハッシュツールはデプロイパイプラインの強い味方
まとめ: 2026年のAIモデル本番デプロイにおいて、NVIDIA Tritonはデファクトスタンダードです。マルチモデル同時実行、動的バッチ処理、マルチバックエンド対応の3つのコア機能は、他のサービングフレームワークでは同時に提供できません。ゴールデンパスを忘れずに:PyTorch訓練 → ONNXエクスポート → TensorRT高速化 → Tritonデプロイ。動的バッチ処理とパフォーマンスチューニングを組み合わせれば、推論スループットは10倍以上簡単に向上します。もうFlaskで推論エンドポイントを手書きするのはやめましょう — Tritonを採用してください。
ブラウザローカルツールを無料で試す →