Ejecuta una inferencia de TensorFlow a gran escala mediante las GPU NVIDIA T4 y TensorRT 5

En este instructivo, se describe cómo ejecutar una inferencia a gran escala en las GPU NVIDIA TensorRT 5 y T4. NVIDIA TensorRT™ es una plataforma para inferencia de aprendizaje profundo de alto rendimiento.  Incluye un optimizador de inferencia de aprendizaje profundo y entorno de ejecución que ofrece baja latencia y alta capacidad de procesamiento para aplicaciones de inferencia de aprendizaje profundo.

En el instructivo, configuras un clúster de múltiples zonas para ejecutar una inferencia con un grupo de ajuste de escala automático sobre la base de la utilización de GPU.

Descripción general

En este instructivo, se proporciona lo siguiente:

  • Una arquitectura de referencia a fin de implementar un sistema de inferencia de aprendizaje automático escalable en Google Cloud que sea adecuado para un entorno de desarrollo. Tus necesidades de infraestructura y seguridad varían, por lo que puedes ajustar las opciones de configuración descritas en este instructivo según corresponda
  • Un repositorio de GitHub con secuencias de comandos que usas en el instructivo para instalar el modelo de TensorFlow y otros componentes necesarios
  • Instrucciones a fin de cuantizar el modelo de TensorFlow con TensorRT y de implementar las secuencias de comandos y la arquitectura de referencia
  • Instrucciones para configurar Cloud Load Balancing

Cuando completes este instructivo, tendrás un modelo cuantizado previamente entrenado en Cloud Storage y dos grupos de instancias de Compute Engine agrupados en clústeres de regiones diferentes encabezados por Cloud Load Balancing para el tráfico web. Esta arquitectura se ilustra en el siguiente diagrama.

Arquitectura que se usa en este instructivo

Objetivos

  • Comienza con un grafo previamente entrenado.
  • Optimiza el modelo con TensorRT y observa cuánto más rápido es el modelo con diferentes optimizaciones.
  • Después de finalizar el modelo, crea un clúster con base en las VM de aprendizaje profundo de Compute Engine que ya vienen con TensorFlow, TensorFlow Serving y TensorRT 5 preinstalados.

Costos

En este instructivo, se usan los siguientes componentes facturables de Google Cloud:

  • Compute Engine
  • Persistent Disk
  • Cloud Storage
  • Herramientas de redes
  • GPU NVIDIA T4

Para generar una estimación de costos en función del uso previsto, usa la calculadora de precios. Es posible que los usuarios nuevos de Google Cloud sean aptos para obtener una prueba gratuita.

Antes de comenzar

  1. En Cloud Console, ve a la página Selector de proyectos.

    Ir a la página de selección de proyectos

  2. Selecciona o crea un proyecto de Cloud.

  3. Asegúrate de que la facturación esté habilitada para tu proyecto de Google Cloud. Obtén información sobre cómo confirmar que tienes habilitada la facturación para tu proyecto.

  4. Habilita las API de Compute Engine and Cloud Logging.

    Habilita las API

  5. Asegúrate de tener suficiente cuota de GPU para crear VM.

Cuando finalices este instructivo, podrás borrar los recursos creados para evitar que se te siga facturando. Para obtener más información, consulta cómo hacer una limpieza.

Prepara tu entorno

En esta sección, establecerás la configuración predeterminada para los valores que se usan en todo el instructivo, como la región y la zona. En el instructivo, se usa us-central1 como la región predeterminada y us-central1-b como la zona predeterminada.

También crearás un archivo con toda la configuración, de modo que puedas cargar las variables de forma automática si necesitas volver a abrir Cloud Shell y reiniciar la configuración.

  1. Abre Cloud Shell:

    Abrir Cloud Shell

  2. Establece la región y la zona predeterminadas siguientes:

    gcloud compute project-info add-metadata \
        --metadata google-compute-default-region=us-central1,google-compute-default-zone=us-central1-a
    
  3. Reinicializa la shell:

    gcloud init --console-only
    
  4. Presiona 1 para las primeras tres preguntas y, luego, ingresa el ID del proyecto para la última pregunta.

