TensorRT debug及FP16浮点数溢出问题分析

前言

TensorRT是NVIDIA推出的一款高效深度学习模型推理框架,其包括了深度学习推理优化器和运行时,能够让深度学习推理应用拥有低时延和高吞吐的优点。

在这里插入图片描述
除了常规的加速功能外,TensorRT还提供了一套可用于engine生成过程中debug的工具,包括Polygraphy、ONNX GraphSurgeon和PyTorch-Quantization。这些小工具用处很大,值得花时间进一步研究。

在这里插入图片描述

Debug方法示例

polygraphy

Polygraphy是TensorRT官方提供的一系列小工具合集,是一个非常强大的Debug工具。
在这里插入图片描述
可以使用Polygraphy检查ONNX或TRT的网络结构,可以debug engine,还可以利用Polygraphy查看ONNX与TRT模型的输出差异等。

例如利用Polygraphy查看ONNX与TRT模型的输出差异:

# 使用默认参数
polygraphy run polygraphy_debug.engine --trt

# 加载onnx推理得到的结果,并设置绝对误差
polygraphy run polygraphy_debug.engine --trt --load-outputs onnx_res.json --abs 1e-4

# 使用fp16和cublas策略库生成engine,并与onnx推理得到的结果作比较
polygraphy run net_bs8.onnx --trt --onnxrt --tactic-sources CUBLAS --fp16 --rtol 1e-03 --atol 1e-03 

# 保存 data_loader生成的输入和运行得到的结果
polygraphy run net_bs8.onnx --onnxrt  --val-range [0,1] --save-inputs  net_input.json --save-outputs onnx_res.json
# 对onnx推理得到的结果进行保存,同时运行trt engine,并与onnx推理得到的结果作比较
polygraphy run net_bs8.onnx --onnxrt --load-inputs  gpen_input.json --save-outputs onnx_res.json
polygraphy run net_bs8_fp32.engine --model-type engine --trt --load-outputs onnx_res.json --abs 1e-4
--load-inputs  net_input.json

# 采用二分策略,使用fp16和cublas策略库按层数迭代生成engine,并加载onnx推理得到的结果作比较,以判断设置fp16时误差出现的层的范围
polygraphy debug precision net_bs8.onnx --fp16 --tactic-sources cublas --check polygraphy run polygraphy_debug.engine --trt --load-outputs onnx_res.json --abs 1e-1

误差在设置范围内时,打印结果如下:
在这里插入图片描述

# 使用polygraphy debug precision工具搜索Layer精度设置,判断FP16的engine运行结果与onnx结果的误差是否在误差范围内,不移除运行中生成的engine文件
polygraphy debug precision net_bs8.onnx --fp16 --tactic-sources cublas --check polygraphy run polygraphy_debug.engine --trt --load-inputs  net_input.json --load-outputs onnx_res.json --abs 1e-1 --no-remove-intermediate

# POLYGRAPHY_ARRAY_SWAP_THRESHOLD_MB=0表示结果交换到硬盘上,不放在内存中,mark all 表示对比所有节点结果
POLYGRAPHY_ARRAY_SWAP_THRESHOLD_MB=0 polygraphy run net_bs8.onnx --trt --onnxrt --tactic-sources CUBLAS --fp16 --rtol 1e-03 --atol 1e-03  --trt-outputs mark all --onnx-outputs mark all


# 比较单个或者多个Tensor的结果
polygraphy run net_bs8.onnx --onnxrt --trt --tactic-sources CUBLAS --fp16 --rtol 1e-03 --atol 1e-03 --trt-outputs 151 --onnx-outputs 151

polygraphy debug run net_bs8.onnx --onnxrt  --check polygraphy run net_bs8.engine --trt --tactic-sources CUBLAS --fp16 --rtol 1e-03 --atol 1e-03 --trt-outputs 153 197 --onnx-outputs 153 197


# 检查模型
polygraphy inspect model net_bs8.engine --mode=basic --display-as=trt
polygraphy inspect model net_bs8.onnx --mode=full --display-as=trt

更多polygraphy的使用可以查阅参考资料[2][6]

Python API

