このドキュメントでは、スケーラブルな TensorFlow 推論システムで説明されているリファレンス アーキテクチャをデプロイする方法について説明します。
このシリーズは、Google Kubernetes Engine と、TensorFlow、NVIDIA TensorRT などの機械学習(ML)フレームワークに精通しているデベロッパーを対象としています。
デプロイが完了したら、TensorFlow 推論システムのパフォーマンスの測定と調整をご覧ください。
アーキテクチャ
次の図は、推論システムのアーキテクチャを示しています。
Cloud Load Balancing は、最も近い GKE クラスタにリクエスト トラフィックを送信します。クラスタにはノードごとに Pod が 1 つ含まれます。各 Pod で Triton Inference Server が推論サービス(ResNet-50 モデル)を提供し、NVIDIA T4 GPU によりパフォーマンスを向上させています。クラスタ上のモニタリング サーバーが GPU 使用率とメモリ使用量の指標データを収集します。
詳細については、スケーラブルな TensorFlow 推論システムをご覧ください。
目標
- トレーニング済みの ResNet-50 モデルをダウンロードし、TensorRT(TF-TRT)との TensorFlow インテグレーションを使用して最適化を適用する
- NVIDIA Triton Inference Server から ResNet-50 モデルを提供する
- Prometheus と Grafana を使用して Triton 用のモニタリング システムをビルドする
- Locust を使用して負荷テストツールをビルドする
費用
このデプロイでは、NVIDIA T4 GPU に加えて、Google Cloud の次の課金対象のコンポーネントを使用します。
料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを生成できます。
デプロイの完了後、作成したリソースを削除しないでください。これらのリソースは、デプロイを測定して調整する際に必要になります。
始める前に
- Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
-
Make sure that billing is enabled for your Google Cloud project.
-
Enable the GKE API.
-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
-
Make sure that billing is enabled for your Google Cloud project.
-
Enable the GKE API.
TF-TRT で最適化されたモデルを構築する
このセクションでは、作業環境を作成し、トレーニング済みモデルを最適化します。
トレーニング済みモデルは、gs://cloud-tpu-test-datasets/fake_imagenet/
にある架空のデータセットを使用します。トレーニング済みモデルのコピーも Cloud Storage のロケーション gs://solutions-public-assets/tftrt-tutorial/resnet/export/1584366419/
にあります。
作業環境を作成する
作業環境として、Deep Learning VM Image を使用して Compute Engine インスタンスを作成します。このインスタンスで、TensorRT を使用して ResNet-50 モデルの最適化と量子化を行います。
In the Google Cloud console, activate Cloud Shell.
working-vm
という名前のインスタンスをデプロイします。gcloud config set project PROJECT_ID gcloud config set compute/zone us-west1-b gcloud compute instances create working-vm \ --scopes cloud-platform \ --image-family common-cu113 \ --image-project deeplearning-platform-release \ --machine-type n1-standard-8 \ --min-cpu-platform="Intel Skylake" \ --accelerator=type=nvidia-tesla-t4,count=1 \ --boot-disk-size=200GB \ --maintenance-policy=TERMINATE \ --metadata="install-nvidia-driver=True"
PROJECT_ID
は、前の手順で作成した Google Cloud プロジェクトの ID に置き換えます。このコマンドは、NVIDIA T4 を使用して Compute Engine インスタンスを起動します。初回起動時に、TensorRT 5.1.5 と互換性のある NVIDIA GPU ドライバが自動的にインストールされます。
さまざまな最適化でモデルファイルを作成する
このセクションでは、TF-TRT を使用して、元の ResNet-50 モデルに次の最適化を行います。
- グラフの最適化
- グラフの最適化による FP16 への変換
- INT8 による量子化とグラフの最適化
これらの最適化の詳細については、パフォーマンスの最適化をご覧ください。
Google Cloud コンソールで、[Compute Engine] > [VM インスタンス] に移動します。
前に作成した
working-vm
インスタンスが表示されます。インスタンスのターミナル コンソールを開くには、[SSH] をクリックします。
このドキュメントの残りのコマンドを実行するには、このターミナルを使用します。
ターミナルで、必要なリポジトリのクローンを作成し、現在のディレクトリを変更します。
cd $HOME git clone https://github.com/GoogleCloudPlatform/gke-tensorflow-inference-system-tutorial cd gke-tensorflow-inference-system-tutorial/server
トレーニング済みの ResNet-50 モデルをローカル ディレクトリにダウンロードします。
mkdir -p models/resnet/original/00001 gcloud storage cp gs://solutions-public-assets/tftrt-tutorial/resnet/export/1584366419/* models/resnet/original/00001 --recursive
TF-TRT の最適化ツールを含むコンテナ イメージをビルドします。
docker build ./ -t trt-optimizer docker image list
最後のコマンドでリポジトリのテーブルが表示されます。
テーブルの
tft-optimizer
リポジトリの行で、イメージ ID をコピーします。元のモデルに最適化(グラフの最適化、FP16 への変換、INT8 による量子化)を適用します。
export IMAGE_ID=IMAGE_ID nvidia-docker run --rm \ -v `pwd`/models/:/workspace/models ${IMAGE_ID} \ --input-model-dir='models/resnet/original/00001' \ --output-dir='models/resnet' \ --precision-mode='FP32' \ --batch-size=64 nvidia-docker run --rm \ -v `pwd`/models/:/workspace/models ${IMAGE_ID} \ --input-model-dir='models/resnet/original/00001' \ --output-dir='models/resnet' \ --precision-mode='FP16' \ --batch-size=64 nvidia-docker run --rm \ -v `pwd`/models/:/workspace/models ${IMAGE_ID} \ --input-model-dir='models/resnet/original/00001' \ --output-dir='models/resnet' \ --precision-mode='INT8' \ --batch-size=64 \ --calib-image-dir='gs://cloud-tpu-test-datasets/fake_imagenet/' \ --calibration-epochs=10
IMAGE_ID
は、前の手順でコピーしたtft-optimizer
のイメージ ID に置き換えます。--calib-image-dir
オプションには、トレーニング済みモデルに使用するトレーニング データの場所を指定します。INT8 量子化の調整にも同じトレーニング データが使用されます。調整プロセスには 5 分ほどかかります。コマンドが完了すると、最後の出力行は次のようになります。ここでは、最適化されたモデルが
./models/resnet
に保存されています。INFO:tensorflow:SavedModel written to: models/resnet/INT8/00001/saved_model.pb
ディレクトリ構造は次のようになります。
models └── resnet ├── FP16 │ └── 00001 │ ├── saved_model.pb │ └── variables ├── FP32 │ └── 00001 │ ├── saved_model.pb │ └── variables ├── INT8 │ └── 00001 │ ├── saved_model.pb │ └── variables └── original └── 00001 ├── saved_model.pb └── variables ├── variables.data-00000-of-00001 └── variables.index
次の表は、ディレクトリと最適化の関係をまとめたものです。
ディレクトリ | 最適化 |
---|---|
FP16 |
グラフの最適化と FP16 への変換 |
FP32 |
グラフの最適化 |
INT8 |
グラフの最適化と INT8 での量子化 |
original |
元のモデル(TF-TRT での最適化なし) |
推論サーバーをデプロイする
このセクションでは、5 つのモデルを使用して Triton サーバーをデプロイします。まず、前のセクションで作成したモデルバイナリを Cloud Storage にアップロードします。次に、GKE クラスタを作成し、そのクラスタに Triton サーバーをデプロイします。
モデルバイナリをアップロードする
SSH ターミナルで、モデルバイナリと
config.pbtxt
構成ファイルをストレージ バケットにアップロードします。export PROJECT_ID=PROJECT_ID export BUCKET_NAME=${PROJECT_ID}-models mkdir -p original/1/model/ cp -r models/resnet/original/00001/* original/1/model/ cp original/config.pbtxt original/1/model/ cp original/imagenet1k_labels.txt original/1/model/ mkdir -p tftrt_fp32/1/model/ cp -r models/resnet/FP32/00001/* tftrt_fp32/1/model/ cp tftrt_fp32/config.pbtxt tftrt_fp32/1/model/ cp tftrt_fp32/imagenet1k_labels.txt tftrt_fp32/1/model/ mkdir -p tftrt_fp16/1/model/ cp -r models/resnet/FP16/00001/* tftrt_fp16/1/model/ cp tftrt_fp16/config.pbtxt tftrt_fp16/1/model/ cp tftrt_fp16/imagenet1k_labels.txt tftrt_fp16/1/model/ mkdir -p tftrt_int8/1/model/ cp -r models/resnet/INT8/00001/* tftrt_int8/1/model/ cp tftrt_int8/config.pbtxt tftrt_int8/1/model/ cp tftrt_int8/imagenet1k_labels.txt tftrt_int8/1/model/ mkdir -p tftrt_int8_bs16_count4/1/model/ cp -r models/resnet/INT8/00001/* tftrt_int8_bs16_count4/1/model/ cp tftrt_int8_bs16_count4/config.pbtxt tftrt_int8_bs16_count4/1/model/ cp tftrt_int8_bs16_count4/imagenet1k_labels.txt tftrt_int8_bs16_count4/1/model/ gcloud storage buckets create gs://${BUCKET_NAME} gcloud storage cp original tftrt_fp32 tftrt_fp16 tftrt_int8 tftrt_int8_bs16_count4 \ gs://${BUCKET_NAME}/resnet/ --recursive
PROJECT_ID
は、前の手順で作成した Google Cloud プロジェクトの ID に置き換えます。config.pbtxt
ファイルでは、次のチューニング パラメータを指定します。- モデル名
- 入力テンソルと出力テンソルの名前
- 各モデルへの GPU 割り当て
- バッチサイズとインスタンス グループの数
たとえば、
original/1/model/config.pbtxt
ファイルには次の内容が含まれています。name: "original" platform: "tensorflow_savedmodel" max_batch_size: 64 input { name: "input" data_type: TYPE_FP32 format: FORMAT_NHWC dims: [ 224, 224, 3 ] } output { name: "probabilities" data_type: TYPE_FP32 dims: 1000 label_filename: "imagenet1k_labels.txt" } default_model_filename: "model" instance_group [ { count: 1 kind: KIND_GPU } ] dynamic_batching { preferred_batch_size: [ 64 ] max_queue_delay_microseconds: 20000 }
バッチサイズとインスタンス グループ数の詳細については、パフォーマンスの最適化をご覧ください。
次の表は、このセクションでデプロイした 5 つのモデルの概要を示しています。
モデル名 | 最適化 |
---|---|
original |
元のモデル(TF-TRT での最適化なし) |
tftrt_fp32 |
グラフの最適化 (バッチサイズ 64、インスタンス グループ 1) |
tftrt_fp16 |
グラフの最適化と FP16 への変換 (バッチサイズ 64、インスタンス グループ 1) |
tftrt_int8 |
グラフの最適化と INT8 での量子化 (バッチサイズ 64、インスタンス グループ 1) |
tftrt_int8_bs16_count4 |
グラフの最適化と INT8 での量子化 (バッチサイズ 16、インスタンス グループ 4) |
Triton を使用して推論サーバーをデプロイする
SSH ターミナルで、GKE クラスタを管理する認証パッケージをインストールして構成します。
export USE_GKE_GCLOUD_AUTH_PLUGIN=True sudo apt-get install google-cloud-sdk-gke-gcloud-auth-plugin
NVIDIA T4 GPU を使用するコンピューティング ノードを含む GKE クラスタと GPU ノードプールを作成します。
gcloud auth login gcloud config set compute/zone us-west1-b gcloud container clusters create tensorrt-cluster \ --num-nodes=20 gcloud container node-pools create t4-gpu-pool \ --num-nodes=1 \ --machine-type=n1-standard-8 \ --cluster=tensorrt-cluster \ --accelerator type=nvidia-tesla-t4,count=1
--num-nodes
フラグには、GKE クラスタ用に 20 個のインスタンス、GPU ノードプールt4-gpu-pool
用に 1 個のインスタンスを指定します。GPU ノードプールは、NVIDIA T4 GPU を持つ単一の
n1-standard-8
インスタンスで構成されます。NVIDIA T4 GPU は同じインスタンス上の複数の Pod で共有できません。そのため、GPU インスタンスの数は推論サーバーの Pod の数以上である必要があります。クラスタ情報を表示します。
gcloud container clusters list
出力は次のようになります。
NAME LOCATION MASTER_VERSION MASTER_IP MACHINE_TYPE NODE_VERSION NUM_NODES STATUS tensorrt-cluster us-west1-b 1.14.10-gke.17 XX.XX.XX.XX n1-standard-1 1.14.10-gke.17 21 RUNNING
ノードプール情報を表示します。
gcloud container node-pools list --cluster tensorrt-cluster
出力は次のようになります。
NAME MACHINE_TYPE DISK_SIZE_GB NODE_VERSION default-pool n1-standard-1 100 1.14.10-gke.17 t4-pool n1-standard-8 100 1.14.10-gke.17
daemonSet
ワークロードを有効にします。gcloud container clusters get-credentials tensorrt-cluster kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/container-engine-accelerators/master/nvidia-driver-installer/cos/daemonset-preloaded.yaml
このコマンドは、GPU ノードプール内のノードに NVIDIA GPU ドライバを読み込みます。また、GPU ノードプールに新しいノードを追加すると、ドライバが自動的に読み込まれます。
クラスタに推論サーバーをデプロイします。
sed -i.bak "s/YOUR-BUCKET-NAME/${PROJECT_ID}-models/" trtis_deploy.yaml kubectl create -f trtis_service.yaml kubectl create -f trtis_deploy.yaml
サービスが利用可能になるまで数分待ちます。
Triton の
clusterIP
アドレスを取得し、環境変数に保存します。export TRITON_IP=$(kubectl get svc inference-server \ -o "jsonpath={.spec['clusterIP']}") echo ${TRITON_IP}
この段階では、推論サーバーは、さまざまな最適化でモデルファイルを作成するのセクションで作成した 4 つの ResNet-50 モデルを提供しています。クライアントは、推論リクエストの送信時に使用するモデルを指定できます。
Prometheus と Grafana を使用してモニタリング サーバーをデプロイする
SSH ターミナルで、クラスタに Prometheus サーバーをデプロイします。
sed -i.bak "s/CLUSTER-IP/${TRITON_IP}/" prometheus-configmap.yml kubectl create namespace monitoring kubectl apply -f prometheus-service.yml -n monitoring kubectl create -f clusterRole.yml kubectl create -f prometheus-configmap.yml -n monitoring kubectl create -f prometheus-deployment.yml -n monitoring
Prometheus サービスのエンドポイント URL を取得します。
ip_port=$(kubectl get svc prometheus-service \ -o "jsonpath={.spec['clusterIP']}:{.spec['ports'][0]['port']}" -n monitoring) echo "http://${ip_port}"
Prometheus エンドポイント URL をメモしておきます。この URL は、後で Grafana を構成する際に使用します。
クラスタに Grafana サーバーをデプロイします。
kubectl create -f grafana-service.yml -n monitoring kubectl create -f grafana-deployment.yml -n monitoring
すべてのサービスが利用可能になるまで数分待ちます。
Grafana サービスのエンドポイント URL を取得します。
ip_port=$(kubectl get svc grafana-service \ -o "jsonpath={.status['loadBalancer']['ingress'][0]['ip']}:{.spec['ports'][0]['port']}" -n monitoring) echo "http://${ip_port}"
Grafana エンドポイント URL をメモしておきます。この URL は次のステップで使用します。
ウェブブラウザで、前の手順でメモした Grafana URL に移動します。
デフォルトのユーザー ID とパスワード(
admin
とadmin
)でログインします。プロンプトが表示されたら、デフォルトのパスワードを変更します。[Add your first data source] をクリックし、[Time series databases] リストで [Prometheus] を選択します。
[Settings] タブの [URL] フィールドに、先ほどメモした Prometheus エンドポイント URL を入力します。
[Save and Test] をクリックして、ホーム画面に戻ります。
nv_gpu_utilization
のモニタリング指標を追加します。- [Create your first dashboard] をクリックしてから、[Add visualization] をクリックします。
- [Data source] リストで、[Prometheus] を選択します。
[Query] タブの [Metric] フィールドに「
nv_gpu_utilization
」と入力します。[Panel options] セクションの [Title] フィールドに「
GPU Utilization
」と入力し、[Apply] をクリックします。このページに GPU 使用率のパネルが表示されます。
nv_gpu_memory_used_bytes
のモニタリング指標を追加します。- [Add] をクリックして、[Visualization] を選択します。
[Query] タブの [Metric] フィールドに「
nv_gpu_memory_used_bytes
」と入力します。[Panel options] セクションの [Title] フィールドに「
GPU Memory Used
」と入力し、[Save] をクリックします。
ダッシュボードを追加するには、[Save dashboard] パネルで [Save] をクリックします。
GPU 使用率と GPU メモリの使用量のグラフが表示されます。
負荷テストツールをデプロイする
このセクションでは、Locust 負荷テストツールを GKE にデプロイし、推論サーバーのパフォーマンスを測定するためのワークロードを生成します。
SSH ターミナルで、Triton クライアント ライブラリを含む Docker イメージをビルドし、Container Registry にアップロードします。
cd ../client git clone https://github.com/triton-inference-server/server cd server git checkout r19.05 sed -i.bak "s/bootstrap.pypa.io\/get-pip.py/bootstrap.pypa.io\/pip\/2.7\/get-pip.py/" Dockerfile.client docker build -t tritonserver_client -f Dockerfile.client . gcloud auth configure-docker docker tag tritonserver_client \ gcr.io/${PROJECT_ID}/tritonserver_client docker push gcr.io/${PROJECT_ID}/tritonserver_client
ビルドプロセスには 5 分ほどかかります。処理が完了すると、SSH ターミナルにコマンド プロンプトが表示されます。
ビルドプロセスが完了したら、Docker イメージをビルドしてテスト ワークロードを生成し、Container Registry にアップロードします。
cd .. sed -i.bak "s/YOUR-PROJECT-ID/${PROJECT_ID}/" Dockerfile docker build -t locust_tester -f Dockerfile . docker tag locust_tester gcr.io/${PROJECT_ID}/locust_tester docker push gcr.io/${PROJECT_ID}/locust_tester
コマンド内の
YOUR-PROJECT-ID
を変更または置換しないでください。このイメージは、前の手順で作成したイメージから作成されます。
Locust ファイル
service_master.yaml
とdeployment_master.yaml
をデプロイします。sed -i.bak "s/YOUR-PROJECT-ID/${PROJECT_ID}/" deployment_master.yaml sed -i.bak "s/CLUSTER-IP-TRTIS/${TRITON_IP}/" deployment_master.yaml kubectl create namespace locust kubectl create configmap locust-config --from-literal model=original --from-literal saddr=${TRITON_IP} --from-literal rps=10 -n locust kubectl apply -f service_master.yaml -n locust kubectl apply -f deployment_master.yaml -n locust
configmap
リソースは、クライアントが推論リクエストを送信する ML モデルを指定するために使用されます。サービスが利用可能になるまで数分待ちます。
locust-master
クライアントのclusterIP
アドレスを取得し、そのアドレスを環境変数に格納します。export LOCUST_MASTER_IP=$(kubectl get svc locust-master -n locust \ -o "jsonpath={.spec['clusterIP']}") echo ${LOCUST_MASTER_IP}
Locust クライアントをデプロイします。
sed -i.bak "s/YOUR-PROJECT-ID/${PROJECT_ID}/" deployment_slave.yaml sed -i.bak "s/CLUSTER-IP-LOCUST-MASTER/${LOCUST_MASTER_IP}/" deployment_slave.yaml kubectl apply -f deployment_slave.yaml -n locust
これらのコマンドは、テスト ワークロードの生成に使用できる 10 個の Locust クライアント Pod をデプロイします。現在のクライアント数で十分なリクエストを生成できない場合は、次のコマンドで Pod の数を変更できます。
kubectl scale deployment/locust-slave --replicas=20 -n locust
デフォルト クラスタにレプリカ数を増やせるだけの十分な容量がない場合は、GKE クラスタのノード数を増やすことをおすすめします。
Locust コンソールの URL をコピーして、ウェブブラウザでこの URL を開きます。
export LOCUST_IP=$(kubectl get svc locust-master -n locust \ -o "jsonpath={.status.loadBalancer.ingress[0].ip}") echo "http://${LOCUST_IP}:8089"
Locust のコンソールが開き、そこからテスト用のワークロードを生成できます。
実行中の Pod を確認する
コンポーネントが正常にデプロイされたことを確認するには、Pod が実行されていることを確認します。
SSH ターミナルで、推論サーバー Pod を確認します。
kubectl get pods
出力は次のようになります。
NAME READY STATUS RESTARTS AGE inference-server-67786cddb4-qrw6r 1/1 Running 0 83m
想定どおりの出力が得られない場合は、Triton を使用して推論サーバーをデプロイするの手順が完了していることを確認します。
Locust Pod を確認します。
kubectl get pods -n locust
出力は次のようになります。
NAME READY STATUS RESTARTS AGE locust-master-75f6f6d4bc-ttllr 1/1 Running 0 10m locust-slave-76ddb664d9-8275p 1/1 Running 0 2m36s locust-slave-76ddb664d9-f45ww 1/1 Running 0 2m36s locust-slave-76ddb664d9-q95z9 1/1 Running 0 2m36s
想定どおりの出力が得られない場合は、負荷テストツールをデプロイするの手順が完了していることを確認します。
モニタリング Pod を確認します。
kubectl get pods -n monitoring
出力は次のようになります。
NAME READY STATUS RESTARTS AGE grafana-deployment-644bbcb84-k6t7v 1/1 Running 0 79m prometheus-deployment-544b9b9f98-hl7q8 1/1 Running 0 81m
想定どおりの出力が得られない場合は、Prometheus と Grafana を使用してモニタリング サーバーをデプロイするの手順が完了していることを確認します。
このシリーズの次のパートでは、この推論サーバー システムを使用して、さまざまな最適化によりパフォーマンスがどのように向上するか、およびその最適化の解釈方法を説明します。次のステップについては、TensorFlow 推論システムのパフォーマンスの測定と調整をご覧ください。
次のステップ
- Google Kubernetes Engine(GKE)の詳細を確認する。
- Cloud Load Balancing の詳細を確認する。
- Cloud Architecture Center で、リファレンス アーキテクチャ、図、ベスト プラクティスを確認する。