Optimiza el modelo con TensorRT

  1. En Cloud Shell, crea una instancia que puedas usar para la preparación del modelo de la manera siguiente:

    export IMAGE_FAMILY="tf-latest-cu100"
    export INSTANCE_NAME="model-prep"
    
    gcloud compute instances create $INSTANCE_NAME \
        --image-family=$IMAGE_FAMILY \
        --machine-type=n1-standard-8 \
        --image-project=deeplearning-platform-release \
        --maintenance-policy=TERMINATE \
        --accelerator="type=nvidia-tesla-t4,count=1" \
        --metadata="install-nvidia-driver=True"
    

    Una GPU es más que suficiente para comparar diferentes modos de optimización de TensorRT y tener una idea de lo rápida que puede ser una GPU.

  2. Después de crear la instancia de VM, usa ssh para conectarte a la VM.

  3. En la instancia, descarga el modelo resnetv2 del repositorio oficial de TensorFlow para probar la optimización de TensorRT:

    wget -q http://download.tensorflow.org/models/official/resnetv2_imagenet_frozen_graph.pb
    

TensorRT puede acelerar la inferencia, pero una mejora adicional proviene de la cuantización. La cuantización de modelos lineales convierte pesos y activaciones de puntos flotantes a números enteros. Por ejemplo, si los pesos iniciales del modelo son FP32 (punto flotante de 32 bits), puedes reducir la precisión cuando usas INT8. Pero la cuantización no es gratuita: cuando disminuyes la representación de almacenamiento, puedes reducir mínimamente la precisión del modelo. Sin embargo, pasar de FP32 a FP16 es prácticamente gratis.

¿Cómo eliges el compromiso correcto entre la velocidad (exactitud de los pesos) y la precisión? Existe un código que hace esto. Este código puede medir la precisión en comparación con la velocidad y otras métricas. La prueba se limita a los modelos de reconocimiento de imagen, pero no es difícil implementar una prueba personalizada sobre la base de este código.

  1. En Cloud Shell, descarga y, luego, implementa una prueba personalizada de la manera siguiente:

    git clone https://github.com/tensorflow/models.git
    cd models
    git checkout f0e10716160cd048618ccdd4b6e18336223a172f
    touch research/__init__.py
    touch research/tensorrt/__init__.py
    cp research/tensorrt/labellist.json .
    cp research/tensorrt/image.jpg .
    
  2. Prepara la prueba para su ejecución:

    python -m research.tensorrt.tensorrt \
        --frozen_graph=$HOME/resnetv2_imagenet_frozen_graph.pb \
        --image_file=$HOME/models/image.jpg \
        --native --fp32 --fp16 --int8 \
        --output_dir=$HOME
    

    La prueba requiere un grafo inmovilizado (el modelo resnetv2 que descargaste anteriormente) y argumentos para los diferentes modos de cuantización que deseas probar. Este comando tarda un poco en terminar.

    Cuando finaliza la ejecución, la salida resultante es una comparación del resultado de la inferencia para una versión diferente del grafo:

    Comparación del resultado de inferencia para una versión diferente del grafo

    Los resultados de FP32 y FP16 son idénticos y muestran la misma exactitud, lo que significa que si estás conforme con TensorRT, puedes comenzar a usar FP16 de inmediato. Por el contrario, INT8 muestra resultados ligeramente menos precisos.

  3. Muestra los números de precisión con el comando siguiente:

    cat $HOME/log.txt
    

    Este comando produce el siguiente resultado:

    Registro de la inferencia a gran escala

TensorRT 5 muestra los siguientes resultados, todos comparados con los nativos:

  • Para FP32, la capacidad de procesamiento mejoró un 34%, de 319.1 fps a 428.2 fps.
  • Para FP16, la capacidad de procesamiento mejoró un 207%, de 319.1 fps a 979.6 fps.
  • Para INT8, la capacidad de procesamiento mejoró un 376%, de 319.1 fps a 1,519.5 fps.

Puedes aprender lo siguiente gracias a estos resultados:

  • Pasar de nativo a TensorRT impacta en la incertidumbre. Sin embargo, si estás de acuerdo con el precio pequeño, es probable que puedas ir directamente a FP16.
  • INT8 es muy rápido, pero el precio es notablemente más alto.

La sección siguiente usa el modelo INT8.

Convierte un modelo personalizado en TensorRT