TensorRT提供了Python API,可以基于该API进行生成engine的过程。同时,可以按层名或节点名设置一些debug参数,例如该层是否使用FP16。以实现针对层的调试。

__author__ = 'TracelessLe'

import os
import tensorrt as trt

ONNX_SIM_MODEL_PATH = 'net_bs8_v1_simple.onnx'
TENSORRT_ENGINE_PATH_PY = 'net_bs8_v1_fp16_py.engine'

def build_engine(onnx_file_path, engine_file_path, flop=16):
    trt_logger = trt.Logger(trt.Logger.VERBOSE)  # trt.Logger.ERROR
    builder = trt.Builder(trt_logger)
    network = builder.create_network(
        1 << (int)(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH)
    )
    
    parser = trt.OnnxParser(network, trt_logger)
    # parse ONNX
    with open(onnx_file_path, 'rb') as model:
        if not parser.parse(model.read()):
            print('ERROR: Failed to parse the ONNX file.')
            for error in range(parser.num_errors):
                print(parser.get_error(error))
            return None
    print("Completed parsing ONNX file")
    builder.max_workspace_size = 2 << 30
    # default = 1 for fixed batch size
    builder.max_batch_size = 1
    # set mixed flop computation for the best performance
    if builder.platform_has_fast_fp16 and flop == 16:
        builder.fp16_mode = True

    if os.path.isfile(engine_file_path):
        try:
            os.remove(engine_file_path)
        except Exception:
            print("Cannot remove existing file: ",
                engine_file_path)

    print("Creating Tensorrt Engine")

    include_list = ['ConvTranspose']
    check_list = ['Pow_293', 'Sqrt_297']
    for i, n_i in enumerate(network):
        if n_i.name in check_list or n_i.name.split('_')[0] in include_list:
            layer = network[i]
            layer.precision = trt.float32
        print(f'Network Layer {i}:  {n_i.name}, {n_i.type}, {n_i.precision}, is_set: {n_i.precision_is_set }')

    config = builder.create_builder_config()
    config.set_tactic_sources(1 << int(trt.TacticSource.CUBLAS))
    config.max_workspace_size = 2 << 30
    config.set_flag(trt.BuilderFlag.FP16)
    config.set_flag(trt.BuilderFlag.STRICT_TYPES)
    # config.flags = (1 << int(trt.BuilderFlag.FP16) | 1 << int(trt.BuilderFlag.STRICT_TYPES))
    print('config.flags: ', config.flags)
    
    engine = builder.build_engine(network, config)
    with open(engine_file_path, "wb") as f:
        f.write(engine.serialize())
    print("Serialized Engine Saved at: ", engine_file_path)
    return engine

if __name__ == "__main__":    
    build_engine(ONNX_SIM_MODEL_PATH, TENSORRT_ENGINE_PATH_PY)

从上述代码中,通过config.set_flag(trt.BuilderFlag.STRICT_TYPES)打开了trt的严格类型限制(Enables strict type constraints)。这样通过使用layer.precision = trt.float32可以使该层类型严格为FP32。利于对全FP16模式下某些层溢出问题的定位。

FP16溢出问题分析

半精度浮点数是一种较新的浮点类型。NVIDIA在2002年初发布的Cg语言中将它称为half类型,并首次在2002年末发布的GeForce FX中实现。半精度浮点数使用2个字节(16位)来存储,这种数据类型只适合存储对精度要求不高的数字,不适合用来计算。其与8位或16位整数相比具有动态范围高的优点,可以使高对比度图片中更多细节得以保留。与单精度浮点数相比,它的优点是只需要一半的存储空间和带宽,但是会牺牲精度和数值范围。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

可以看到,如果使用FP16数据类型,由于最大值也仅为65504 ,在计算中很容易出现溢出问题。因此在对网络使用TensorRT的FP16模式加速时,需要特别注意部分计算的数值溢出问题,例如Pow, Sqrt等。

在PyTorch框架下,对于FP16的Tensor操作如果发生了溢出,则得到的结果为tensor(inf, dtype=torch.float16)

import torch

a = torch.tensor(1e3, dtype=torch.float16)
b = torch.tensor(1e-2, dtype=torch.float16)
c = a/b
print(c)

