推論コスト最適化プレイブック(画像解析・生成・OCR・検索)調達戦略 × 実装レシピ × 量子化・最適化 設定例

推論コスト最適化プレイブック

前提:オンプレ(例:RTX 4070 Ti SUPER ×1)またはクラウドの混在を想定。CUDA だけに縛られず、ROCm / OpenVINO / ONNX Runtime / TensorRT / Triton / vLLM 等を適材適所で使い分ける。ここにあるのは調査中内容のメモです。

0. まず決めること(3分チェックリスト)

ワークロードの型
  • 画像解析:分類 / 検出(YOLO/RT-DETR系)/ セグメンテーション
  • 生成:拡散(SD系)/ テキスト→画像 / 画像→画像
  • OCR:レイアウト解析 + 認識(PaddleOCR 等)
  • 検索:埋め込み生成 + ANN 検索 + (任意)再ランキング
SLO/制約
  • レイテンシ(p95 ms)・スループット(req/s)・QoS(Drop許容)
  • 精度しきい値(mAP / F1 / CER / nDCG)
  • コスト上限(円/1000 リクエスト or 円/時)
配備先
  • CUDA(NVIDIA)/ ROCm(AMD)/ OpenVINO(Intel CPU+iGPU)
  • クラウド専用 HW(AWS Inferentia2 / TPU v5e など)
モデル選定
  • 蒸留済み・軽量系(例:YOLOv8n/s, MobileSAM, Distil-Embedding)
  • 量子化前提(FP16/INT8/INT4)で再学習許容か

1. 調達戦略(ハード別・費用対効果の勘所)

区分適性ワークロード強み落とし穴ざっくり指針
NVIDIA(CUDA/TensorRT) 画像解析/生成, 大規模LLM推論, 混在 成熟ツール群(TensorRT, Triton, cuDNN)と高効率 FP16/INT8 装置単価・消費電力高め、対中輸出規制の影響 学習も視野なら第一候補。推論専用なら L4/L40S/RTX 4000 SFF も検討
AMD(ROCm) 推論全般(特に画像/検索/一部LLM) 価格性能比/入手性、ONNX/Triton/torch.compile も活用可 フレームワーク適合性差・一部モデルの最適化ノウハウが必要 推論コスト最小化の本命。クラウド/オンプレ混在でロックイン回避
Intel(OpenVINO / CPU+iGPU) OCR・検出・クラシカルCV・軽量LLM/Embedding CPUだけでINT8が速い。依存ライブラリ少なく運用が軽い 超大規模生成は不向き 省電力・現場Edge用に最適。既設サーバの延命に効く
専用HW(Inferentia2/TPU等) LLM/Embedding 大量バッチ / 検索 クラウドでのスループット単価が安い 学習/モデル互換や運用知見が必要 スパイク負荷や夜間バッチに限定して混在運用

意思決定の要点:推論は多様化・分散(マルチベンダ)が基本。学習はNVIDIA寄りでも、推論は AMD/Intel/専用HW を混ぜて 円/リクエスト を最小化。

2. 実装レシピ(フレームワーク別)

2.1 ONNX Runtime(全方位・まずはこれ)

EP(Execution Provider)を差し替えるだけで CUDA / ROCm / OpenVINO / TensorRT を切り替え可能。単一コードでマルチベンダ対応。

# インストール例(環境に合わせて選択)
# CUDA:
pip install onnxruntime-gpu  # バージョンはCUDA対応表を必ず確認
# ROCm:
pip install onnxruntime-rocm
# OpenVINO(=CPU/iGPU最適化):
pip install onnxruntime-openvino
import onnxruntime as ort

# EP優先順位を列挙(最初に使えるものが選ばれる)
providers = [
    ("TensorrtExecutionProvider", {"trt_fp16_enable": True, "trt_int8_enable": True}),
    "CUDAExecutionProvider",
    "ROCmExecutionProvider",
    ("OpenVINOExecutionProvider", {"device_type": "CPU_FP32"}),
    "CPUExecutionProvider"
]
so = ort.SessionOptions(); so.intra_op_num_threads = 0
sess = ort.InferenceSession("model.onnx", providers=providers, sess_options=so)

