使用 TensorRT 5 和 NVIDIA T4 GPU 大规模运行 TensorFlow 推理

本教程介绍如何在 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
  • 永久性磁盘
  • Cloud Storage
  • 网络
  • NVIDIA T4 GPU

您可使用价格计算器根据您的预计使用量来估算费用。 Google Cloud 新用户可能有资格申请免费试用

准备工作

  1. 在 Cloud Console 中,转到项目选择器页面。

    转到项目选择器页面

  2. 选择或创建 Cloud 项目。

  3. 确保您的 Google Cloud 项目已启用结算功能。 了解如何确认您的项目已启用结算功能

  4. 启用 Compute Engine and Cloud Logging API。

    启用 API

  5. 确保您有足够的 GPU 配额可创建虚拟机。

完成本教程后,您可以删除所创建的资源以避免继续计费。如需了解详情,请参阅清理

准备环境

在本部分中,您将为整个教程中所用的值(如地区和区域)配置默认设置。本教程使用 us-central1 作为默认地区,使用 us-central1-b 作为默认区域。

此外,您还可以创建一个包含所有设置的文件,以便在需要再次打开 Cloud Shell 并重新初始化设置的情况下能够自动加载相应变量。

  1. 打开 Cloud Shell:

    打开 Cloud Shell

  2. 设置默认区域和地区:

    gcloud compute project-info add-metadata \
        --metadata google-compute-default-region=us-central1,google-compute-default-zone=us-central1-a
    
  3. 重新初始化 shell:

    gcloud init --console-only
    
  4. 对于出现的前三个问题,请按 1,对于最后一个问题,请输入项目 ID。

使用 TensorRT 优化模型

  1. 在 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 的可增速幅度。

  2. 创建虚拟机实例后,使用 ssh 连接到该虚拟机。

  3. 在实例中,从官方 TensorFlow 代码库下载 resnetv2 模型以测试 TensorRT 优化:

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

TensorRT 可以加速推理,而量化可带来其他改进。线性模型量化可将权重和激活从浮点型转换为整数型。例如,如果模型的初始权重是 FP32(浮点 32 位),通过降低精确率,您可以使用 INT8。但量化并非毫无代价:简化存储表示法可能会小幅降低模型的准确率。但从 FP32 转换为 FP16 则无此影响。

如何在速度(权重精确率)与准确率之间正确取舍权衡?代码库中有现有代码可实现此目标。此代码可衡量准确率与速度及其他指标。测试仅限图像识别模型,但要基于此代码实现自定义测试并不困难。

  1. 在 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 .
    
  2. 准备要执行的测试:

    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 显示的结果准确率稍差。

  3. 显示准确率数字:

    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。

  1. 在 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
    
  2. 创建一个 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
    
  3. 将模型转换为 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 模型。

  4. 运行推理以确保一切正常:

    tensorflow_model_server --model_base_path=$HOME/resnet_v2_int8_NCHW/ --rest_api_port=8888
    
  5. 要验证是否正常,请发送以下示例输入:

    curl -X POST localhost:8888/v1/models/default:predict -d '{"instances": [[[[1,1,1]]]]}'
    
  6. 如果收到此 curl 命令的结果,请按 Ctrl+C 退出推理运行。

  7. 要使用集群中的优化模型,请将模型上传到 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 中拥有模型后,您就可以创建集群了。首先要创建虚拟机模板。集群会使用该虚拟机模板创建新实例。

  1. 在 Cloud Shell 中,下载设置集群所需的代码:

    git clone https://github.com/GoogleCloudPlatform/tensorflow-inference-tensorrt5-t4-gpu.git
    
  2. 创建虚拟机模板,将 [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 个正在运行的实例。

  3. 创建代管式实例组:

    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 在所有区域都可用)和您的配额选择区域。

    创建组需要一些时间。

  4. 通过运行以下命令查看进度:

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

    输出如下所示:

    在创建组时

    创建完成后,将显示如下内容:

    创建组后

  5. 在 Cloud Console 中打开 Monitoring 页面。

    转到“监控”页面

  6. 确保您处于正确的项目工作区中,您的项目工作区显示在左上角。如果您是第一次访问此页面,则必须创建一个新工作区。

  7. Metrics Explorer 页面上,为资源类型选择 GCE VM Instance,同时为指标选择 custom/gpu_utilization

    Metrics Explorer 页面

    如果有数据传入,您会看到如下内容:

    显示零使用率的指标图表

  8. 在 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%。
  • 再创建一个实例来观察自动扩缩组的扩容情况。

  1. 通过 ssh 连接到实例。
  2. 增加 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 页面上的活动:

    Metrics Explorer 页面中的活动峰值

  3. 创建第二个实例。

  4. 转到 GCE Compute 页面 > 实例组 > 监控并观察活动:

    监控中的较高活动峰值

    此时,您的自动调节程序正在尝试启动尽可能多的实例来减少负载(但运气不佳)。 具体情况如下:

    启动大量实例

  5. 停止启动实例并观察活动缩减情况。

查看您的现有资源:

  • 一个经过训练的模型,已使用 TensorRT 5 (INT8) 进行优化
  • 一个由深度学习实例组成的托管群组
  • 根据 GPU 利用率的自动扩缩

创建负载平衡器

最后,在实例前创建一个负载平衡器。

  1. 在 Cloud Shell 中,创建运行状况检查以确定后端上的特定主机是否可以处理流量:

    gcloud compute health-checks create http $HEALTH_CHECK_NAME \
        --request-path /v1/models/default \
        --port 8888
    
  2. 配置实例组的指定端口,以便负载平衡器可以通过端口 80 将推理请求转发到使用端口 8888 的推理服务:

    gcloud compute instance-groups set-named-ports $INSTANCE_GROUP_NAME \
        --named-ports http:8888 \
        --region us-central1
    
  3. 创建后端服务:

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

    实际上,后端服务是具有运行状况检查的实例组。

  4. 将实例组添加到新的后端服务:

    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. 告诉负载平衡器要转发到后端服务的网址:

    export WEB_MAP_NAME="map-all"
    
    gcloud compute url-maps create $WEB_MAP_NAME \
        --default-service $WEB_BACKED_SERVICE_NAME
    
  6. 创建负载平衡器:

    export LB_NAME="tf-lb"
    
    gcloud compute target-http-proxies create $LB_NAME \
        --url-map $WEB_MAP_NAME
    
  7. 为负载平衡器创建外部 IP 地址:

    export IP4_NAME="lb-ip4"
    
    gcloud compute addresses create $IP4_NAME \
        --ip-version=IPV4 \
        --global
    
  8. 验证是否已分配 IP 地址:

    gcloud compute addresses list
    
  9. 查看 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
    

    创建全局转发规则后,可能需要几分钟来传播配置。

  10. 要连接到外部实例,请在项目上启用防火墙:

    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. 将图像转换为可以发送到服务器的格式:

    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. 运行推理:

    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 上创建的资源,以免这些资源占用配额,日后产生费用。以下部分介绍如何删除或关闭这些资源。

  1. 在 Cloud Console 中,转到管理资源页面。

    转到“管理资源”页面

  2. 在项目列表中,选择要删除的项目,然后点击删除
  3. 在对话框中输入项目 ID,然后点击关闭以删除项目。

后续步骤