スケーラブルな TensorFlow 推論システムをデプロイする

Last reviewed 2023-11-02 UTC

このドキュメントでは、スケーラブルな 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 モデルを提供する
  • PrometheusGrafana を使用して Triton 用のモニタリング システムをビルドする
  • Locust を使用して負荷テストツールをビルドする

費用

このデプロイでは、NVIDIA T4 GPU に加えて、Google Cloud の次の課金対象のコンポーネントを使用します。

料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを生成できます。

デプロイの完了後、作成したリソースを削除しないでください。これらのリソースは、デプロイを測定して調整する際に必要になります。

始める前に

  1. 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.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  3. Make sure that billing is enabled for your Google Cloud project.

  4. Enable the GKE API.

    Enable the API

  5. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  6. Make sure that billing is enabled for your Google Cloud project.

  7. Enable the GKE API.

    Enable the 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 モデルの最適化と量子化を行います。

  1. In the Google Cloud console, activate Cloud Shell.

    Activate Cloud Shell

  2. 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 による量子化とグラフの最適化

これらの最適化の詳細については、パフォーマンスの最適化をご覧ください。

  1. Google Cloud コンソールで、[Compute Engine] > [VM インスタンス] に移動します。

    [VM インスタンス] に移動

    前に作成した working-vm インスタンスが表示されます。

  2. インスタンスのターミナル コンソールを開くには、[SSH] をクリックします。

    このドキュメントの残りのコマンドを実行するには、このターミナルを使用します。

  3. ターミナルで、必要なリポジトリのクローンを作成し、現在のディレクトリを変更します。

    cd $HOME
    git clone https://github.com/GoogleCloudPlatform/gke-tensorflow-inference-system-tutorial
    cd gke-tensorflow-inference-system-tutorial/server
    
  4. トレーニング済みの 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
    
  5. TF-TRT の最適化ツールを含むコンテナ イメージをビルドします。

    docker build ./ -t trt-optimizer
    docker image list
    

    最後のコマンドでリポジトリのテーブルが表示されます。

  6. テーブルの tft-optimizer リポジトリの行で、イメージ ID をコピーします。

  7. 元のモデルに最適化(グラフの最適化、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 を使用して推論サーバーをデプロイする

  1. SSH ターミナルで、GKE クラスタを管理する認証パッケージをインストールして構成します。

    export USE_GKE_GCLOUD_AUTH_PLUGIN=True
    sudo apt-get install google-cloud-sdk-gke-gcloud-auth-plugin
    
  2. 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 の数以上である必要があります。

  3. クラスタ情報を表示します。

    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
    
  4. ノードプール情報を表示します。

    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
    
  5. 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 ノードプールに新しいノードを追加すると、ドライバが自動的に読み込まれます。

  6. クラスタに推論サーバーをデプロイします。

    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
    
  7. サービスが利用可能になるまで数分待ちます。

  8. Triton の clusterIP アドレスを取得し、環境変数に保存します。

    export TRITON_IP=$(kubectl get svc inference-server \
      -o "jsonpath={.spec['clusterIP']}")
    echo ${TRITON_IP}
    

この段階では、推論サーバーは、さまざまな最適化でモデルファイルを作成するのセクションで作成した 4 つの ResNet-50 モデルを提供しています。クライアントは、推論リクエストの送信時に使用するモデルを指定できます。

Prometheus と Grafana を使用してモニタリング サーバーをデプロイする

  1. 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
    
  2. 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 を構成する際に使用します。

  3. クラスタに Grafana サーバーをデプロイします。

    kubectl create -f grafana-service.yml -n monitoring
    kubectl create -f grafana-deployment.yml -n monitoring
    
  4. すべてのサービスが利用可能になるまで数分待ちます。

  5. 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 は次のステップで使用します。

  6. ウェブブラウザで、前の手順でメモした Grafana URL に移動します。

  7. デフォルトのユーザー ID とパスワード(adminadmin)でログインします。プロンプトが表示されたら、デフォルトのパスワードを変更します。

  8. [Add your first data source] をクリックし、[Time series databases] リストで [Prometheus] を選択します。

  9. [Settings] タブの [URL] フィールドに、先ほどメモした Prometheus エンドポイント URL を入力します。

  10. [Save and Test] をクリックして、ホーム画面に戻ります。

  11. nv_gpu_utilization のモニタリング指標を追加します。

    1. [Create your first dashboard] をクリックしてから、[Add visualization] をクリックします。
    2. [Data source] リストで、[Prometheus] を選択します。
    3. [Query] タブの [Metric] フィールドに「nv_gpu_utilization」と入力します。

    4. [Panel options] セクションの [Title] フィールドに「GPU Utilization」と入力し、[Apply] をクリックします。

      このページに GPU 使用率のパネルが表示されます。

  12. nv_gpu_memory_used_bytes のモニタリング指標を追加します。

    1. [Add] をクリックして、[Visualization] を選択します。
    2. [Query] タブの [Metric] フィールドに「nv_gpu_memory_used_bytes」と入力します。

    3. [Panel options] セクションの [Title] フィールドに「GPU Memory Used」と入力し、[Save] をクリックします。

  13. ダッシュボードを追加するには、[Save dashboard] パネルで [Save] をクリックします。

    GPU 使用率と GPU メモリの使用量のグラフが表示されます。

負荷テストツールをデプロイする

このセクションでは、Locust 負荷テストツールを GKE にデプロイし、推論サーバーのパフォーマンスを測定するためのワークロードを生成します。

  1. 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 ターミナルにコマンド プロンプトが表示されます。

  2. ビルドプロセスが完了したら、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 を変更または置換しないでください。

    このイメージは、前の手順で作成したイメージから作成されます。

  3. Locust ファイル service_master.yamldeployment_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 モデルを指定するために使用されます。

  4. サービスが利用可能になるまで数分待ちます。

  5. locust-master クライアントの clusterIP アドレスを取得し、そのアドレスを環境変数に格納します。

    export LOCUST_MASTER_IP=$(kubectl get svc locust-master -n locust \
        -o "jsonpath={.spec['clusterIP']}")
    echo ${LOCUST_MASTER_IP}
    
  6. 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 クラスタのノード数を増やすことをおすすめします。

  7. 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 が実行されていることを確認します。

  1. SSH ターミナルで、推論サーバー Pod を確認します。

    kubectl get pods
    

    出力は次のようになります。

    NAME                                READY   STATUS    RESTARTS   AGE
    inference-server-67786cddb4-qrw6r   1/1     Running   0          83m
    

    想定どおりの出力が得られない場合は、Triton を使用して推論サーバーをデプロイするの手順が完了していることを確認します。

  2. 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
    

    想定どおりの出力が得られない場合は、負荷テストツールをデプロイするの手順が完了していることを確認します。

  3. モニタリング 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 推論システムのパフォーマンスの測定と調整をご覧ください。

次のステップ