量子化(PTQ/QAT)

# 動的量子化(Linear INT8:主にTransformerのMatMul)
python -m onnxruntime.quantization.quantize_dynamic \
  --model_input model.onnx --model_output model.int8.onnx \
  --per_channel --optimize_model --activation_type qint8 --weight_type qint8

# 静的量子化(校正データで校正):画像/検出モデルに有効
python -m onnxruntime.quantization.quantize \
  --model_input model.onnx --model_output model.int8.onnx \
  --quant_format QDQ --calibrate \
  --data_folder ./calib --per_channel --activation_type qint8 --weight_type qint8

Tip:INT8化で精度が落ちたら、(1) activation だけ INT8、weight は FP16、(2) 事前の SmoothQuant 互換のスケーリング、(3) 校正データを“本番分布”に寄せる。

2.2 TensorRT(NVIDIA最速の定番)

# エンジン生成(FP16/INT8)
trtexec --onnx=model.onnx --saveEngine=model_fp16.trt \
        --fp16 --workspace=4096 --timingCacheFile=cache.txt

# INT8(要校正):
trtexec --onnx=model.onnx --saveEngine=model_int8.trt \
        --int8 --calib=calib.cache --workspace=4096 --timingCacheFile=cache.txt

配信は Triton Inference Server で。モデルリポジトリに置くだけで同時に ONNX/TensorRT/PyTorch を扱える。A/Bテストや動的バッチで 円/リクエスト を下げる。

2.3 ROCm / AMD(PyTorch + ONNX/Triton)

# PyTorch(ROCm) 例:インデックスURLは環境に合わせる
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/rocm6.0

# 実行時にターゲットGPUを制御
export HIP_VISIBLE_DEVICES=0
import torch
# 既存PyTorchモデルを ROCm で FP16 実行
model.half().to("cuda")  # AMDでも torch.cuda デバイス扱い
with torch.inference_mode():
    y = model(x.half().to("cuda"))

ONNXに落として onnxruntime-rocm / Triton(TensorRT ではなく ONNX Backend)で運用すると管理が楽。
JITや torch.compile()(inductor)で kernel fusion を狙う。

2.4 OpenVINO(Intel CPU/iGPU)

# FP16 変換(OVC)
python -m openvino.tools.ovc --input_model model.onnx --compress_to_fp16 \
  --output_dir out_ov_fp16

# PTQ(POT)設定例(YAML/JSON)
cat <<'JSON' > pot.json
{
  "model": {"model_name": "detector", "model": "out_ov_fp16/model.xml"},
  "engine": {"type": "simplified", "data_source": "./calib"},
  "compression": [{"algorithm": "DefaultQuantization", "preset": "performance"}]
}
JSON
pot -c pot.json -d out_ov_int8
from openvino.runtime import Core
ie = Core()
comp = ie.compile_model("out_ov_int8/detector.xml", device_name="CPU")

OpenVINO は CPU だけでも INT8 で健闘。OCR・小型検出・レイアウト解析の 円/処理 を劇的に下げやすい。

3. ワークロード別の最適化パターン

3.1 画像解析(検出/分類/セグメンテーション)

  • モデル選択:蒸留・小型(n/s/tiny系)。NMS/罰則の閾値は実データで再最適化。
  • 前処理:入力解像度を 動的 に下げる(スコア安定範囲で)。
  • バッチング:Tritonの動的バッチで p95 を崩さずスループット最大化。
  • 量子化:INT8(QDQ/PTQ)→落ちるクラスだけ蒸留付き QAT。
  • 後処理高速化:NMS を GPU/EP 実装に寄せる(ONNX opset or TensorRT plugin)。
