業務システムにAIを組み込むと「時々遅い」「ピーク時に不安定」といった声を聞きがちです。目的は単に速くすることではなく、業務要件を満たしつつ安定して運用することです。本記事では、現場で実際に使える手順とチェックリストを、Pythonの実装イメージも交えて整理します。まずは現状のつまずきに寄り添い、最小の変更で効果が出る順に進めます。
導入と目的 — 要件を明確にする
最初にやるべきはターゲット(目標)を数値化することです。曖昧な「速く」では対策がぶれます。以下は実務で使える要件定義の例です。
| 項目 | 例 | 理由/注 |
|---|---|---|
| ターゲットレイテンシ(P95) | 300ms | ユーザ操作で許容できる遅延 |
| スループット(同時リクエスト) | 200 req/s | 業務ピーク想定 |
| SLO | P95 < 300ms、エラー率 < 1% | 運用監視のしきい値 |
| コスト上限 | 月額予算の上限 | スケール時の現実的制約 |
現状計測で揃えるべきメトリクス:
- P50 / P95 / P99 レイテンシ
- CPU / GPU 使用率、メモリ使用量
- キュー長(リクエスト待ち数)と待ち時間
- レスポンスの構成要素(前処理/モデル推論/後処理 毎の時間)
計測とベンチマーク設計
本番に近い負荷を再現することが重要です。単純なシングルリクエストだけで判断すると誤った最適化をすることになります。
負荷試験を作るときのポイント:
- 代表入力を作る(分布を保つ、長さやトークン数の偏りに注意)
- キャッシュのウォームアップを考慮する(初回と平常時で分けて測る)
- シード固定で再現性を担保する
| 測定種別 | 目的 | 例(Pythonでの負荷試験イメージ) |
|---|---|---|
| エンドツーエンド | クライアントからレスポンスまでの総合評価 | ループでHTTP並列リクエスト(asyncioで並列化)を作り、P95を測定 |
| モデル内計測 | 前処理・推論・後処理の分解 | 各処理前後でtimeを取り、平均/分位点を比較 |
ボトルネック特定の実践手順
まずはリクエストのライフサイクルを分解して、どの段階が遅いかを特定します。以下は典型的な分解例です。
- 受信 → ルーティング → 前処理 → モデルロード/推論 → 後処理 → 応答送信
| ツール/手法 | 使いどころ | 備考 |
|---|---|---|
| timeベース計測(simple) | 処理分解の最初の一歩 | 軽量ですぐ導入可能。async時はイベントループの計測に注意 |
| cProfile / pyinstrument | Pythonコード内のCPUホットスポット | I/O待ちや外部呼び出しは別途計測 |
| psutil | プロセスのCPU/メモリ状況 | リソース逼迫がないか確認 |
| torch.profiler / TensorBoard | PyTorchモデル内の演算詳細 | GPUオペレーションのボトルネック特定に有効 |
よくある切り分けの流れ:
- 前処理で時間がかかる → 入力変換を軽くする、バッチ化を検討
- GPUがアイドル → バッチ化不足またはデータ転送がボトルネック
- CPUが高負荷でスロットリング → 並列数を見直すか、前処理を別プロセスへ移行
実行時最適化の策略(Python実装イメージ)
対策は「効果が大きく、実装負担が小さい」順に試すのが効率的です。ここでは主要な手法と注意点を示します。
リクエストバッチ化
| 狙い | 効果 | 実装ヒント |
|---|---|---|
| GPUのスループット向上 | 小さな入力をまとめて処理することで単体コスト低下 | 短い遅延を許容できる場合は一定時間ごとにキューを集めてバッチ化(例: 20msごとに集める) |
非同期処理(async)とFastAPIの例
I/O待ちを減らすことでCPU資源を有効活用できます。非同期で外部APIやDBを待つ間に別の処理を回します。
| 施策 | 注意点 |
|---|---|
| async/await の導入 | CPUバウンド処理は別スレッド/プロセス化が必要 |
| FastAPIで非同期エンドポイント | 内部でモデル推論は同期で行い、I/O部分だけ非同期にする運用も選択肢 |
モデル軽量化(量子化・ONNX変換)
量子化やONNXへのエクスポートは推論速度やメモリを改善しますが、精度影響と互換性に注意が必要です。
| 手法 | 利点 | 注意点 |
|---|---|---|
| 動的/静的量子化 | モデルサイズ縮小、CPU推論高速化 | 精度低下の可能性、検証必須 |
| ONNX変換+ONNX Runtime | クロスプラットフォームで高速化可能 | 一部の演算が非互換になる場合あり |
推論用キャッシュ
頻出の入力に対しては結果をキャッシュすると大きな効果があります。設計は慎重に。
| 設計要素 | 推奨 | 落とし穴 |
|---|---|---|
| キャッシュキー | 入力の安定したハッシュ(正規化後) | 代表入力が偏ると過補正や誤結果を返す恐れ |
| TTL(有効期限) | 業務要件に応じ短めから試す(例: 5分) | 変更頻度の高い入力はTTL短め |
運用面の対策
単発の改善だけでなく、運用で安定させる仕組みが重要です。
| 対策 | 具体例 | 実務のポイント |
|---|---|---|
| 自動スケーリング | CPU/GPU使用率やP95でスケール | スケール遅延を考慮してヒストリカルメトリクスで判断 |
| 冷スタート対策 | 定期的なウォームアップジョブ | 無駄なコストにならないよう間隔を最適化 |
| バックプレッシャー制御 | キュー長閾値で受け入れ制限 | ユーザ向けに待ち時間のメッセージを用意 |
フォールバックと安全な劣化戦略
重い推論時や障害発生時に備えた安全弁を設計します。ユーザー体験を大きく損なわない形が望ましいです。
| フォールバック種類 | トリガー | ユーザー向け挙動例 |
|---|---|---|
| 簡易モデル(小型モデル) | レイテンシ閾値超過 | 若干精度の落ちる回答だが即時応答 |
| ルールベース応答 | モデルが失敗/例外発生 | 定型文で最低限の案内を行う |
| キャッシュ応答 | 同入力の再要求時 | 即座に前回応答を返す |
フェイルオーバーの条件は明文化し、監視アラート(例: P95 > 閾値が5分継続)を設定します。UIでは「現在応答が遅くなっています。簡易応答で続けますか?」と選択肢を出すと親切です。
テスト・リリース手順とチェックリスト
運用に移す前に段階的にテストします。以下は実務向けのチェックリストです。
| 項目 | 合格基準 |
|---|---|
| ステージング負荷試験 | P95が本番ターゲットの120%以内 |
| カナリアリリース | 段階的に5%→20%→100%へ拡大。各段階でSLOを監視 |
| CIでの回帰テスト | 軽負荷の推論テストを自動化し、推論結果(重要な出力)とレイテンシをチェック |
| 手順書 | ロールバック手順・緊急連絡先を明記 |
実務でよくある落とし穴と運用のコツ
- 代表入力が偏る:ベンチマーク用データセットは本番ログからサンプリングして作る
- ベンチを最適化しすぎる:ベンチ向けの過学習を避ける(多様な入力で検証)
- キャッシュキーの誤設計:細かい差分でヒットしなくなることがあるため正規化を用意
| 失敗例 | 原因 | 回避策 |
|---|---|---|
| バッチ化で応答が遅延しすぎた | バッチ時間が長過ぎ | レイテンシSLOと折り合いをつけ、最大待ち時間で切る |
| 量子化で精度が落ちた | 検証不足 | 影響検証を自動化し、重要指標で差分を確認 |
まとめ
- まずは要件(P95, スループット, SLO)を数値化する。
- エンドツーエンドとモデル内計測を分けて測る。代表入力とウォームアップを忘れない。
- 効果が大きく実装が容易な対策(キャッシュ、バッチ化、非同期)から優先的に試す。
- 量子化やONNX変換は有効だが精度影響を必ず検証する。
- 運用面(スケーリング、冷スタート、フォールバック)を設計して初めて安定運用が可能になる。
この記事はシリーズ「AIとPythonの実務」の一部です。次回はログ設計とSLO連携のテンプレートを配布する予定です。実践で使える手順を小さく回して改善を積み上げてください。