然而在TensorRT框架下,则会出现最终值为溢出值(错误结果)的问题。例如需要对1e3先平方再开方,如果使用默认的FP16计算则会得到一个错误的计算结果(平方后得到1e6>65504,发生溢出)。

out = input * torch.rsqrt(torch.mean(input ** 2, dim=1, keepdim=True) + 1e-8)

在发生这种问题时,可以通过除以一个scale(如1e3),将原始值缩小一定比例(=1),再做平方操作(=1),然后再进行开方操作(=1),最后将结果乘以scale(=1e3),所有的中间结果和最终结果均在FP16表示范围内,不会出现溢出风险。

fp16_div = 1e4
fp16_div_neg = 1e-4
input_div = input/fp16_div
out = input * torch.rsqrt(torch.mean(input_div ** 2, dim=1, keepdim=True) + 1e-8)*fp16_div_neg

因此,在使用TensorRT的FP16模式时,如果出现部分计算溢出问题,可以通过一些巧妙的操作使得整个计算过程不再溢出。

版权说明

本文为原创文章,独家发布在blog.csdn.net/TracelessLe。未经个人允许不得转载。如需帮助请email至tracelessle@163.com
在这里插入图片描述

参考资料

[1] Prototyping and Debugging Deep Learning Inference Models Using TensorRT’s ONNX-Graphsurgeon and Polygraphy Tools | NVIDIA On-Demand
[2] TensorRT/tools/Polygraphy at master · NVIDIA/TensorRT
[3] Polygraphy — Polygraphy 0.33.0 documentation
[4] 低精度表示用于深度学习训练与推断.pdf-深度学习文档类资源-CSDN文库
[5] TensorRT详细入门指北,如果你还不了解TensorRT,过来看看吧! - SegmentFault 思否
[6] 分享一些值的深入学习的关于AI部署的NVIDIA新技术 - 知乎
[7] ONNX转TensorRT加速模型推理_TracelessLe的专栏-CSDN博客
[8] IBuilderConfig — NVIDIA TensorRT Standard Python API Documentation 8.2.0 documentation
[9] 半精度浮点数 - 维基百科,自由的百科全书
[10] 基于Apex的混合精度加速:半精度浮点数FP16 - stardsd - 博客园
[11] DataType — NVIDIA TensorRT Standard Python API Documentation 8.2.0 documentation
[12] 深度学习算法优化系列二十 | TensorRT 如何进行细粒度的Profiling - GiantPandaCV专栏
[13] ONNX model produces different results in tensorRT 7 compared to pytorch and tensorRT 6 · Issue #1087 · NVIDIA/TensorRT
[14] A complete guide to AI accelerators for deep learning inference — GPUs, AWS Inferentia and Amazon Elastic Inference | by Shashank Prasanna | Towards Data Science
[15] Low Precision Inference with TensorRT | by Vignesh Ungrapalli | Towards Data Science
[16] 自动混合精度训练-使用文档-PaddlePaddle深度学习平台
[17] Achieving FP32 Accuracy for INT8 Inference Using Quantization Aware Training with NVIDIA TensorRT | NVIDIA Developer Blog
[18] FP16 weights out of range error · Issue #420 · NVIDIA/TensorRT
[19] (抛砖引玉)TensorRT的FP16不得劲?怎么办?在线支招! - 知乎
[20] Developer Guide :: NVIDIA Deep Learning TensorRT Documentation - set_layer_mp_python
[21] Layer Base Classes — NVIDIA TensorRT Standard Python API Documentation 8.0.1 documentation
[22] Developer Guide :: NVIDIA Deep Learning TensorRT Documentation - mixed_precision
[23] TensorRT/LayerDumpAndAnalyze - eLinux.org
[24] TensorRT/LayerDumpAndAnalyzeForINT8 - eLinux.org
[25] TensorRT/How2Debug - eLinux.org
[26] Warning: Encountered known unsupported method torch.rsqrt · Issue #485 · NVIDIA-AI-IOT/torch2trt

  • 7
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TracelessLe

❀点个赞加个关注再走吧❀

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值