Para convertir un modelo en un grafo de TensorRT, necesitas un modelo guardado.

  1. En Cloud Shell, descarga el siguiente modelo guardado que fuese previamente entrenado:

    wget http://download.tensorflow.org/models/official/20181001_resnet/savedmodels/resnet_v2_fp32_savedmodel_NCHW.tar.gz
    tar -xzvf resnet_v2_fp32_savedmodel_NCHW.tar.gz
    
  2. Crea la secuencia de comandos de Python siguiente que convierta el modelo inmovilizado en un grafo de TensorRT:

    cat <<EOF > convert_to_rt.py
    import tensorflow.contrib.tensorrt as trt
    import argparse
    
    parser = argparse.ArgumentParser(description="Converts TF SavedModel to the TensorRT enabled graph.")
    
    parser.add_argument("--input_model_dir", required=True)
    parser.add_argument("--output_model_dir", required=True)
    parser.add_argument("--batch_size", type=int, required=True)
    parser.add_argument("--precision_mode", choices=["FP32", "FP16", "INT8"], required=True)
    
    args = parser.parse_args()
    
    trt.create_inference_graph(
        None, None, max_batch_size=args.batch_size,
        input_saved_model_dir=args.input_model_dir,
        output_saved_model_dir=args.output_model_dir,
        precision_mode=args.precision_mode)
    EOF
    
  3. Convierte el modelo al grafo de TensorRT:

    python ./convert_to_rt.py \
        --input_model_dir=$HOME/resnet_v2_fp32_savedmodel_NCHW/1538687196 \
        --output_model_dir=$HOME/resnet_v2_int8_NCHW/00001 \
        --batch_size=128 \
        --precision_mode="INT8"
    

    Ahora tienes un modelo INT8 en la carpeta $HOME/resnet_v2_int8_NCHW/00001.

  4. Ejecuta la inferencia siguiente para asegurarte de que todo funciona:

    tensorflow_model_server --model_base_path=$HOME/resnet_v2_int8_NCHW/ --rest_api_port=8888
    
  5. Para verificar que funcione, envía la siguiente entrada de muestra:

    curl -X POST localhost:8888/v1/models/default:predict -d '{"instances": [[[[1,1,1]]]]}'
    
  6. Si ves resultados de este comando curl, presiona Ctrl+C para salir de la ejecución de la inferencia.

  7. A fin de usar el modelo optimizado desde tu clúster, sube el modelo en Cloud Storage. Para ello, reemplaza [GCS_PATH] por el nombre de tu depósito de Cloud Storage:

    tar -zcvf model.tar.gz ./resnet_v2_int8_NCHW/
    export GCS_PATH=[GCS_PATH]
    gsutil cp model.tar.gz $GCS_PATH
    

    La próxima vez que quieras usar este modelo, no tendrás que repetir todo este proceso. En cambio, puedes usar el grafo inmovilizado de INT8 que está en el depósito de Cloud Storage de la manera siguiente:

    gs://solutions-public-assets/tensorrt-t4-gpu/model.tar.gz
    

Configura un clúster

Ahora que tienes un modelo en Cloud Storage, puedes crear un clúster. El primer paso es crear una plantilla de VM. El clúster usa la plantilla de VM para crear nuevas instancias.

  1. En Cloud Shell, descarga el código que necesitas para configurar un clúster:

    git clone https://github.com/GoogleCloudPlatform/tensorflow-inference-tensorrt5-t4-gpu.git
    
  2. Reemplaza [PROJECT_NAME] por el nombre de tu proyecto para crear la plantilla de VM:

    export PROJECT_NAME=[PROJECT_NAME]
    export INSTANCE_TEMPLATE_NAME="tf-inference-template"
    export IMAGE_FAMILY="tf-latest-cu100"
    
    gcloud beta compute --project=$PROJECT_NAME instance-templates create $INSTANCE_TEMPLATE_NAME \
        --machine-type=n1-standard-16 \
        --maintenance-policy=TERMINATE \
        --accelerator=type=nvidia-tesla-t4,count=4 \
        --min-cpu-platform=Intel\ Skylake \
        --tags=http-server,https-server \
        --image-family=$IMAGE_FAMILY \
        --image-project=deeplearning-platform-release \
        --boot-disk-size=100GB \
        --boot-disk-type=pd-ssd \
        --boot-disk-device-name=$INSTANCE_TEMPLATE_NAME \
        --metadata startup-script-url=gs://solutions-public-assets/tensorrt-t4-gpu/start_agent_and_inf_server.sh
    

    El parámetro metadata especifica una secuencia de comandos de inicio que se instala en cada instancia que creó la plantilla de VM. Mediante la secuencia de comandos de inicio, se realizan los siguientes procedimientos cuando se inicia la instancia de VM:

    • Instala controladores NVIDIA.
    • Instala un agente para supervisar el uso de la GPU.
    • Descarga el modelo.
    • Inicia el servicio de inferencia.

    Cuando la plantilla esté lista, puedes crear el grupo de instancias administrado. El grupo no será de escalamiento y no habrá una verificación de estado. En este caso, creas un grupo que solo te da una garantía de que habrá 2 instancias en ejecución en zonas específicas.

  3. Crea el grupo de instancias administrado:

    gcloud compute instance-groups managed create $INSTANCE_GROUP_NAME \
        --template $INSTANCE_TEMPLATE_NAME \
        --base-instance-name deeplearning-instances \
        --size 2 \
        --zones us-central1-a,us-central1-b
    

    El valor INSTANCE_TEMPLATE_NAME es el nombre de la instancia que configuraste en un paso anterior. Elige zonas según la disponibilidad de GPU (no todas las GPU están disponibles en todas las zonas) y en función de tus cuotas.

    Crear el grupo lleva algo de tiempo.

  4. Ejecuta el comando siguiente para mirar el proceso:

    gcloud compute instance-groups managed list-instances $INSTANCE_GROUP_NAME --region us-central1
    

    El resultado debería ser similar a este:

    Mientras se crea el grupo

    Cuando finalice la creación, obtendrás algo como lo siguiente:

    Después de crear el grupo

  5. Abre la página Supervisión en Cloud Console.

    Abrir la página Supervisión

  6. Asegúrate de estar en el lugar de trabajo correcto del proyecto, que se muestra en la esquina superior izquierda. Si visitas esta página por primera vez, debes crear un lugar de trabajo nuevo.

  7. En la página Metrics Explorer (Explorador de métricas), en Resource type (Tipo de recurso), selecciona GCE VM Instance y, en Métricas, selecciona custom/gpu_utilization:

    Página de Explorador de métricas

    Si entran datos, deberías ver algo como lo siguiente:

    Grafo de métricas que muestra un uso de cero

  8. En Cloud Shell, habilita el ajuste de escala automático para tu grupo:

    gcloud compute instance-groups managed set-autoscaling $INSTANCE_GROUP_NAME \
        --custom-metric-utilization metric=custom.googleapis.com/gpu_utilization,utilization-target-type=GAUGE,utilization-target=85 \
        --max-num-replicas 4 \
        --cool-down-period 360 \
        --region us-central1
    

    La parte importante aquí es la ruta de uso, custom.googleapis.com/gpu_utilization, que es la ruta completa a tu métrica. Además, como especificaste un nivel objetivo de 85, cada vez que el uso de GPU alcance 85, se creará una instancia nueva en tu grupo.

Prueba el ajuste de escala automático

Para probar el ajuste de escala automático que configuraste en la sección anterior, debes hacer lo siguiente:

  • Usa SSH para conectarte a una de las instancias de GPU de aprendizaje profundo.
  • Carga todas las GPU en el 100%.
  • Observa a medida que tu grupo de ajuste de escala automático escala verticalmente mediante la creación de una instancia más.

  1. Conéctate a la instancia a través de ssh.
  2. Carga la GPU al 100% durante 600 segundos:

    git clone https://github.com/GoogleCloudPlatform/tensorflow-inference-tensorrt5-t4-gpu.git
    cd tensorflow-inference-tensorrt5-t4-gpu
    git submodule update --init --recursive
    cd third_party/gpu-burn
    make
    ./gpu_burn 600 > /dev/null &
    

    En Cloud Console, observa la actividad en la página Metrics Explorer (Explorador de métricas):

    Aumento de actividad en la página Explorador de métricas

  3. Crea la segunda instancia.

  4. En la Página de GCE Compute, dirígete a Instance Groups (Grupos de instancias) > Monitoring (Supervisión) y observa la actividad:

    Mayor aumento de actividad en la supervisión

    En este punto, el escalador automático intenta iniciar tantas instancias como sea posible para reducir la carga (sin lograrlo), y sucede lo siguiente:

    Inicio de muchas instancias

  5. Deja de iniciar instancias y observa cómo se reduce la actividad.

Observa lo siguiente que tienes:

  • Un modelo entrenado, optimizado con TensorRT 5 (INT8)
  • Un grupo administrado de instancias de aprendizaje profundo
  • Ajuste de escala automático con base en la utilización de GPU

Crea un balanceador de cargas

