はじめに
こんにちは、技術本部 研究開発部Automationグループの石井です。
本記事では、内製のVision Language Model “Viola”をGoogle CloudのVertex AIにデプロイするまでに行った意思決定を振り返ります。
比較的大きなVision Language Modelを本番環境にデプロイするにあたって、考慮すべき観点がいくつかありました。特に、システム要件を満たすための構成設計、画像処理の責務設計について、どのような判断を行ったのかを共有します。
目次
- はじめに
- 目次
- Violaとは
- 要件
- アーキテクチャ概要(予定)
- アーキテクチャ選定
- Vertex AIの検証
- 制約を解決するアーキテクチャ
- 負荷試験・リリース
- 残課題
- 終わりに
- Sansan技術本部ではカジュアル面談を実施しています
Violaとは
Violaは文書画像からの情報抽出に特化した内製のVision Language Modelです。
下記の記事にてより詳細に紹介しておりますので、興味がある方はぜひご覧ください。
要件
プロジェクトの目的は、複数の画像を入力として受け取り、それらを3つのVision Language Modelで解析し、リアルタイムAPIとして結果を提供することでした。
この目的を達成するにあたり、以下の制約がありました。
- 同時実行数100でリクエストを送り続けるシナリオで、すべてのリクエストをエラーなく1件あたり4分以内に処理できる。
- 処理の対象には複数の高解像度画像(数MB)が含まれており、これを適切に処理して結果を提供する。
- Google Cloudの既存サービスから呼ばれるため、基本的に、本システムもGoogle Cloudの東京リージョンに構築する。
- 解析対象の画像はCloud Storageに格納されている。
- 学習済みのVision Language Modelと推論用APIサーバーの実装は既に存在しており、流用することは可能である。
- CPUによる推論では性能要件を満たさないことが判明していたため、GPUによる推論が必須である。
アーキテクチャ概要(予定)
3つのVision Language Modelを呼び出し、結果をまとめて返すことが要求されるため、当初は下記のアーキテクチャを想定していました。

処理フローは下記の通りです。
- プロキシーAPIは、システムからリクエストを受けとります。
- プロキシーAPIは、リクエストをもとに解析対象の画像をCloud Storageからダウンロードします。
- プロキシーAPIは画像をbase64エンコードしてペイロードに含め、3つの機械学習モデルの推論API(以下、ML API)を呼び出します。
- ML APIは前処理と解析を行い、プロキシーAPIに解析結果を返します。
- プロキシーAPIは結果を統合してシステムに返します。
シーケンス図にすると、下記の通りです。