# YOLO系のONNX最適化(不要ノード削減, NMS統合)
python tools/export_yolo_onnx.py --weights best.pt --opt --nms --img 960 960

3.2 生成(拡散:Stable Diffusion 系)

  • 解像度可変:既定 1024 を 768/896 に自動降格(文脈次第)
  • 重み蒸留:LoRA/行列分解で軽量化、VAE の INT8/FP16化 は体感差大
  • xFormers / FlashAttention 相当:メモリ帯域削減
  • バッチ生成:ガイダンス比とステップ数のクリッピング(品質-速度 Pareto)
pipe.enable_xformers_memory_efficient_attention()
pipe.vae.to(dtype=torch.float16)
pipe.unet.to(memory_format=torch.channels_last)
pipe = torch.compile(pipe)  # ROCm/CUDA いずれも有効なことが多い

3.3 OCR(レイアウト + 認識)

  • レイアウト分割:段落/表/図を先に切り分けて個別最適(小型検出+CRNN/ViT)
  • 解像度自動:細字/小文字は DPI を上げて局所再スキャン
  • OpenVINO INT8:CPUのみで可。Edge/サーバ混在に強い
  • 言語モード分離:英数字/日本語/中韓でモデル切替(集合知より速い)
# onnxruntime + OpenVINO EP で CPU INT8 を使う例
sess = ort.InferenceSession("ocr_int8.onnx", providers=["OpenVINOExecutionProvider"])

3.4 検索(埋め込み + ANN + 再ランキング)

  • 軽量埋め込み:次元数 768→384 へ蒸留。INT8/FP16 で充分。
  • ANN:HNSW or IVF+PQ(OPQ)でメモリ&ディスク削減。
  • 再ランク:Cross-Encoder は トップKのみ に限定(1〜5件)。
  • キャッシュ:頻出クエリ/ドキュメント埋め込みをメモリ/SSDキャッシュ。
# FAISS: IVF+PQ のインデックス例
import faiss
import numpy as np

D = 384; nlist = 4096; m = 64;  # 64x8bit = 512bit/ベクトル
quantizer = faiss.IndexFlatL2(D)
index = faiss.IndexIVFPQ(quantizer, D, nlist, m, 8)
index.train(train_vecs.astype('float32'))
index.add(base_vecs.astype('float32'))
D,I = index.search(query_vecs.astype('float32'), 20)

4. 量子化・蒸留の実戦レシピ

4.1 PTQ(後付け量子化)

  • 校正セットを 本番データ分布 からサンプリング(誤差は分布ズレが主因)
  • 精度悪化が大きい層は 混合精度(INT8⇔FP16)で救済

4.2 QAT(量子化対応学習)

# PyTorch:FakeQuant を組み込み再学習
from torch.ao.quantization import get_default_qat_qconfig, prepare_qat, convert
model.qconfig = get_default_qat_qconfig("fbgemm")
prepare_qat(model, inplace=True)
# ここで微調整学習 ...
model = convert(model.eval())

4.3 蒸留(教師=元モデル, 生徒=軽量)

# 典型ロス:α*SoftTarget(KL) + β*HardCE + γ*FeatureMatch
loss = alpha*kl(soft_student, soft_teacher) + beta*ce(y_s, y) + gamma*mse(f_s, f_t)

鍵は 評価の自動化(精度↔速度の Pareto 曲線を日次で更新)。精度が閾値を割らない最も安い構成を常に選ぶ。

5. 配信・オーケストレーション

5.1 Triton Inference Server

  • ONNX/TensorRT/PyTorch/Python backend を単一エンドポイントで束ねる
  • 動的バッチ / 同時実行 / モデル間 Ensemble / A/B テスト
# model_repository/layout(例)
repo/
  detector/
    1/model.onnx
    config.pbtxt  # max_batch_size, dynamic_batching, instance_group
  ocr/
    1/model.xml  # OpenVINO
    config.pbtxt

