本教程介绍如何在 NVIDIA TensorRT 5 和 T4 GPU 上大规模运行推理。 NVIDIA TensorRT™ 是一个用于高效深度学习推理的平台。它包括深度学习推理优化器和运行时,具有低延迟和高吞吐量的特点,可为各深度学习推理应用提供支持。
在本教程中,您将设置一个多地区集群,通过可根据 GPU 利用率自动扩缩的组来运行推理。
概览
本教程提供以下内容:
- 为在 Google Cloud 上实现适合开发环境的可扩缩机器学习推理系统提供的参考架构。您的基础架构和安全需求可能有所不同,可相应地调整本教程中说明的配置。
- GitHub 代码库,包含您在本教程中用于安装 TensorFlow 模型的脚本,及其他必要组件。
- 如何使用 TensorRT 量化 TensorFlow 模型、如何部署脚本以及如何部署参考架构的相关说明。
- 如何配置 Cloud Load Balancing 的相关说明。
完成本教程后,您将在 Cloud Storage 中拥有一个经过预先训练的量化模型,并拥有两个位于不同地区的聚簇 Compute Engine 实例组,其网络流量由 Cloud Load Balancing 负责处理。该架构如下图所示。
目标
- 从经过预先训练的图表开始学习。
- 使用 TensorRT 优化模型,并对比采用不同优化方式的模型增速幅度。
- 完成模型后,在预安装了 TensorFlow、TensorFlow Serving 和 TensorRT 5 的 Compute Engine 深度学习虚拟机的基础上创建集群。
费用
本教程使用 Google Cloud 的以下收费组件:
- Compute Engine
- Persistent Disk
- Cloud Storage
- 网络
- NVIDIA T4 GPU
准备工作
-
在 Google Cloud Console 中,转到项目选择器页面。
-
选择或创建 Google Cloud 项目。
-
确保您的 Cloud 项目已启用结算功能。 了解如何确认您的项目是否已启用结算功能。
- 启用 Compute Engine and Cloud Logging API。
- 确保您有足够的 GPU 配额可创建虚拟机。
完成本教程后,您可以删除所创建的资源以避免继续计费。如需了解详情,请参阅清理。
准备环境
在本部分中,您将为整个教程中所用的值(如地区和区域)配置默认设置。本教程使用 us-central1
作为默认地区,使用 us-central1-b
作为默认区域。
此外,您还可以创建一个包含所有设置的文件,以便在需要再次打开 Cloud Shell 并重新初始化设置的情况下能够自动加载相应变量。
打开 Cloud Shell:
设置默认区域和地区:
gcloud compute project-info add-metadata \ --metadata google-compute-default-region=us-central1,google-compute-default-zone=us-central1-a
重新初始化 shell:
gcloud init --console-only
对于出现的前三个问题,请按
1
,对于最后一个问题,请输入项目 ID。
使用 TensorRT 优化模型
在 Cloud Shell 中,创建可用于模型准备的实例:
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"
一个 GPU 就足以比较各种 TensorRT 优化模式,并了解单个 GPU 的可增速幅度。
创建虚拟机实例后,使用
ssh
连接到该虚拟机。在实例中,从官方 TensorFlow 代码库下载
resnetv2
模型以测试 TensorRT 优化:wget -q http://download.tensorflow.org/models/official/resnetv2_imagenet_frozen_graph.pb
TensorRT 可以加速推理,而量化可带来其他改进。线性模型量化可将权重和激活从浮点型转换为整数型。例如,如果模型的初始权重是 FP32(浮点 32 位),通过降低精确率,您可以使用 INT8。但量化并非毫无代价:简化存储表示法可能会小幅降低模型的准确率。但从 FP32 转换为 FP16 则无此影响。
如何在速度(权重精确率)与准确率之间正确取舍权衡?代码库中有现有代码可实现此目标。此代码可衡量准确率与速度及其他指标。测试仅限图像识别模型,但要基于此代码实现自定义测试并不困难。
在 Cloud Shell 中,下载并实现自定义测试:
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 .
准备要执行的测试:
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
此测试需要一个冻结图表(您之前下载的 resnetv2 模型)以及您要测试的不同量化模式的对应参数。此命令需要一些时间才能完成。
执行完成后,生成的输出是对不同版本图表的推理结果比较:
FP32 和 FP16 的结果完全相同,展现出相同的准确率,这意味着如果您准备使用 TensorRT,可以直接选择 FP16。相比之下,INT8 显示的结果准确率稍差。
显示准确率数字:
cat $HOME/log.txt
此命令会生成以下输出:
TensorRT 5 显示以下结果(均与原生模式比较):
- 对于 FP32,吞吐量从 319.1 fps 到 428.2 fps,增幅约 34%。
- 对于 FP16,吞吐量从 319.1 fps 到 979.6 fps,增幅为 207%。
- 对于 INT8,吞吐量从 319.1 fps 到 1519.5 fps,增幅约 376%。
根据这些结果,您可以发现:
- 从原生模式到 TensorRT 会影响模型的不确定性。但如果您能接受小小的代价,可以直接选择 FP16。
- INT8 非常快,但代价明显更高。
下一部分将使用 INT8 模型。
将自定义模型转换为 TensorRT
要将模型转换为 TensorRT 图表,您需要一个 SavedModel。
在 Cloud Shell 中,下载以下经过预先训练的保存模型:
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
创建一个 Python 脚本,将冻结模型转换为 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
将模型转换为 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"
现在,
$HOME/resnet_v2_int8_NCHW/00001
文件夹中有一个 INT8 模型。运行推理以确保一切正常:
tensorflow_model_server --model_base_path=$HOME/resnet_v2_int8_NCHW/ --rest_api_port=8888
要验证是否正常,请发送以下示例输入:
curl -X POST localhost:8888/v1/models/default:predict -d '{"instances": [[[[1,1,1]]]]}'
如果收到此
curl
命令的结果,请按 Ctrl+C 退出推理运行。要使用集群中的优化模型,请将模型上传到 Cloud Storage,并将
[GCS_PATH]
替换为您的 Cloud Storage 存储分区名称:tar -zcvf model.tar.gz ./resnet_v2_int8_NCHW/ export GCS_PATH=[GCS_PATH] gsutil cp model.tar.gz $GCS_PATH
下次要使用此模型时,无需重复此全部过程。您可以改为使用 Cloud Storage 存储分区中的 INT8 冻结图表:
gs://solutions-public-assets/tensorrt-t4-gpu/model.tar.gz
设置集群
在 Cloud Storage 中拥有模型后,您就可以创建集群了。首先要创建虚拟机模板。集群会使用该虚拟机模板创建新实例。
在 Cloud Shell 中,下载设置集群所需的代码:
git clone https://github.com/GoogleCloudPlatform/tensorflow-inference-tensorrt5-t4-gpu.git
创建虚拟机模板,将
[PROJECT_NAME]
替换为您的项目名称: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
metadata
参数指定在通过虚拟机模板创建的每个实例上安装的启动脚本。启动脚本用于在虚拟机实例启动时执行以下过程:- 安装 NVIDIA 驱动程序。
- 安装监控代理,以监控 GPU 利用率。
- 下载模型。
- 启动推理服务。
模板准备就绪后,您就可以创建代管式实例组。 该组不会扩缩,也不会执行运行状况检查。 您将要创建的组只保证在特定区域有 2 个正在运行的实例。
创建代管式实例组:
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
INSTANCE_TEMPLATE_NAME
值是您在之前步骤中设置的实例的名称。根据 GPU 的可用性(并非所有 GPU 在所有区域都可用)和您的配额选择区域。创建组需要一些时间。
通过运行以下命令查看进度:
gcloud compute instance-groups managed list-instances $INSTANCE_GROUP_NAME --region us-central1
输出如下所示:
创建完成后,将显示如下内容:
在 Cloud Console 中打开 Monitoring 页面。
确保您处于正确的项目工作区中,您的项目工作区显示在左上角。如果您是第一次访问此页面,则必须创建一个新工作区。
在 Metrics Explorer 页面上,为资源类型选择
GCE VM Instance
,同时为指标选择custom/gpu_utilization
:如果有数据传入,您会看到如下内容:
在 Cloud Shell 中,为您的组启用自动扩缩:
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
上面示例中的重要部分是利用率路径
custom.googleapis.com/gpu_utilization
,它是指标的完整路径。此外,由于您指定的目标级别为 85,因此只要 GPU 利用率达到 85,就会在您的组中创建一个新实例。
测试自动扩缩
要测试您在上一部分设置的自动扩缩,必须执行以下操作:
- 使用 SSH 连接到其中一个深度学习 GPU 实例。
- 增加所有 GPU 的负载,使其利用率达到 100%。
- 再创建一个实例来观察自动扩缩组的扩容情况。
- 通过
ssh
连接到实例。 增加 GPU 的负载,使利用率达到 100% 并保持 600 秒:
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 &
在 Cloud Console 中,注意 Metrics Explorer 页面上的活动:
创建第二个实例。
转到 GCE Compute 页面 > 实例组 > 监控并观察活动:
此时,您的自动调节程序正在尝试启动尽可能多的实例来减少负载(但运气不佳)。 具体情况如下:
停止启动实例并观察活动缩减情况。
查看您的现有资源:
- 一个经过训练的模型,已使用 TensorRT 5 (INT8) 进行优化
- 一个由深度学习实例组成的托管群组
- 根据 GPU 利用率的自动扩缩
创建负载平衡器
最后,在实例前创建一个负载平衡器。
在 Cloud Shell 中,创建运行状况检查以确定后端上的特定主机是否可以处理流量:
gcloud compute health-checks create http $HEALTH_CHECK_NAME \ --request-path /v1/models/default \ --port 8888
配置实例组的指定端口,以便负载平衡器可以通过端口 80 将推理请求转发到使用端口 8888 的推理服务:
gcloud compute instance-groups set-named-ports $INSTANCE_GROUP_NAME \ --named-ports http:8888 \ --region us-central1
创建后端服务:
export WEB_BACKED_SERVICE_NAME="tensorflow-backend" gcloud compute backend-services create $WEB_BACKED_SERVICE_NAME \ --protocol HTTP \ --health-checks $HEALTH_CHECK_NAME \ --global
实际上,后端服务是具有运行状况检查的实例组。
将实例组添加到新的后端服务:
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
告诉负载平衡器要转发到后端服务的网址:
export WEB_MAP_NAME="map-all" gcloud compute url-maps create $WEB_MAP_NAME \ --default-service $WEB_BACKED_SERVICE_NAME
创建负载平衡器:
export LB_NAME="tf-lb" gcloud compute target-http-proxies create $LB_NAME \ --url-map $WEB_MAP_NAME
为负载平衡器创建外部 IP 地址:
export IP4_NAME="lb-ip4" gcloud compute addresses create $IP4_NAME \ --ip-version=IPV4 \ --global
验证是否已分配 IP 地址:
gcloud compute addresses list
查看 Google Cloud 用于将所有请求从公共 IP 转发到负载平衡器的转发规则:
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
创建全局转发规则后,可能需要几分钟来传播配置。
要连接到外部实例,请在项目上启用防火墙:
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
将图像转换为可以发送到服务器的格式:
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
运行推理:
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
如果推理正常运行,将显示如下结果:
清理
学完本教程后,您可以清理在 Google Cloud 上创建的资源,以免这些资源占用配额,日后产生费用。以下部分介绍如何删除或关闭这些资源。
- 在 Cloud Console 中,转到管理资源页面。
- 在项目列表中,选择要删除的项目,然后点击删除。
- 在对话框中输入项目 ID,然后点击关闭以删除项目。