ここでML APIをどのサービス上で動作させるかを決める必要がありました。
私はGoogle Cloud上でGPUを使って機械学習モデルをデプロイする方法に詳しくなかったため、アーキテクチャを机上で検討することから開始しました。
アーキテクチャ選定
Google Cloud上でGPUを利用して機械学習モデルをデプロイする方法として、4つの選択肢を検討しました。
Compute Engine
Compute EngineインスタンスでAPIを起動させる方法です。
完全な制御が可能で柔軟性が高い一方、設定量が多く運用負荷が高いため、最終手段として位置付けました。
Cloud Run(GPU対応版)
Docker Imageを作り、Cloud RunでAPIを起動させる方法です。
既存のCPUで動作する機械学習APIの殆どをCloud Runで動作させている実績から安心感がありましたが、検討時点では東京リージョンでは利用できなかったため見送りました。
Google Kubernetes Engine (GKE)
Kubernetes上にリソースを定義してAPIを起動させる方法です。
Kubernetes自体の運用の複雑性に加え、当時時点でGKEでGPUを利用した実績もなかったため、実装コストとリスクが高いと判断して見送りました。
Vertex AI
Vertex AIのオンライン推論エンドポイント上にAPIを起動させる方法です。
Compute Engineと比較してインフラ管理の手間が少なく済むと考えられたため、第一候補として採用しました。カスタムコンテナをサポートしていることから既存の実装資産を活用できるため、特殊な実装が必要になるリスクも低いと判断しました。
まとめ
机上での簡易な比較ではありますが、上述の通りVertex AIを第一候補として選定して検証を進め、難しければCompute Engineを採用する方針としました。
Vertex AIの検証
オンライン推論APIの構築
実現可能性を検証するためにVertex AIを用いたオンライン推論APIを実際に構築しました。
Vertex AIの推論に関するドキュメントを見ると、以下3つの方法で予測を取得できることがわかります。(注2025年9月5日時点での情報です。)
- カスタムトレーニング済みモデルからの予測を取得する
- AutoMLモデルから予測を取得する
- BigQuery MLモデルから予測を取得する
今回デプロイする対象のモデルは既に別の方法で学習済みだったため、「1. カスタムトレーニング済みモデルからの予測を取得する」方法を採用しました。
予測を取得するには、まずモデルをインポートする必要があります。
このとき、ビルド済みコンテナまたはカスタムコンテナをインポートできますが、今回は既に別のマネージドサービスでもデプロイした実績のあるAPIのDocker Image(Dockerfile)を流用するため、カスタムコンテナをインポートします。
以下のコードのように、Docker ImageをビルドしてArtifact Registryにプッシュし、コンテナをVertex AIのModelとしてアップロードします。
gcloud builds submit . \
--project $PROJECT \
--config .cloudbuild/cloudbuild.yaml
gcloud ai models upload \
--project $PROJECT \
--region=$REGION \
--display-name=$DISPLAY_NAME \
--container-image-uri=$CONTAINER_IMAGE_URI \
--container-health-route=$CONTAINER_HEALTH_ROUTE \
--container-predict-route=$CONTAINER_PREDICT_ROUTE \
--flags-file=$FLAGS_FILE
なお、カスタムコンテナをデプロイする場合、いくつか満たさなければならない要件があります。適宜ドキュメントをご確認ください。
続いて、下記コードのように、エンドポイントを作成してモデルをデプロイします。
gcloud ai endpoints create \
--region=$REGION \
--display-name=$DISPLAY_NAME
gcloud ai endpoints deploy-model \
$ENDPOINT_ID \
--project=$PROJECT_ID \
--region=$REGION \
--model=$MODEL_ID \
--traffic-split=0=100 \
--flags-file=$FLAGS_FILE
これでデプロイは完了です。こちらに関しても検討の上設定すべき項目がいくつかあります。適宜ドキュメントをご確認ください。
モデルをデプロイした後は、これもドキュメントを参考にリクエストを作り、予測を取得します。
予測を取得するために、端的には以下の2つ方法が提供されています。
- predictメソッドを利用する。
- raw_predictメソッドを利用する。
predictメソッドは、ドキュメントに記載されているガイドラインに従う場合に利用し、任意のHTTPペイロードを使用する場合はraw_predictメソッドを利用するよう案内があります。
今回はカスタムコンテナかつガイドラインに従わないインターフェースを採用しているため、raw_predictメソッドを利用することにしました。
これで既存APIのDocker ImageをVertex AIにデプロイして予測を取得できるようになりました。
直面した制約
上述の実装を進める中で、パブリックエンドポイントには以下の制約があることに気付きました。
- タイムアウトが60秒である
- ペイロードサイズの上限が1MBである
(2025年3月頃の情報です。現在は異なっている可能性があります。)
タイムアウトに関しては、要件の4分を下回っています。
本来は4分の猶予があるものの60秒でエラーを返すことになるため、可能であればタイムアウトは伸ばしたいです。
ペイロードサイズの上限が1MBである点はより致命的でした。
サービスの都合上、複数枚の高解像度な画像を解析する必要がありましたが、これらをそのままbase64エンコードしてペイロードに含めると、殆どの場合1MBを超えてしまうためです。
制約を解決するアーキテクチャ
Private Service Connect (PSC) の導入
タイムアウトが60秒に制限されていることをGoogle Cloudのカスタマーサポートに相談したところ、Private Service Connectエンドポイントを紹介していただきました。 Private Service Connectエンドポイントは、タイムアウトが1時間であるとドキュメントに記載があります。(注2025年9月5日時点での情報です。)
実際にPrivate Service Connectエンドポイントを構築して検証したところ、タイムアウトまでの時間に十分な余裕があることを確認でき、タイムアウトの問題を解決できました。
Google Cloudのカスタマーサポートの皆様にはこの場を借りて御礼申し上げます。ありがとうございました。
プロキシーAPIによる画像前処理の導入
PSCの導入によって、ペイロードサイズの上限も引き上がりました。ドキュメントには、ペイロードサイズの上限は10MBであると記載があります。(注2025年9月5日時点での情報です。)
しかし、ペイロードの一部は10MBを超える場合もあります。また、定常的に10MB程度のペイロードを含む通信を行うことは、レイテンシや通信コストに関する懸念があります。
このことから、根本的にペイロードサイズを小さくする方法を検討する必要がありました。
これに対し、下記2つの案を検討しました。
- ML APIがBlob Storageから画像をダウンロードする。
- プロキシーAPIが画像を前処理してペイロードに含める。
それぞれの案について検討を深めていきます。
案1: ML APIがBlob Storageから画像をダウンロードする
ML APIにCloud Storageから画像をダウンロードする機能を追加し、ペイロードには画像IDだけを付与する案です。Cloud Storageからのダウンロードにペイロードサイズの制限はないため、すべてのリクエストを処理できる見込みです。
下記のようなアーキテクチャになります。