5.2 vLLM / Text Generation Inference(LLM/Reranker)

  • PagedAttention 等でスループット改善、Speculative Decoding 有効
  • Embedding は onnxruntime / openvino で別ラインに逃がす

5.3 監視・SLO

  • p50/p95 レイテンシ、GPU/CPU 利用率、バッチサイズ分布、エラー率
  • 品質監視:mAP/CER/nDCG をオンライン評価(小規模サンプルで)

6. コスト式と意思決定フロー

コスト/リクエスト ≒ (インスタンス時給 + 電力) / (有効スループット req/s) + ストレージ転送
有効スループット = f(動的バッチ, 解像度, 量子化, 前後処理の並列化)
  1. まず FP16 ベースライン(CUDA/ROCm/OV いずれか)を作る
  2. INT8 PTQ → 精度確認 → だめなら層単位で混合精度
  3. 前処理ダウンサンプル/動的バッチ/キャッシュ導入
  4. 必要なら QAT/蒸留で精度回復
  5. クラウド専用HWで夜間バッチを逃がす(スポット活用)

7. 具体的テンプレ(そのまま流用OK)

7.1 REST 推論(onnxruntime + 動的EP)

from fastapi import FastAPI
import onnxruntime as ort
import numpy as np

providers=["TensorrtExecutionProvider","CUDAExecutionProvider",
          "ROCmExecutionProvider","OpenVINOExecutionProvider",
          "CPUExecutionProvider"]
sess = ort.InferenceSession("model.onnx", providers=providers)

app = FastAPI()
@app.post("/infer")
def infer(x: list[float]):
    a = np.array(x, dtype=np.float32)[None, :]
    y = sess.run(None, {sess.get_inputs()[0].name: a})[0]
    return {"y": y.tolist()}

7.2 Triton(動的バッチ)

# config.pbtxt(抜粋)
name: "detector"
platform: "onnxruntime_onnx"
max_batch_size: 32
dynamic_batching { preferred_batch_size: [4, 8, 16] max_queue_delay_microseconds: 2000 }
instance_group [{ kind: KIND_GPU, count: 1 }]

7.3 OpenVINO INT8 OCR

python -m openvino.tools.ovc --input_model ocr.onnx --compress_to_fp16 -o ov_fp16
cat pot.json | jq .  # 上の例を参照
pot -c pot.json -d ov_int8

8. 品質を落とさずコストを落とす「小ワザ」集

  • 解像度アダプト:検出結果が安定する最小解像度をオンラインで探索・保存
  • 早期打ち切り:生成は ステップ数上限 を動的制御、LLM は speculative
  • キャッシュ:再入力の多い OCR ページや検索クエリを TTL キャッシュ
  • 分岐:軽量モデル→微妙なら重いモデルで再判定(Two-Stage)
  • スケール:開店・昼・夜でレプリカ数を自動切替(HPA/スケジューラ)

9. 既存資産への適用順序(最短2週間プラン)

  1. 現状の p95, req/s, mAP/CER を固定化計測(ベースライン)
  2. ONNX へ正規化 → onnxruntime で EP 可変に
  3. INT8 PTQ → 精度検証 →ダメ箇所のみ混合精度 / QAT
  4. Triton 導入(動的バッチ・A/B)→ コスト最小設定を採択
  5. 重いワークロードを夜間だけクラウド専用HWへ逃がす

よくある失敗:校正データがショボい(→INT8がボロボロ)。前処理・後処理がCPUに残留(→GPU遊ぶ)。p50だけ改善(→p95悪化で体感マイナス)。

10. 付録:用語ざっくり

  • EP(Execution Provider):ONNX Runtime における実行エンジンの切替プラグイン
  • PTQ/QAT:後付量子化 / 量子化対応学習
  • IVF+PQ/OPQ:ベクトル圧縮の定番。巨大コーパスの検索を安価に
  • Speculative Decoding:小モデルで先読みし大モデルで検証する高速化

最終更新: 2025-09-01 / 作成者: ビーナレッジデザイン

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA