Neste documento, descrevemos como implantar a arquitetura de referência descrita em Sistema de inferência escalonável do TensorFlow.
Esta série é destinada a desenvolvedores familiarizados com o Google Kubernetes Engine e os frameworks de machine learning (ML), incluindo o TensorFlow e a NVIDIA TensorRT.
Depois de concluir a implantação, consulte Analisar e ajustar o desempenho de um sistema de inferência do TensorFlow.
Arquitetura
O diagrama a seguir mostra a arquitetura do sistema de inferência.
O Cloud Load Balancing envia o tráfego de solicitação ao cluster do GKE mais próximo. O cluster contém um pod para cada nó. Em cada pod, um servidor de inferência Triton fornece um serviço de inferência (para exibir modelos ResNet-50), e uma GPU NVIDIA T4 melhora o desempenho. Os servidores de monitoramento no cluster coletam dados de métricas sobre a utilização da GPU e da memória.
Para saber mais detalhes, consulte Sistema de inferência do TensorFlow escalonável.
Objetivos
- Faça o download de um modelo ResNet-50 pré-treinado e use a integração do TensorFlow com a TensorRT (TF-TRT) para aplicar otimizações.
- Exibir um modelo ResNet-50 de um servidor de inferência NVIDIA Triton
- Crie um sistema de monitoramento para o Triton usando o Prometheus e o Grafana.
- Crie uma ferramenta de teste de carga usando o Locust.
Custos
Além da GPU NVIDIA T4, nesta implantação, você usará os seguintes componentes faturáveis do Google Cloud:
Para gerar uma estimativa de custo baseada na projeção de uso deste tutorial, use a calculadora de preços.
Ao concluir este tutorial, não exclua os recursos que você criou. Você precisa desses recursos ao medir e ajustar a implantação.
Antes de começar
- 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.
Criar modelos otimizados com TF-TRT
Nesta seção, você vai criar um ambiente de trabalho e otimizar o modelo pré-treinado.
O modelo pré-treinado usa o conjunto de dados simulado em gs://cloud-tpu-test-datasets/fake_imagenet/
.
Há também uma cópia do modelo pré-treinado no local do Cloud Storage em gs://solutions-public-assets/tftrt-tutorial/resnet/export/1584366419/
.
Criar um ambiente de trabalho
Para seu ambiente de trabalho, você cria uma instância do Compute Engine usando imagens de VM de aprendizado profundo. Otimize e quantize o modelo ResNet-50 com a TensorRT nessa instância.
In the Google Cloud console, activate Cloud Shell.
Implante uma instância chamada
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"
Substitua
PROJECT_ID
pelo ID do projeto do Cloud que você criou anteriormente.Este comando inicia uma instância do Compute Engine usando o NVIDIA T4. Na primeira inicialização, ele instala automaticamente o driver da GPU NVIDIA compatível com o TensorRT 5.1.5.
Criar arquivos de modelo com diferentes otimizações
Nesta seção, você vai aplicar as seguintes otimizações ao modelo original do ResNet-50 usando o TF-TRT:
- otimização de gráficos
- Conversão para FP16 com a otimização de gráficos
- Quantização com INT8 com a otimização de gráficos
Para saber mais sobre essas otimizações, consulte Otimização de desempenho.
No Console do Google Cloud, selecione Compute Engine > instâncias da VM.
Você verá a instância
working-vm
criada anteriormente.Para abrir o console do terminal da instância, clique em SSH.
Use esse terminal para executar o restante dos comandos neste documento.
No terminal, clone o repositório necessário e altere o diretório atual:
cd $HOME git clone https://github.com/GoogleCloudPlatform/gke-tensorflow-inference-system-tutorial cd gke-tensorflow-inference-system-tutorial/server
Faça o download do modelo ResNet-50 pré-treinado para um diretório local:
mkdir -p models/resnet/original/00001 gcloud storage cp gs://solutions-public-assets/tftrt-tutorial/resnet/export/1584366419/* models/resnet/original/00001 --recursive
Crie uma imagem de contêiner que contenha ferramentas de otimização para o TF-TRT:
docker build ./ -t trt-optimizer docker image list
O último comando mostra uma tabela de repositórios.
Na tabela, na linha do repositório
tft-optimizer
, copie o ID da imagem.Aplique as otimizações (otimização de gráfico, conversão para FP16 e quantização com INT8) ao 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
Substitua
IMAGE_ID
pelo ID da imagem paratft-optimizer
que você copiou na etapa anterior.A opção
--calib-image-dir
especifica o local dos dados de treinamento que são usados para o modelo pré-treinado. Os mesmos dados de treinamento são usados na calibração para a quantização INT8. O processo de calibração pode levar cerca de 5 minutos.Quando os comandos terminam de ser executados, a última linha de saída é semelhante à seguinte, em que os modelos otimizados são salvos em
./models/resnet
:INFO:tensorflow:SavedModel written to: models/resnet/INT8/00001/saved_model.pb
A estrutura de diretórios é semelhante a esta:
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
A tabela a seguir resume a relação entre diretórios e otimizações.
Diretório | Otimização |
---|---|
FP16 |
Conversão para FP16, além da otimização do gráfico |
FP32 |
otimização de gráficos |
INT8 |
Quantização com INT8, além da otimização do gráfico |
original |
Modelo original (sem otimização com TF-TRT) |
Implantar um servidor de inferência
Nesta seção, você implanta servidores do Triton com cinco modelos. Primeiro, faça upload do binário do modelo que você criou na seção anterior para o Cloud Storage. Depois, crie um cluster do GKE e implante servidores do Triton no cluster.
Fazer upload do binário do modelo
No terminal SSH, faça upload dos binários do modelo e dos arquivos de configuração
config.pbtxt
para um bucket de armazenamento: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
Substitua
PROJECT_ID
pelo ID do projeto do Cloud que você criou anteriormente.Os seguintes parâmetros de ajuste são especificados nos arquivos
config.pbtxt
:- Nome do modelo
- Nome do tensor de entrada e de saída
- Alocação de GPU para cada modelo
- Tamanho do lote e número de grupos de instâncias
Por exemplo, o arquivo
original/1/model/config.pbtxt
tem o seguinte conteúdo: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 detalhes sobre o tamanho do lote e o número de grupos de instâncias, consulte Otimização de desempenho.
A tabela a seguir resume os cinco modelos implantados nesta seção.
Nome do modelo | Otimização |
---|---|
original |
Modelo original (sem otimização com TF-TRT) |
tftrt_fp32 |
Otimização de gráficos (tamanho do lote=64, grupos de instâncias=1) |
tftrt_fp16 |
Conversão para FP16, além da otimização do gráfico (tamanho do lote=64, grupos de instâncias=1) |
tftrt_int8 |
Quantização com INT8, além da otimização do gráfico (tamanho do lote=64, grupos de instâncias=1) |
tftrt_int8_bs16_count4 |
Quantização com INT8, além da otimização do gráfico (tamanho do lote/16, grupos de instâncias=4) |
Implantar servidores de inferência com o Triton
No terminal SSH, instale e configure o pacote de autenticação, que gerencia os clusters do GKE:
export USE_GKE_GCLOUD_AUTH_PLUGIN=True sudo apt-get install google-cloud-sdk-gke-gcloud-auth-plugin
Crie um cluster do GKE e um pool de nós de GPU com nós de computação que usam uma 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
A flag
--num-nodes
especifica 20 instâncias para o cluster do GKE e uma instância para o pool de nós da GPUt4-gpu-pool
.O pool de nós da GPU consiste em uma única instância
n1-standard-8
com uma GPU NVIDIA T4. O número de instâncias de GPU precisa ser igual ou maior que os pods de servidor de inferência de números porque a NVIDIA T4 GPU não pode ser compartilhada por vários pods na mesma instância.Mostre as informações do cluster:
gcloud container clusters list
O resultado será assim:
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
Mostre as informações do pool de nós:
gcloud container node-pools list --cluster tensorrt-cluster
O resultado será assim:
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
Ative a carga de trabalho
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 carrega o driver GPU da NVIDIA nos nós do pool GPU. Além disso, ele carrega automaticamente o driver quando você adiciona um novo nó ao pool de nós da GPU.
Implante os servidores de inferência no 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
Aguarde alguns minutos até que os serviços estejam disponíveis.
Consiga o endereço
clusterIP
do Triton e armazene-o em uma variável de ambiente:export TRITON_IP=$(kubectl get svc inference-server \ -o "jsonpath={.spec['clusterIP']}") echo ${TRITON_IP}
Nesse momento, o servidor de inferência fornece quatro modelos ResNet-50 criados na seção Criar arquivos de modelo com otimizações diferentes. Os clientes podem especificar o modelo a ser usado ao enviar solicitações de inferência.
Implante servidores de monitoramento com o Prometheus e o Grafana
No terminal SSH, implante os servidores do Prometheus no 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
Consiga o URL do endpoint do serviço Prometheus.
ip_port=$(kubectl get svc prometheus-service \ -o "jsonpath={.spec['clusterIP']}:{.spec['ports'][0]['port']}" -n monitoring) echo "http://${ip_port}"
Anote o URL do endpoint do Prometheus, porque ele será usado para configurar o Grafana posteriormente.
Implante os servidores do Grafana no cluster:
kubectl create -f grafana-service.yml -n monitoring kubectl create -f grafana-deployment.yml -n monitoring
Aguarde alguns minutos até que todos os serviços fiquem disponíveis.
Consiga o URL do endpoint do serviço 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}"
Anote o URL do endpoint do Grafana para usar na próxima etapa.
Em um navegador da Web, acesse o URL do Grafana que você anotou na etapa anterior.
Faça login com o ID de usuário e a senha padrão (
admin
eadmin
). Quando solicitado, altere a senha padrão.Clique em Adicionar sua primeira fonte de dados e, na lista Bancos de dados de série temporal, selecione Prometheus.
Na guia Configurações, no campo URL, insira o URL do endpoint do Prometheus que você anotou anteriormente.
Clique em Salvar e testar e retorne à tela inicial.
Adicione uma métrica de monitoramento para
nv_gpu_utilization
:- Clique em Criar seu primeiro painel e em Adicionar visualização.
- Na lista Origem de dados, selecione Prometheus.
Na guia Consulta, no campo Métrica, digite
nv_gpu_utilization
.Na seção Panel options, no campo Title, digite
GPU Utilization
e clique em Apply.A página exibe um painel de utilização da GPU.
Adicione uma métrica de monitoramento para
nv_gpu_memory_used_bytes
:- Clique em Adicionar e selecione Visualização.
Na guia Consulta, no campo Métrica, digite
nv_gpu_memory_used_bytes
.Na seção Panel options, no campo Title, digite
GPU Memory Used
e clique em Save.
Para adicionar o painel, no painel Salvar painel, clique em Salvar.
Você verá os gráficos de uso da GPU e da memória da GPU usada.
Implantar uma ferramenta de teste de carga
Nesta seção, você implanta a ferramenta de teste de carga do Locust no GKE e gera carga de trabalho para medir o desempenho dos servidores de inferência.
No terminal SSH, crie uma imagem do Docker que contenha as bibliotecas de cliente Triton e faça o upload dela para o 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
O processo de compilação pode levar cerca de 5 minutos. Quando o processo for concluído, um prompt de comando vai aparecer no terminal SSH.
Quando o processo de build estiver concluído, crie uma imagem do Docker para gerar cargas de trabalho de teste e faça upload dela para o 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
Não altere nem substitua
YOUR-PROJECT-ID
nos comandos.Essa imagem é criada a partir da imagem que você criou na etapa anterior.
Implante os arquivos do Locust
service_master.yaml
edeployment_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
O recurso
configmap
é usado para especificar o modelo de machine learning a que os clientes enviam solicitações de inferência.Aguarde alguns minutos até que os serviços estejam disponíveis.
Consiga o endereço
clusterIP
do clientelocust-master
e armazene esse endereço em uma variável de ambiente:export LOCUST_MASTER_IP=$(kubectl get svc locust-master -n locust \ -o "jsonpath={.spec['clusterIP']}") echo ${LOCUST_MASTER_IP}
Implante o 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
Esses comandos implantam 10 pods de cliente do Locust que podem ser usados para gerar cargas de trabalho de teste. Se não for possível gerar solicitações suficientes com o número atual de clientes, altere o número de pods usando o seguinte comando:
kubectl scale deployment/locust-slave --replicas=20 -n locust
Quando não houver capacidade suficiente para que um cluster padrão aumente o número de réplicas, recomendamos que você aumente o número de nós no cluster do GKE.
Copie o URL do console do Locust e abra-o em um navegador da Web:
export LOCUST_IP=$(kubectl get svc locust-master -n locust \ -o "jsonpath={.status.loadBalancer.ingress[0].ip}") echo "http://${LOCUST_IP}:8089"
O console do Locust é aberto e é possível gerar cargas de trabalho de teste a partir dele.
Verificar os pods em execução
Para garantir que os componentes sejam implantados corretamente, verifique se os pods estão em execução.
No terminal SSH, verifique o pod do servidor de inferência:
kubectl get pods
O resultado será assim:
NAME READY STATUS RESTARTS AGE inference-server-67786cddb4-qrw6r 1/1 Running 0 83m
Se você não receber a saída esperada, verifique se concluiu as etapas em Implantar servidores de inferência usando o Triton.
Verifique os pods do Locust:
kubectl get pods -n locust
O resultado será assim:
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
Se você não receber a saída esperada, verifique se concluiu as etapas em Implantar uma ferramenta de teste de carga.
Verifique os pods de monitoramento:
kubectl get pods -n monitoring
O resultado será assim:
NAME READY STATUS RESTARTS AGE grafana-deployment-644bbcb84-k6t7v 1/1 Running 0 79m prometheus-deployment-544b9b9f98-hl7q8 1/1 Running 0 81m
Se você não receber a saída esperada, verifique se concluiu as etapas em Implantar servidores de monitoramento com o Prometheus e o Grafana.
Na próxima parte desta série, você usará esse sistema de servidor de inferência para saber como várias otimizações melhoram o desempenho e como interpretar essas otimizações. Para as próximas etapas, consulte Medir e ajustar o desempenho de um sistema de inferência do TensorFlow.
A seguir
- Saiba mais sobre o Google Kubernetes Engine (GKE)
- Saiba mais sobre o Cloud Load Balancing.
- Para mais arquiteturas de referência, diagramas e práticas recomendadas, confira a Central de arquitetura do Cloud.