Ce document explique comment déployer l'architecture de référence décrite dans la section Système d'inférence TensorFlow évolutif.
Cette série est destinée aux développeurs qui connaissent déjà les frameworks de Google Kubernetes Engine et de machine learning (ML), y compris TensorFlow et NVIDIA TensorRT.
Une fois ce déploiement terminé, consultez la section Mesurer et régler les performances d'un système d'inférence TensorFlow.
Architecture
Le schéma suivant illustre l'architecture du système d'inférence.
Cloud Load Balancing envoie le trafic de la requête vers le cluster GKE le plus proche. Le cluster contient un pod pour chaque nœud. Dans chaque pod, un serveur d'inférence Triton fournit un service d'inférence (pour diffuser des modèles ResNet-50) et un GPU NVIDIA T4 améliore les performances. Les serveurs de surveillance du cluster collectent des données de métriques sur l'utilisation du GPU et de la mémoire.
Pour en savoir plus, consultez la section Système d'inférence TensorFlow évolutif.
Objectifs
- Télécharger un modèle ResNet-50 pré-entraîné et utiliser l'intégration de TensorFlow avec TensorRT (TF-TRT) pour appliquer des optimisations
- Diffuser un modèle ResNet-50 à partir d'un serveur d'inférence NVIDIA Triton
- Créer un système de surveillance pour Triton à l'aide de Prometheus et de Grafana.
- Créer un outil de test de charge à l'aide de Locust.
Coûts
Outre le GPU NVIDIA T4, vous utilisez les composants facturables Google Cloud suivants dans ce déploiement :
Obtenez une estimation des coûts en fonction de votre utilisation prévue à l'aide du simulateur de coût.
Une fois ce déploiement terminé, ne supprimez pas les ressources que vous avez créées. Vous aurez besoin de ces ressources pour mesurer et régler le déploiement.
Avant de commencer
- 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.
Créer des modèles optimisés avec TF-TRT
Dans cette section, vous allez créer un environnement de travail et optimiser le modèle pré-entraîné.
Le modèle pré-entraîné utilise l'ensemble de données factice situé à l'adresse gs://cloud-tpu-test-datasets/fake_imagenet/
.
Il existe également une copie du modèle pré-entraîné dans l'emplacement Cloud Storage située à l'adresse gs://solutions-public-assets/tftrt-tutorial/resnet/export/1584366419/
.
Créer un environnement de travail
Pour votre environnement de travail, vous créez une instance Compute Engine à l'aide de Deep Learning VM Image. Vous optimisez et quantifiez le modèle ResNet-50 avec TensorRT sur cette instance.
In the Google Cloud console, activate Cloud Shell.
Déployez une instance nommée
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"
Remplacez
PROJECT_ID
par l'ID du projet Google Cloud que vous avez créé précédemment.Cette commande lance une instance Compute Engine à l'aide de NVIDIA T4. Au premier démarrage, elle installe automatiquement le pilote de GPU NVIDIA compatible avec TensorRT 5.1.5.
Créer des fichiers de modèle avec différentes optimisations
Dans cette section, vous allez appliquer les optimisations suivantes au modèle ResNet-50 d'origine à l'aide de TF-TRT :
- Optimisation des graphes
- Conversion en FP16 avec l'optimisation du graphique
- Quantification avec INT8 avec l'optimisation du graphique
Pour en savoir plus sur ces optimisations, consultez la section Optimisation des performances.
Dans la console Google Cloud, accédez à Compute Engine > Instances de VM.
Accéder à la page "Instances de VM"
L'instance
working-vm
que vous avez créée précédemment s'affiche.Pour ouvrir la console (terminal) de l'instance, cliquez sur SSH.
Utilisez ce terminal pour exécuter les autres commandes de ce document.
Dans le terminal, clonez le dépôt requis et modifiez le répertoire actuel :
cd $HOME git clone https://github.com/GoogleCloudPlatform/gke-tensorflow-inference-system-tutorial cd gke-tensorflow-inference-system-tutorial/server
Téléchargez le modèle ResNet-50 pré-entraîné dans un répertoire local :
mkdir -p models/resnet/original/00001 gsutil cp -R gs://solutions-public-assets/tftrt-tutorial/resnet/export/1584366419/* models/resnet/original/00001
Créez une image de conteneur contenant des outils d'optimisation pour TF-TRT :
docker build ./ -t trt-optimizer docker image list
La dernière commande affiche une table des dépôts.
Dans la table, dans la ligne du dépôt
tft-optimizer
, copiez l'ID de l'image.Appliquez les optimisations (optimisation du graphique, conversion en FP16 et quantification avec INT8) au modèle d'origine :
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
Remplacez
IMAGE_ID
par l'ID d'image detft-optimizer
que vous avez copié à l'étape précédente.L'option
--calib-image-dir
spécifie l'emplacement des données d'entraînement utilisées pour le modèle pré-entraîné. Les mêmes données d'entraînement sont utilisées en vue d'un calibrage pour la quantification INT8. Le processus de calibrage peut prendre environ cinq minutes.Une fois l'exécution des commandes terminée, la dernière ligne de sortie ressemble à ce qui suit, où les modèles optimisés sont enregistrés dans
./models/resnet
:INFO:tensorflow:SavedModel written to: models/resnet/INT8/00001/saved_model.pb
La structure du répertoire est semblable à celle-ci :
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
Le tableau suivant récapitule la relation entre les répertoires et les optimisations.
Annuaire | Optimisation |
---|---|
FP16 |
Conversion en FP16 en plus de l'optimisation du graphique |
FP32 |
Optimisation des graphes |
INT8 |
Quantification avec INT8 en plus de l'optimisation du graphique |
original |
Modèle d'origine (sans optimisation avec TF-TRT) |
Déployer un serveur d'inférence
Dans cette section, vous allez déployer des serveurs Triton avec cinq modèles. Commencez par importer le binaire du modèle que vous avez créé à la section précédente dans Cloud Storage. Créez ensuite un cluster GKE et déployez des serveurs Triton sur celui-ci.
Importer le binaire du modèle
Dans le terminal SSH, importez les binaires du modèle et les fichiers de configuration
config.pbtxt
dans un bucket de stockage :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/ gsutil mb gs://${BUCKET_NAME} gsutil -m cp -R original tftrt_fp32 tftrt_fp16 tftrt_int8 tftrt_int8_bs16_count4 \ gs://${BUCKET_NAME}/resnet/
Remplacez
PROJECT_ID
par l'ID du projet Google Cloud que vous avez créé précédemment.Les paramètres de réglage suivants sont spécifiés dans les fichiers
config.pbtxt
:- Nom du modèle
- Nom du Tensor d'entrée et nom du Tensor de sortie
- Allocation de GPU à chaque modèle
- Taille de lot et nombre de groupes d'instances
Par exemple, le fichier
original/1/model/config.pbtxt
contient les éléments suivants :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 }
Pour en savoir plus sur la taille de lot et le nombre de groupes d'instances, consultez la section Optimisation des performances.
Le tableau suivant récapitule les cinq modèles que vous avez déployés dans cette section.
Nom du modèle | Optimisation |
---|---|
original |
Modèle d'origine (sans optimisation avec TF-TRT) |
tftrt_fp32 |
Optimisation du graphique (taille du lot=64, groupes d'instances=1) |
tftrt_fp16 |
Conversion en FP16 en plus de l'optimisation du graphique (taille du lot=64, groupes d'instances=1) |
tftrt_int8 |
Quantification avec INT8 en plus de l'optimisation du graphique (taille du lot=64, groupes d'instances=1) |
tftrt_int8_bs16_count4 |
Quantification avec INT8 en plus de l'optimisation du graphique (taille du lot=16, groupes d'instances=4) |
Déployer des serveurs d'inférence à l'aide de Triton
Dans le terminal SSH, installez et configurez le package d'authentification, qui gère les clusters GKE :
export USE_GKE_GCLOUD_AUTH_PLUGIN=True sudo apt-get install google-cloud-sdk-gke-gcloud-auth-plugin
Créez un cluster GKE et un pool de nœuds GPU avec des nœuds de calcul utilisant un GPU NVIDIA T4 :
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
L'option
--num-nodes
spécifie 20 instances pour le cluster GKE et une instance pour le pool de nœuds GPUt4-gpu-pool
.Le pool de nœuds GPU est composé d'une seule instance
n1-standard-8
avec un GPU NVIDIA T4. Le nombre d'instances de GPU doit être supérieur ou égal au nombre de pods de serveur d'inférence, car le GPU NVIDIA T4 ne peut pas être partagé par plusieurs pods sur la même instance.Affichez les informations sur le cluster :
gcloud container clusters list
Le résultat ressemble à ce qui suit :
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
Affichez les informations relatives au pool de nœuds :
gcloud container node-pools list --cluster tensorrt-cluster
Le résultat ressemble à ce qui suit :
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
Activez la charge de travail
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
Cette commande charge le pilote de GPU NVIDIA sur les nœuds du pool de nœuds GPU. De plus, elle charge automatiquement le pilote lorsque vous ajoutez un nouveau nœud au pool de nœuds GPU.
Déployez des serveurs d'inférence sur le cluster :
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
Attendez quelques minutes jusqu'à ce que les services soient disponibles.
Obtenez l'adresse
clusterIP
de Triton et stockez-la dans une variable d'environnement :export TRITON_IP=$(kubectl get svc inference-server \ -o "jsonpath={.spec['clusterIP']}") echo ${TRITON_IP}
À ce stade, le serveur d'inférence diffuse quatre modèles ResNet-50 que vous avez créés dans la section Créer des fichiers de modèle avec différentes optimisations. Les clients peuvent spécifier le modèle à utiliser lors de l'envoi de requêtes d'inférence.
Déployer des serveurs de surveillance à l'aide de Prometheus et Grafana
Dans le terminal SSH, déployez des serveurs Prometheus sur le cluster :
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
Obtenez l'URL du point de terminaison du service Prometheus.
ip_port=$(kubectl get svc prometheus-service \ -o "jsonpath={.spec['clusterIP']}:{.spec['ports'][0]['port']}" -n monitoring) echo "http://${ip_port}"
Notez l'URL du point de terminaison Prometheus, car vous l'utiliserez pour configurer Grafana ultérieurement.
Déployez des serveurs Grafana sur le cluster :
kubectl create -f grafana-service.yml -n monitoring kubectl create -f grafana-deployment.yml -n monitoring
Attendez quelques minutes jusqu'à ce que tous les services soient disponibles.
Obtenez l'URL du point de terminaison du service Grafana.
ip_port=$(kubectl get svc grafana-service \ -o "jsonpath={.status['loadBalancer']['ingress'][0]['ip']}:{.spec['ports'][0]['port']}" -n monitoring) echo "http://${ip_port}"
Notez l'URL du point de terminaison Grafana à utiliser à l'étape suivante.
Dans un navigateur Web, accédez à l'URL Grafana que vous avez notée à l'étape précédente.
Connectez-vous avec l'ID utilisateur et le mot de passe par défaut (
admin
etadmin
). Lorsque vous y êtes invité, modifiez le mot de passe par défaut.Cliquez sur Ajouter votre première source de données, puis sélectionnez Prometheus dans la liste Bases de données de séries temporelles.
Dans l'onglet Paramètres, dans le champ URL, saisissez l'URL du point de terminaison Prometheus que vous avez notée précédemment.
Cliquez sur Enregistrer et tester, puis revenez à l'écran d'accueil.
Ajoutez une métrique de surveillance pour
nv_gpu_utilization
:- Cliquez sur Créer votre premier tableau de bord, puis sur Ajouter une visualisation.
- Dans la liste Source de données, sélectionnez Prometheus.
Dans l'onglet Requête, dans le champ Métrique, saisissez
nv_gpu_utilization
.Dans le champ Titre de la section Options du panneau, saisissez
GPU Utilization
, puis cliquez sur Appliquer.La page affiche un panneau concernant l'utilisation du GPU.
Ajoutez une métrique de surveillance pour
nv_gpu_memory_used_bytes
:- Cliquez sur Ajouter, puis sélectionnez Visualisation.
Dans l'onglet Requête, dans le champ Métrique, saisissez
nv_gpu_memory_used_bytes
.Dans le champ Titre de la section Options du panneau, saisissez
GPU Memory Used
, puis cliquez sur Enregistrer.
Pour ajouter le tableau de bord, cliquez sur Enregistrer dans le panneau Enregistrer le tableau de bord.
Les graphiques d'utilisation du GPU et de la mémoire GPU utilisée s'affichent.
Déployer un outil de test de charge
Dans cette section, vous allez déployer l'outil de test de charge Locust sur GKE et générer une charge de travail pour mesurer les performances des serveurs d'inférence.
Dans le terminal SSH, créez une image Docker contenant des bibliothèques clientes Triton, puis importez-la dans 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
Le processus de compilation peut prendre environ cinq minutes. Une fois le processus terminé, une invite de commande s'affiche dans le terminal SSH.
Une fois le processus de compilation terminé, créez une image Docker pour générer la charge de travail de test, puis importez-la dans 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
Ne modifiez pas et ne remplacez pas
YOUR-PROJECT-ID
dans les commandes.Cette image est créée à partir de l'image que vous avez créée à l'étape précédente.
Déployez les fichiers Locust
service_master.yaml
etdeployment_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
La ressource
configmap
permet de spécifier le modèle de machine learning auquel les clients envoient des requêtes d'inférence.Attendez quelques minutes jusqu'à ce que les services soient disponibles.
Obtenez l'adresse
clusterIP
du clientlocust-master
et stockez cette adresse dans une variable d'environnement :export LOCUST_MASTER_IP=$(kubectl get svc locust-master -n locust \ -o "jsonpath={.spec['clusterIP']}") echo ${LOCUST_MASTER_IP}
Déployez le client 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
Ces commandes déploient dix pods clients Locust que vous pouvez utiliser pour générer des charges de travail de test. Si vous ne pouvez pas générer suffisamment de requêtes avec le nombre actuel de clients, vous pouvez modifier le nombre de pods à l'aide de la commande suivante :
kubectl scale deployment/locust-slave --replicas=20 -n locust
Lorsque la capacité d'un cluster par défaut est insuffisante pour augmenter le nombre d'instances dupliquées, nous vous recommandons d'augmenter le nombre de nœuds dans le cluster GKE.
Copiez l'URL de la console Locust, puis ouvrez-la dans un navigateur Web :
export LOCUST_IP=$(kubectl get svc locust-master -n locust \ -o "jsonpath={.status.loadBalancer.ingress[0].ip}") echo "http://${LOCUST_IP}:8089"
La console Locust s'ouvre et vous pouvez générer des charges de travail de test depuis la console.
Vérifier les pods en cours d'exécution
Pour vous assurer que les composants sont déployés correctement, vérifiez que les pods sont en cours d'exécution.
Dans le terminal SSH, vérifiez le pod du serveur d'inférence :
kubectl get pods
Le résultat ressemble à ce qui suit :
NAME READY STATUS RESTARTS AGE inference-server-67786cddb4-qrw6r 1/1 Running 0 83m
Si vous n'obtenez pas le résultat attendu, assurez-vous d'avoir suivi la procédure décrite dans la section Déployer des serveurs d'inférence à l'aide de Triton.
Vérifiez les pods Locust :
kubectl get pods -n locust
Le résultat ressemble à ce qui suit :
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
Si vous n'obtenez pas le résultat attendu, assurez-vous d'avoir suivi la procédure décrite dans la section Déployer un outil de test de charge.
Vérifiez les pods de surveillance :
kubectl get pods -n monitoring
Le résultat ressemble à ce qui suit :
NAME READY STATUS RESTARTS AGE grafana-deployment-644bbcb84-k6t7v 1/1 Running 0 79m prometheus-deployment-544b9b9f98-hl7q8 1/1 Running 0 81m
Si vous n'obtenez pas le résultat attendu, assurez-vous d'avoir suivi la procédure décrite dans la section Déployer des serveurs de surveillance avec Prometheus et Grafana.
Dans la partie suivante de cette série, vous utiliserez ce système de serveurs d'inférence pour découvrir comment différentes optimisations améliorent les performances et comment les interpréter. Pour les étapes suivantes, consultez la section Mesurer et régler les performances d'un système d'inférence TensorFlow.
Étapes suivantes
- En savoir plus sur Google Kubernetes Engine (GKE).
- En savoir plus sur Cloud Load Balancing.
- Pour découvrir d'autres architectures de référence, schémas et bonnes pratiques, consultez le Centre d'architecture cloud.