En este documento, se describe cómo implementar la arquitectura de referencia que se describe en Sistema de inferencia de TensorFlow escalable.
Esta serie está dirigida a desarrolladores familiarizados con Google Kubernetes Engine y con los marcos de trabajo de aprendizaje automático (AA), incluidos TensorFlow y NVIDIA TensorRT.
Después de completar esta implementación, consulta Mide y ajusta el rendimiento de un sistema de inferencia de TensorFlow.
Arquitectura
En el siguiente diagrama, se muestra la arquitectura del sistema de inferencia.
Cloud Load Balancing envía el tráfico de la solicitud al clúster de GKE más cercano. El clúster contiene un Pod para cada nodo. En cada Pod, un Triton Inference Server proporciona un servicio de inferencia para entregar modelos ResNet-50 y una GPU NVIDIA T4 mejora el rendimiento. Los servidores de supervisión en el clúster recopilan datos de métricas sobre el uso de GPU y el uso de la memoria.
Para obtener más detalles, consulta Sistema de inferencia de TensorFlow escalable.
Objetivos
- Descargar un modelo previamente entrenado ResNet-50 y usa la integración de TensorFlow con TensorRT (TF-TRT) para aplicar las optimizaciones
- Entregar un modelo ResNet-50 desde un NVIDIA Triton Inference Server
- Compilar un sistema de supervisión para Triton mediante Prometheus y Grafana
- Compilar una herramienta de prueba de carga mediante Locust
Costos
Además de la GPU de NVIDIA T4, en esta implementación, usas los siguientes componentes facturables de Google Cloud:
Para generar una estimación de costos en función del uso previsto, usa la calculadora de precios.
Cuando termines esta implementación, no borres los recursos que creaste. Necesitas estos recursos cuando medes y ajustas la implementación.
Antes de comenzar
- 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.
Compila modelos optimizados con TF-TRT
En esta sección, crearás un entorno de trabajo y optimizarás el modelo previamente entrenado.
El modelo previamente entrenado usa el conjunto de datos falso en gs://cloud-tpu-test-datasets/fake_imagenet/
.
También hay una copia del modelo previamente entrenado en la ubicación de Cloud Storage en gs://solutions-public-assets/tftrt-tutorial/resnet/export/1584366419/
.
Crea un entorno de trabajo
Para tu entorno de trabajo, crea una instancia de Compute Engine mediante Deep Learning VM Images. Debes optimizar y cuantificar el modelo ResNet-50 con TensorRT en esta instancia.
In the Google Cloud console, activate Cloud Shell.
Implementa una instancia llamada
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"
Reemplaza
PROJECT_ID
por el ID del proyecto de Google Cloud que creaste antes.Este comando inicia una instancia de Compute Engine con NVIDIA T4. En el primer inicio, instala automáticamente el controlador de GPU de NVIDIA que es compatible con TensorRT 5.1.5.
Crea archivos del modelo con diferentes optimizaciones
En esta sección, aplicarás las siguientes optimizaciones al modelo ResNet-50 original mediante TF-TRT:
- Optimización de grafos
- Conversión a FP16 con la optimización del grafo
- Cuantización con INT8 con la optimización del grafo
Para obtener detalles sobre estas optimizaciones, consulta Optimización del rendimiento.
En la consola de Google Cloud, ve a Compute Engine > Instancias de VM.
Verás la instancia
working-vm
que creaste antes.Para abrir la consola de la terminal de la instancia, haz clic en SSH.
Usa esta terminal para ejecutar el resto de los comandos de este documento.
En la terminal, clona el repositorio requerido y cambia el directorio actual:
cd $HOME git clone https://github.com/GoogleCloudPlatform/gke-tensorflow-inference-system-tutorial cd gke-tensorflow-inference-system-tutorial/server
Descarga el modelo ResNet-50 previamente entrenado en un directorio local:
mkdir -p models/resnet/original/00001 gsutil cp -R gs://solutions-public-assets/tftrt-tutorial/resnet/export/1584366419/* models/resnet/original/00001
Compila una imagen de contenedor que contenga herramientas de optimización para TF-TRT:
docker build ./ -t trt-optimizer docker image list
El último comando muestra una tabla de repositorios.
En la tabla, en la fila del repositorio
tft-optimizer
, copia el ID de la imagen.Aplica las optimizaciones (optimización de grafos, conversión a FP16 y cuantización con INT8) al modelo original:
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
Reemplaza
IMAGE_ID
por el ID de la imagen detft-optimizer
que copiaste en el paso anterior.La opción
--calib-image-dir
especifica la ubicación de los datos de entrenamiento que se usan para el modelo previamente entrenado. Se usan los mismos datos de entrenamiento para una calibración de la cuantización con INT8. El proceso de calibración puede tomar alrededor de 5 minutos.Cuando los comandos terminen de ejecutarse, la última línea de resultado será similar a la siguiente, en la que los modelos optimizados se guardan en
./models/resnet
:INFO:tensorflow:SavedModel written to: models/resnet/INT8/00001/saved_model.pb
La estructura de directorios es similar a la siguiente:
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
En la siguiente tabla, se resume la relación entre los directorios y las optimizaciones.
Directorio | Optimización |
---|---|
FP16 |
Conversión a FP16, además de la optimización del grafo |
FP32 |
Optimización de grafos |
INT8 |
Cuantización con INT8, además de la optimización del grafo |
original |
Modelo original (sin optimización con TF-TRT) |
Implementa un servidor de inferencia
En esta sección, implementarás servidores de Triton con cinco modelos. En primer lugar, debes subir el objeto binario del modelo que creaste en la sección anterior a Cloud Storage. Luego, crearás un clúster de GKE y, luego, implementarás servidores Triton en él.
Sube el objeto binario del modelo
En la terminal SSH, sube los objetos binarios del modelo y los archivos de configuración
config.pbtxt
a un bucket de almacenamiento: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/
Reemplaza
PROJECT_ID
por el ID del proyecto de Google Cloud que creaste antes.Los siguientes parámetros de ajuste se especifican en los archivos
config.pbtxt
:- Nombre del modelo
- Nombre del tensor de entrada y de salida
- Asignación de GPU a cada modelo
- Tamaño del lote y cantidad de grupos de instancias
Por ejemplo, el archivo
original/1/model/config.pbtxt
incluye el siguiente contenido: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 }
Para obtener información detallada sobre el tamaño del lote y la cantidad de grupos de instancias, consulta Optimización del rendimiento.
En la siguiente tabla, se resumen los cinco modelos que implementaste en esta sección.
Nombre del modelo | Optimización |
---|---|
original |
Modelo original (sin optimización con TF-TRT) |
tftrt_fp32 |
Optimización de grafos (tamaño del lote=64, grupos de instancias=1) |
tftrt_fp16 |
La conversión a FP16, además de la optimización del grafo (tamaño de lote=64, grupos de instancias=1) |
tftrt_int8 |
Cuantización con INT8 además de la optimización del grafo (tamaño del lote=64, grupos de instancias=1) |
tftrt_int8_bs16_count4 |
Cuantización con INT8, además de la optimización de grafos (tamaño de lote =16, grupos de instancias=4) |
Implementa servidores de inferencia mediante Triton
En la terminal de SSH, instala y configura el paquete de autenticación, que administra los clústeres de GKE:
export USE_GKE_GCLOUD_AUTH_PLUGIN=True sudo apt-get install google-cloud-sdk-gke-gcloud-auth-plugin
Crea un clúster de GKE y un grupo de nodos de GPU con nodos de procesamiento que usen una 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
La marca
--num-nodes
especifica 20 instancias para el clúster de GKE y una instancia para el grupo de nodos de GPUt4-gpu-pool
.El grupo de nodos de GPU consta de una única instancia de
n1-standard-8
con una GPU NVIDIA T4. La cantidad de instancias de GPU debe ser igual o mayor que la cantidad de Pods del servidor de inferencia porque la GPU NVIDIA T4 no puede compartirse por varios Pods en la misma instancia.Muestra la información del clúster:
gcloud container clusters list
El resultado es similar al siguiente:
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
Muestra la información del grupo de nodos:
gcloud container node-pools list --cluster tensorrt-cluster
El resultado es similar al siguiente:
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
Habilita la carga de trabajo
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
Este comando carga el controlador de GPU de NVIDIA en los nodos del grupo de nodos de GPU. También carga automáticamente el controlador cuando agregas un nodo nuevo al grupo de nodos de GPU.
Implementa servidores de inferencia en el clúster:
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
Espera unos minutos hasta que los servicios estén disponibles.
Obtén la dirección
clusterIP
de Triton y almacénala en una variable de entorno:export TRITON_IP=$(kubectl get svc inference-server \ -o "jsonpath={.spec['clusterIP']}") echo ${TRITON_IP}
En este punto, el servidor de inferencia está entregando cuatro modelos ResNet-50 que creaste en la sección Crea archivos de modelo con diferentes optimizaciones. Los clientes pueden especificar el modelo que se usará cuando se envíen solicitudes de inferencia.
Implementa servidores de supervisión con Prometheus y Grafana
En la terminal SSH, implementa servidores de Prometheus en el clúster:
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
Obtén la URL de extremo del servicio de Prometheus.
ip_port=$(kubectl get svc prometheus-service \ -o "jsonpath={.spec['clusterIP']}:{.spec['ports'][0]['port']}" -n monitoring) echo "http://${ip_port}"
Toma nota de la URL del extremo de Prometheus, ya que la usarás para configurar Grafana más adelante.
Implementa servidores de Grafana en el clúster:
kubectl create -f grafana-service.yml -n monitoring kubectl create -f grafana-deployment.yml -n monitoring
Espera unos minutos hasta que todos los servicios estén disponibles.
Obtén la URL de extremo del servicio de 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}"
Toma nota de la URL del extremo de Grafana para usarla en el siguiente paso.
En un navegador web, ve a la URL de Grafana que anotaste en el paso anterior.
Accede con el ID de usuario y la contraseña predeterminados (
admin
yadmin
). Cuando se te solicite, cambia la contraseña predeterminada.Haz clic en Agrega tu primera fuente de datos y, en la lista Bases de datos de series temporales, selecciona Prometheus.
En la pestaña Configuración, en el campo URL, ingresa la URL del extremo de Prometheus que anotaste antes.
Haz clic en Guardar y probar y, luego, regresa a la pantalla principal.
Agrega una métrica de supervisión para
nv_gpu_utilization
:- Haz clic en Crea tu primer panel y, luego, en Agregar visualización.
- En la lista Fuente de datos, selecciona Prometheus.
En la pestaña Consulta, ingresa
nv_gpu_utilization
en el campo Métrica.En la sección Opciones de panel, en el campo Título, ingresa
GPU Utilization
y, luego, haz clic en Aplicar.En la página, se muestra un panel sobre el uso de GPU.
Agrega una métrica de supervisión para
nv_gpu_memory_used_bytes
:- Haz clic en Agregar y selecciona Visualización.
En la pestaña Consulta, ingresa
nv_gpu_memory_used_bytes
en el campo Métrica.En la sección Opciones de panel, en el campo Título, ingresa
GPU Memory Used
y, luego, haz clic en Guardar.
Para agregar el panel, en el panel Guardar panel, haz clic en Guardar.
Verás los grafos para el uso de GPU y la memoria de GPU usadas.
Implementa una herramienta de prueba de carga
En esta sección, implementarás la herramienta de prueba de carga de Locust en GKE y generarás cargas de trabajo para medir el rendimiento de los servidores de inferencia.
En la terminal de SSH, compila una imagen de Docker que contenga bibliotecas cliente de Triton y súbela a 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
El proceso de compilación puede tomar alrededor de 5 minutos. Cuando se complete el proceso, aparecerá un símbolo del sistema en la terminal SSH.
Cuando finalice el proceso de compilación, compila una imagen de Docker para generar una carga de trabajo de prueba y súbela a 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
No cambies ni reemplaces
YOUR-PROJECT-ID
en los comandos.Esta imagen se compiló a partir de la imagen que creaste en el paso anterior.
Implementa los archivos
service_master.yaml
ydeployment_master.yaml
de Locust: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
El recurso
configmap
se usa para especificar el modelo de aprendizaje automático al que los clientes envían solicitudes de inferencia.Espera unos minutos hasta que los servicios estén disponibles.
Obtén la dirección
clusterIP
del clientelocust-master
y almacénala en una variable de entorno:export LOCUST_MASTER_IP=$(kubectl get svc locust-master -n locust \ -o "jsonpath={.spec['clusterIP']}") echo ${LOCUST_MASTER_IP}
Implementa el cliente 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
Estos comandos implementan 10 Pods de cliente de Locust que puedes usar para generar cargas de trabajo de prueba. Si no puedes generar suficientes solicitudes con la cantidad actual de clientes, puedes cambiar la cantidad de Pods con el siguiente comando:
kubectl scale deployment/locust-slave --replicas=20 -n locust
Cuando no haya suficiente capacidad para que un clúster predeterminado aumente la cantidad de réplicas, te recomendamos que aumentes la cantidad de nodos en el clúster de GKE.
Copia la URL de la consola de Locust y, luego, abre esta URL en un navegador web:
export LOCUST_IP=$(kubectl get svc locust-master -n locust \ -o "jsonpath={.status.loadBalancer.ingress[0].ip}") echo "http://${LOCUST_IP}:8089"
Se abrirá la consola de Locust y podrás generar cargas de trabajo de prueba a partir de ella.
Verifica los Pods en ejecución
Para asegurarte de que los componentes se implementen de forma correcta, verifica que los Pods se estén ejecutando.
En la terminal de SSH, verifica el Pod del servidor de inferencia:
kubectl get pods
El resultado es similar al siguiente:
NAME READY STATUS RESTARTS AGE inference-server-67786cddb4-qrw6r 1/1 Running 0 83m
Si no obtienes el resultado esperado, asegúrate de que completaste los pasos en Implementa servidores de inferencia mediante Triton.
Verifica los Pods de Locust:
kubectl get pods -n locust
El resultado es similar al siguiente:
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 no obtienes el resultado esperado, asegúrate de que completaste los pasos en Implementa una herramienta de prueba de carga.
Verifica los Pods de supervisión:
kubectl get pods -n monitoring
El resultado es similar al siguiente:
NAME READY STATUS RESTARTS AGE grafana-deployment-644bbcb84-k6t7v 1/1 Running 0 79m prometheus-deployment-544b9b9f98-hl7q8 1/1 Running 0 81m
Si no obtienes el resultado esperado, asegúrate de que completaste los pasos en Implementa servidores de supervisión con Prometheus y Grafana.
En la siguiente parte de esta serie, deberás usar este sistema de servidores de inferencia para conocer cómo varias optimizaciones mejoran el rendimiento y cómo interpretarlas. Para conocer los próximos pasos, consulta Mide y ajusta el rendimiento de un sistema de inferencia de TensorFlow.
¿Qué sigue?
- Más información sobre Google Kubernetes Engine (GKE).
- Obtén más información sobre Cloud Load Balancing.
- Para obtener más información sobre las arquitecturas de referencia, los diagramas y las prácticas recomendadas, explora Cloud Architecture Center.