Google Cloudでの非同期ML推論
目次
- System Design
- Computing Service on Google Cloud
- Async ML Inference System Design
Google CloudでML推論のコストが下がるアーキテクチャ考えてみた
System Design
システム設計をする上で考慮することの例
- 同期処理 or 非同期処理
- WebAPI, Batch, Streaming ...etc
DBやネットワーク、セキュリティなど考えることは他にもあるが、
この発表ではコンピューティングに絞って考える
同期処理と非同期処理
同期処理
- 処理結果が返ってくるまで次に進まない
- 逐次処理されるのでワークフローはシンプルになる
- 処理の待ち時間がボトルネックになることがある
非同期処理
- 処理結果を待たずに次に進み、結果は後で取得する
- 出力先が呼び出し元と別でも良い
同期処理と非同期処理
同期処理と非同期処理
PushとPull
Push
Server側で負荷調整がしにくい構成。負荷によってスケールアウトしても、間に合わず過負荷になり得る。Rate LimitやExponential Backoffを用いてリクエストを送りすぎないように工夫が求められる
Pull
Server側で負荷調整がしやすい構成。Serverが処理可能な状態の時にQueueにあるものから取り出して新しく処理を開始する
同期処理とPush型の問題点
同期処理もPush型も準備できたサーバーがある前提。リクエストに備えて常時立ち上げることもあり、その分費用がかかる
負荷によってスケールアウトし、新規にサーバーを起動することもあるが、プロビジョニングに時間がかかる。その間に既存のサーバーが過負荷になり不安定になる
特にGPUを使うようなコンテナイメージは数GB~あるのでスケールアウトに時間がかかる
同期処理とPush型の問題点
同期処理もPush型も準備できたサーバーがある前提。リクエストに備えて常時立ち上げることもあり、その分費用がかかる
負荷によってスケールアウトし、新規にサーバーを起動することもあるが、プロビジョニングに時間がかかる。その間に既存のサーバーが過負荷になり不安定になる
特にGPUを使うようなコンテナイメージは数GB~あるのでスケールアウトに時間がかかる
→非同期&PullのGPU推論アーキテクチャを考えてみた
Compute Service
Google Cloud で Computing 機能を有するサービス
- Google Compute Engine(GCE)
-
Google Kubernets Engine(GKE)
-
Google App Engine(GAE)
-
Vertex AI series
-
Cloud Functions
-
Cloud Run
-
Cloud Run for Anthos
-
Cloud Run Jobs
-
Batch
-
Dataflow
Service | Preemptible | Auto Scale | Batch | Streaming | WebAPI | GPU |
---|---|---|---|---|---|---|
GCE | ○ | ○ | ○ | ○ | ○ | ○ |
GKE | ○ | ○ | ○ | ○ | ○ | ○ |
Cloud Run | × | ○ | × | △ | ○ | △ |
Cloud Run Jobs | × | × | ○ | △ | × | × |
GAE | × | ○ | ○ | △ | ○ | × |
Batch | ○ | × | ○ | △ | × | ○ |
Dataflow | △ | ○ | ○ | ○ | × | ○ |
Vertex AI series | × | ○ | ○ | △ | ○ | ○ |
Cloud Functions | × | ○ | × | △ | ○ | × |
各サービスの星取表
(Anthos含む)
Preemptible Instance
Google Cloudの余っているリソースを割安で使うことのできるオプション。リソースが余ってない時は使えない。
プリエンプティブル VM インスタンスは、標準 VM の料金よりもはるかに低価格(60~91% 割引)で利用できます。ただし、他の VM に割り当てるためにコンピューティング容量を再利用する必要がある場合、Compute Engine はこのインスタンスを停止(プリエンプト)する可能性があります。プリエンプティブル インスタンスは、Compute Engine の余剰の容量を利用する機能であり、使用できるかどうかは利用状況に応じて変わります。
GPUとPreemptible Instance両方が使える次のサービスの活用を考えてみる
- GCE
- Batch
- Dataflow
GKEはGPUのshared resouseで費用抑えたりもできるが、今回は対象外
Instance template: マシンのスペックやOS、起動時のスクリプトやコンテナイメージなどをあらかじめ指定しているVM
GCE Managed Instance Group(MIG)
Instance templateを条件によってスケールさせられる。最小は0台
- CPU使用率、ロードバランサの状況、Cloud Monitoringの指標でスケール
- Pub/Subの未確認メッセージの数でもスケールできる
- schedulingもできる。平日昼間は多く立ち上げるみたいなことが可能
- カスタム指標でスケールさせることができる
- 複数の指標を組み合わせることもでき、その内の最大の数が採用される
- GitHub Actionsのself-hostedでも使える
Batch
- shell scriptやコンテナイメージを実行してバッチ処理を行う。マネージドなサービスでバッチ処理が終わったらインスタンスを終了してくれる。
- 複数台で実行できる
- GCSやFilestoreをマウントできる
- networkやGPUの設定をする時はコンソールからはできず、gcloud CLIやWeb API, SDKから実行する必要がある
- GPU環境の動作確認をしたい場合にCIに組み込むことができるかも。。
※WebUIから起動する時はdefaultという名前のVPCが必要
Dataflow
- Apache BeamをGoogle Cloudの環境で動かしたのがDataflow
- Beamはバッチ処理とストリーミング処理用のフレームワーク
- CPU負荷やPub/Subのメッセージの状況でAuto scaleする
- 実行グラフで時間がかかってる部分を特定できたり、実行・マシンごとのメトリクス集計、コスト計算機能がある
- Streaming Engineのmin workerは1。0に設定できない
- Preemptible Instanceはバッチ処理だけで利用できる
Dataflow MLでPyTorch, Tensorflow, ONNX, TensorRT, scikit-learnなどの推論をサポートしている。実行中にモデルの差し替えができる。高頻度でモデルが変わるシステムと相性がいい。
Dataflow MLを使った実験
CPU
-
resnet101
-
30分で完了、1.4$
-
-
mobilenet v2
-
22分で完了、0.5$
-
それぞれ50,000の画像を使って実験
GPU
-
resnet101:
-
60分で完了、0.5$
-
-
mobilenet v2:
-
60分で完了、0.05$
-
GPUを使った方が費用が安いが、時間がかかってる。GPUを使うコンテナイメージは大きいのでプロビジョニングに時間がかかる
Dataflow ML Example
def run(
argv=None,
model_class=None,
model_params=None,
save_main_session=True,
device='CPU',
test_pipeline=None) -> PipelineResult:
known_args, pipeline_args = parse_known_args(argv)
pipeline_options = PipelineOptions(pipeline_args)
pipeline_options.view_as(SetupOptions).save_main_session = save_main_session
if not model_class:
# default model class will be mobilenet with pretrained weights.
model_class = models.mobilenet_v2
model_params = {'num_classes': 1000}
def preprocess(image_name: str) -> Tuple[str, torch.Tensor]:
image_name, image = read_image(
image_file_name=image_name,
path_to_dir=known_args.images_dir)
return (image_name, preprocess_image(image))
def postprocess(element: Tuple[str, PredictionResult]) -> str:
filename, prediction_result = element
prediction = torch.argmax(prediction_result.inference, dim=0)
return filename + ',' + str(prediction.item())
# In this example we pass keyed inputs to RunInference transform.
# Therefore, we use KeyedModelHandler wrapper over PytorchModelHandler.
model_handler = KeyedModelHandler(
PytorchModelHandlerTensor(
state_dict_path=known_args.model_state_dict_path,
model_class=model_class,
model_params=model_params,
device=device,
min_batch_size=10,
max_batch_size=100)).with_preprocess_fn(
preprocess).with_postprocess_fn(postprocess)
pipeline = test_pipeline
if not test_pipeline:
pipeline = beam.Pipeline(options=pipeline_options)
filename_value_pair = (
pipeline
| 'ReadImageNames' >> beam.io.ReadFromText(known_args.input)
| 'FilterEmptyLines' >> beam.ParDo(filter_empty_lines))
predictions = (
filename_value_pair
| 'PyTorchRunInference' >> RunInference(model_handler))
predictions | "WriteOutputToGCS" >> beam.io.WriteToText( # pylint: disable=expression-not-assigned
known_args.output,
shard_name_template='',
append_trailing_newlines=True)
result = pipeline.run()
result.wait_until_finish()
return result
まとめ
- リアルタイムの処理が求められてないならPreemptible Instance使って費用を1/4くらいにできそう
- 費用を抑えて、Auto scaleする構成ならmin 0に設定できるGCE MIGを使うのが良さそう
- Pub/Subの未確認メッセージがうまく処理できてないなら一時的にBatch起動してブーストすることもできそう
- Pub/Subからデータ受け取る構成ならMIG, Batch, Dataflowの間で切り替える際の外部の変更を最小限でできそう
- Preemptible Instance使うならリソースが枯渇した時のことも考える必要がありそう
DataflowのStreamingでPreemptible Instance使えると嬉しい
async inference system on Google Cloud
By snkg2450
async inference system on Google Cloud
- 136