プロキシーAPIからML APIへのペイロードサイズは非常に小さくなるものの、それぞれのML APIが同じ画像をダウンロードすることになるため、やや無駄があります。
また、ML APIがストレージアクセスの責務を持つことになり、責務の肥大化が懸念されます。
大きな肥大化ではないので一般的には許容できるトレードオフとも考えられますが、当社はこのML APIを別のドメインでもモデルだけを差し替えたDocker Imageとして利用しています。この事情もあり、複数ドメインに共通となるコードベースにドメインの責務を滲ませたくないと考えました。
コードベースを複製・拡張してAPIの実装を作り直す事も考えられますが、実装コスト・実装を維持するコストを考慮するとやや採用しづらいと考えました。
案2: プロキシーAPIが画像を前処理してペイロードに含める
プロキシーAPI上で画像の前処理を行い、前処理済みの画像をML APIへのペイロードに含める案です。ML APIが要求するサイズ・チャネルに変換された画像は確実に10MB 以下になるため、すべてのリクエストを処理できる見込みです。
当初の想定と変わりないですが、下記のようなアーキテクチャになります。

シーケンス図は下記の通りです。

画像のダウンロードはプロキシーAPIで1度実行すれば良いものの、単にIDを渡す場合と比べるとペイロードサイズは大きくなります。
画像を扱う機械学習モデルにとって画像の前処理をどこで行うかは重要な観点です。
画像処理はリサイズやグレースケール化のような一般的な処理でも様々なアルゴリズムが存在し、利用するライブラリや言語によって変換の結果が微妙に異なる可能性があります。画像処理の結果が学習時・評価時と推論時でズレていると、本番環境で意図しない精度劣化が発生するリスクがあるためです。
ただし、誤った前処理による精度劣化は、ML APIの中身を正確に把握していない場合に大きくなると考えられます。言い換えると、ML APIの中身を正確に把握している実装者が適切に前処理を実装できる場合はリスクを抑えることができると考えられます。
まとめ
上述の2案で検討した観点をそれぞれまとめると下記の通りです。
| 観点 | 案1 | 案2 |
|---|---|---|
| ML API外での画像処理 | なし | あり |
| 画像のダウンロード回数 | 3回 | 1回 |
| ペイロードサイズ | 1KB 以下 | 1MB 以下 |
| ML APIコードの変更有無 | あり | なし |
今回は案2を採用しました。
ML API外での画像処理が介在することが懸念ですが、このプロジェクトでは、プロキシーAPIとML APIをあわせて実装します。プロキシーAPIの前処理を適切に実装することで、精度劣化のリスクを抑えることができると考えました。
また、ペイロードサイズはやや大きいですが許容できる範囲であると考え、ダウンロード回数を抑えることができること・ML APIの実装を流用できることのメリットを優先しました。
なお、プロキシーAPIの実装に際しては下記の対応を行いました。これにより、精度の劣化が発生するリスクを一定低減できていると考えています。
- モデルの作成・精度の評価に利用していた環境・コードベースで得られる出力とML APIから得られる出力に差異がないことを一定量のサンプルで確認した。
- バージョン更新による意図しないアルゴリズム変更が発生しないようML APIに入れる画像処理ライブラリのバージョンを固定した。
- リサイズメソッドの引数(特にアルゴリズムを指定する引数)を明示して固定した。
負荷試験・リリース
上述の構成で負荷試験を行いました。Cloud Runで起動するAPIサーバーのワーカー数、Cloud Runのconcurrency、Vertex AIエンドポイントのminReplica、maxReplica、machine-typeを調整し、スパイクテストを通過できる構成の中でコストパフォーマンスの良い構成を選択しました。
負荷試験の通過を確認し、要件を満たすことを確認できたため、2025年5月頃リリースに至りました。このシステムは現在も稼働を続けており、ビジネス価値を継続的に提供できています。
残課題
リリースしてからしばらく期間が経過したこともありいくつかの課題が見えてきています。いくつかを紹介します。
- PSCエンドポイントへのアクセスに手間がかかる
- デプロイを解除するとサービスアタッチメントが削除される
- GPUインスタンスを確保できないことがある
PSCエンドポイントへのアクセスに手間がかかる
当然ですが、PSCを利用するということは、閉域内にVertex AIエンドポイントを立てることになります。構成次第かとは思いますが、汎用的に利用するサーバーから直接アクセスできない場合が多いため、疎通のできる踏み台サーバーを用意して通信を経由する等の手順を踏む必要があります。
デプロイを解除するとサービスアタッチメントが削除される
GPUインスタンスは比較的高価であるため、ステージング環境や開発環境では、リソースを利用しないタイミングではエンドポイントからモデルのデプロイを解除する運用としています。
しかし、全てのモデルのデプロイが解除されてから10分が経過するとVertex AIエンドポイントのサービスアタッチメントは削除される仕様になっています。
このため、新たにモデルをデプロイして検証を行うためには、モデルのデプロイ後に作成されるサービスアタッチメントURIを取得し、改めて転送ルールを作り直す必要があります。
GPUインスタンスを確保できないことがある
Vertex AIとは直接関係ない事情かもしれませんが、GPUインスタンスは確保できないことがあります。インスタンスを確保できないとスケールアウトに失敗するため、これに備えて何らかの対策を行う必要があります。
終わりに
内製のVision Language Model “Viola”を本番環境にデプロイするまでに様々な課題に直面し、どのような検討を行い、意思決定してきたかを紹介しました。すべての課題を解決できるアーキテクチャではなく残課題も残っていますが、より良い設計を目指し、引き続き改善を続ける予定です。
規模の大きいVision Language Modelを本番環境にリリースするにあたっては、検討が必要な事項は多岐に渡ります。本記事の意思決定が、同様の課題に取り組んでいる方々の参考になれば幸いです。
Sansan技術本部ではカジュアル面談を実施しています
Sansan技術本部では中途の方向けにカジュアル面談を実施しています。Sansan技術本部での働き方、仕事の魅力について、現役エンジニアの視点からお話しします。「実際に働く人の話を直接聞きたい」「どんな人が働いているのかを事前に知っておきたい」とお考えの方は、ぜひエントリーをご検討ください。