El último paso es crear un balanceador de cargas frente a las instancias.

  1. En Cloud Shell, crea verificaciones de estado para determinar si un host en particular en tu backend puede entregar el tráfico de la manera siguiente:

    gcloud compute health-checks create http $HEALTH_CHECK_NAME \
        --request-path /v1/models/default \
        --port 8888
    
  2. Configura los puertos con nombre del grupo de instancias para que el balanceador de cargas pueda reenviar las solicitudes de inferencia a través del puerto 80 al servicio de inferencia que se entrega mediante el puerto 8888 de la manera siguiente:

    gcloud compute instance-groups set-named-ports $INSTANCE_GROUP_NAME \
        --named-ports http:8888 \
        --region us-central1
    
  3. Crea un servicio de backend de la manera siguiente:

    export WEB_BACKED_SERVICE_NAME="tensorflow-backend"
    
    gcloud compute backend-services create $WEB_BACKED_SERVICE_NAME \
        --protocol HTTP \
        --health-checks $HEALTH_CHECK_NAME \
        --global
    

    Efectivamente, un servicio de backend es un grupo de instancias con verificación de estado.

  4. Agrega tu grupo de instancias al nuevo servicio de backend de la manera siguiente:

    gcloud compute backend-services add-backend $WEB_BACKED_SERVICE_NAME \
        --balancing-mode UTILIZATION \
        --max-utilization 0.8 \
        --capacity-scaler 1 \
        --instance-group $INSTANCE_GROUP_NAME \
        --instance-group-region us-central1 \
        --global
    
  5. Indica al balanceador de cargas qué URL debe reenviar al servicio de backend de la manera siguiente:

    export WEB_MAP_NAME="map-all"
    
    gcloud compute url-maps create $WEB_MAP_NAME \
        --default-service $WEB_BACKED_SERVICE_NAME
    
  6. Crea el balanceador de cargas de la manera siguiente:

    export LB_NAME="tf-lb"
    
    gcloud compute target-http-proxies create $LB_NAME \
        --url-map $WEB_MAP_NAME
    
  7. Crea una dirección IP externa para tu balanceador de cargas de la manera siguiente:

    export IP4_NAME="lb-ip4"
    
    gcloud compute addresses create $IP4_NAME \
        --ip-version=IPV4 \
        --global
    
  8. Verifica que se haya asignado la dirección IP:

    gcloud compute addresses list
    
  9. Revisa la regla de reenvío que Google Cloud usa para reenviar todas las solicitudes de la IP pública al balanceador de cargas:

    export IP=$(gcloud compute addresses list | grep ${IP4_NAME} | awk '{print $2}')
    export FORWARDING_RULE="lb-fwd-rule"
    
    gcloud compute forwarding-rules create $FORWARDING_RULE \
        --address $IP \
        --global \
        --target-http-proxy $LB_NAME \
        --ports 80
    

    Una vez que creas las reglas de reenvío globales, es posible que la configuración tome varios minutos en propagarse.

  10. Para conectarte a instancias externas, habilita el firewall en el proyecto de la manera siguiente:

    gcloud compute firewall-rules create www-firewall-80 \
        --target-tags http-server --allow tcp:80
    
    gcloud compute firewall-rules create www-firewall-8888 \
        --target-tags http-server --allow tcp:8888
    
  11. Convierte la imagen en un formato que pueda enviarse al servidor de la manera siguiente:

    cat <<EOF > load_and_convert_image.py
    from PIL import Image
    import numpy as np
    import json
    import codecs
    
    img = Image.open("image.jpg").resize((240, 240))
    img_array=np.array(img)
    result = {
        "instances":[img_array.tolist()]
    }
    file_path="/tmp/out.json"
    print(json.dump(result, codecs.open(file_path, 'w', encoding='utf-8'), separators=(',', ':'), sort_keys=True, indent=4))
    EOF
    
  12. Ejecuta la inferencia siguiente:

    wget https://pixnio.com/free-images/2017/10/31/2017-10-31-10-43-58-1032x825.jpg -O image.jpg
    python load_and_convert_image.py
    curl -X POST $IP/v1/models/default:predict -d @/tmp/out.json
    

    Si la inferencia funciona de forma correcta, el resultado es similar al siguiente:

    Resultado exitoso de la ejecución de una inferencia

Realiza una limpieza

Una vez que termines el instructivo, puedes limpiar los recursos que creaste en Google Cloud para que no consuman la cuota y no se te cobre por ellos en el futuro. En las siguientes secciones, se describe cómo borrar o desactivar estos recursos.

  1. En Cloud Console, ve a la página Administrar recursos.

    Ir a la página Administrar recursos

  2. En la lista de proyectos, selecciona el proyecto que deseas borrar y haz clic en Borrar .
  3. En el cuadro de diálogo, escribe el ID del proyecto y haz clic en Cerrar para borrar el proyecto.

Próximos pasos