• TensorRT学习笔记(一)——使用Python API调用TensorRT


    前言

    作为在英伟达自家GPU上的推理库,TensoRT仍然是使用英伟达显卡部署方案的最佳选择。TensorRT虽然支持Pytho和C++调用,但是在TensorRT8之前,python api只能在linux上使用,直到TensorRT8才支持python api在window下使用。
    具体安装在官方文档都有说明,附上官方文档:TensorRT官方文档

    参考文章:

    1. TensorRT 7.2.2 下载、安装、使用(ubuntu 18.04, cuda 10.2, cudnn 8.0.5
    2. ImportError: libnvinfer.so.7: cannot open shared object file
    3. TensorRT使用教程(Python版)_ZhouJianGuos的博客-程序员宝宝_tensorrt使用教程
    4. 【踩坑实录】TensorRT 傻瓜式部署流程

    安装

    首先需要进入tensorrt 官网: https://developer.nvidia.com/tensorrt
    点击“立即下载”,需要英伟达账号,可以选择任意平台适配你的cuda版本的TensorRT.
    注意:安装tensorrt需要先安装好配套的cuda和cudnn。请根据自己的硬件配置自行安装cuda和cudnn

    1. 下载并解压tar文件
    tar -zxvf TensorRT-8.4.1.5.Linux.x86_64-gnu.cuda-11.6.cudnn8.4.tar.gz
    
    • 1
    1. 进入到解压路径下的python文件夹,使用pip安装
    cd TensorRT-8.4.1.5/python
    # 选择自己的python版本进行安装
    pip3 install tensorrt-8.4.1.5-cp36-none-linux_x86_64.whl
    
    • 1
    • 2
    • 3
    1. 安装uff以支持tensorflow
    cd TensorRT-8.4.1.5/uff
    pip install uff-0.6.9-py2.py3-none-any.whl
    
    • 1
    • 2
    1. 安装graphsurgeon以支持自定义结构
    cd TensorRT-8.4.1.5/graphsurgeon
    pip install graphsurgeon-0.4.6-py2.py3-none-any.whl
    
    • 1
    • 2
    1. onnx_graphsurgeon以支持onnx
    cd TensorRT-8.4.1.5/onnx_grahsurgeon
    pip install onnx_graphsurgeon-0.3.12-py2.py3-none-any.whl
    
    • 1
    • 2
    1. 修改环境变量
      把刚才解压的TensorRT文件夹路径写进 ~/.bashrc中
    #使用以下命令打开.bashrc文件
    sudo gedit ~/.bashrc
    
    #解压得到TensorRT-7.2.1.6的文件夹,将里边的lib绝对路径添加到环境变量中,用户名和TensorRT的版本号更换为自己的
    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/youpath/TensorRT-8.4.1.5/lib
    
    #保存文件并退出,执行以下命令,使路径生效
    source ~/.bashrc
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    如果这个时候发现在终端调用tensorrt没有问题,但在pycharm里运行时,提示找不到libnvinfer.so.7(或者缺少其他so包,这些包能在lib下找到)。虽然在bashrc中设置了环境变量,但是也没有对pycharm起到作用,出现如下报错:

    Traceback (most recent call last):
    File "/media/lindsay/591d8930-456f-45c2-9059-4e020abd1169/tensorrtx/yolov5/streams_trt.py", line 15, in <module>
      import tensorrt as trt
    File "/home/lindsay/anaconda3/envs/yolov5/lib/python3.8/site-packages/tensorrt/__init__.py", line 66, in <module>
      from .tensorrt import *
    ImportError: libnvinfer.so.7: cannot open shared object file: No such file or directory
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    可以参考这篇文章:ImportError: libnvinfer.so.7: cannot open shared object file 在pycharm进行设置。

    Python API 调用

    1. 导出ONNX文件(Pytorch为例)

     # 构造模型实例
     model = HRNet()
     # 反序列化权重参数
     model.load_state_dict(torch.load(self.weight_path),strict=False)
     model.eval()
     # 定义输入名称,list结构,可能有多个输入
     input_names = ['input']
     # 定义输出名称,list结构,可能有多个输出
     output_names = ['output']
     # 构造输入用以验证onnx模型的正确性
     input = torch.rand(1, 3, self.resolution[0], self.resolution[1])
     # 导出
     torch.onnx.export(model, input, output_path,
                              export_params=True,
                              opset_version=11,
                              do_constant_folding=True,
                              input_names=input_names,
                              output_names=output_names)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    参数介绍:

    • model为pytorch模型实例
    • input为测试输入数据(形状必须要和模型输入一致,但是数值可以是随机的)
    • output_path为导出路径,xxx.onnx
    • export_params为是否导出参数权重,必然是True
    • opset_version=11 发行版本,11就可以了
    • do_constant_folding是否对常量进行折叠,True就可以了
    • input_names是模型输入的名称,list类型,因为模型可能有多个输入
    • output_names同上,只不过这是针对输出的

    2. trtexec.exe实现预推理

    打开tensorrt安装包的子目录bin,里面有一个trtexec文件,trtexec 是 TensorRT sample 里的一个例子,时把 TensorRT 许多方法包装成了一个可执行文件。它可以把模型优化成 TensorRT Engine ,并且填入随机数跑 inference 进行速度测试。
    输入以下指令:

    trtexec --onnx=xxx.onnx --saveEngine=xxx.engine
    
    • 1

    这个命令把 onnx 模型优化成 Engine ,然后多次 inference 后统计并报时。
    报时会报很多个, Enqueue Time 是 GPU 任务排队的时间,H2D Latency 是把网络输入数据从主存 host 拷贝进显存 device 的时间,D2H Latency 是把显存上的网络输出拷贝回主存的时间,只有 GPU Compute Time 是真正的网络 inference 时间。

    一般来说模型的第一维大小是任意的(batch size 维度),而 TensorRT 不能把任意 batch size 都加速。可以指定一个输入范围,并且重点优化其中一个 batch size。例如网络输入格式是 [-1, 3, 244, 244]:

    ./trtexec --onnx=model.onnx --minShapes=input:1x3x244x244 --optShapes=input:16x3x244x244 --maxShapes=input:32x3x244x244 --shapes=input:5x3x244x244
    
    • 1

    还可以降低精度优化速度。一般来说大家写的模型内都是 float32 的运算,TensorRT 会默认开启 TF32 数据格式,它是截短版本的 FP32,只有 19 bit,保持了 fp16 的精度和 fp32 的指数范围。

    另外,TensorRT 可以额外指定精度,把模型内的计算转换成 float16 或者 int8 的类型,可以只开一个也可以两个都开,trtexec 会倾向于速度最快的方式(有些网络模块不支持 int8)

    ./trtexec --onnx=model.onnx --saveEngine=xxx.trt --int8 --fp16
    
    • 1

    一般来说,只开 fp16 可以把速度提一倍并且几乎不损失精度;但是开 --int8 会大大损失精度,速度会比 fp16 快,但不一定能快一倍。

    int8 优化涉及模型量化,需要校准(calibrate)提升精度。TensorRT 有两种量化方法:训练后量化和训练中量化。二者的校准方法不同,精度也不同,后者更高一些。具体参考 Developer Guide :: NVIDIA Deep Learning TensorRT Documentation

    trtexec 采用的是训练后量化,写起来更方便一些。不过看看源码就能发现,因为 trtexec 只为了测试速度,所以校准就象征性做了一下,真想自己部署 int8 模型还得自己写校准。

    常用参数解释

    • – onnx onnx路径
    • – saveEngine执行计划(推理引擎)序列化地址
    • – fp16开启 float16精度的推理(推荐此模式,一方面能够加速,另一方面精度下降比较小)
    • – int8 开启 int8精度的推理(不太推荐,虽然更快,但是精度下降太厉害了)

    使用 TensorRT Python API

    简单版本

    这个版本借助了torch2trt这个库,已经把推理代码的细节封装好了。这个仓库可以实现TensorRT和Pytorch的链接,具体信息请看仓库的更多介绍:https://github.com/NVIDIA-AI-IOT/torch2trt

    import tensorrt as trt
    from torch2trt import TRTModule
    
    # 加载日志记录器
    logger = trt.Logger(trt.Logger.INFO)
    # 加载engine
    with open("xxx.trt", "rb") as f, trt.Runtime(logger) as runtime:
      engine=runtime.deserialize_cuda_engine(f.read())
    
    net = TRTModule(engine, input_names=["input"], output_names=['output'])
    
    # 推理
    image = cv2.imread(image_path)
    image = cv2.resize(image, (224,224))
    image = image.transpose(2,0,1)
    img_input = image.astype(np.float32)
    img_input = torch.from_numpy(img_input)
    img_input = img_input.unsqueeze(0)
    img_input = img_input.to(device)
    # 运行模型
    result_trt = trt_model(img_input)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    整个流程都跟pytorch流程差不多,输入进去给网络的也是pytorch的张量,得到的结果也是pytorch的张量。

    原始版本

    #导入必用依赖
    import tensorrt as trt
    import pycuda.autoinit  #负责数据初始化,内存管理,销毁等
    import pycuda.driver as cuda  #GPU CPU之间的数据传输
    
    #创建logger:日志记录器
    logger = trt.Logger(trt.Logger.WARNING)
    #创建runtime并反序列化生成engine
    with open(“sample.engine”, “rb”) as f, trt.Runtime(logger) as runtime:
        engine = runtime.deserialize_cuda_engine(f.read())
    
    # 分配空间并绑定输入输出
    host_inputs = []
    cuda_inputs = []
    host_outputs = []
    cuda_outputs = []
    bindings = []
    for binding in engine:
        size = trt.volume(engine.get_binding_shape(binding)) * engine.max_batch_size
        dtype = trt.nptype(engine.get_binding_dtype(binding))
        # 分配主机和设备buffers
        host_mem = cuda.pagelocked_empty(size, dtype)    
        cuda_mem = cuda.mem_alloc(host_mem.nbytes)      
        # 将设备buffer绑定到设备.
        bindings.append(int(cuda_mem))
        # 绑定到输入输出
        if engine.binding_is_input(binding):
             host_inputs.append(host_mem)           
             cuda_inputs.append(cuda_mem)    
        else:
             host_outputs.append(host_mem)
             cuda_outputs.append(cuda_mem)
    
    #创建cuda流
    stream = cuda.Stream()
    
    # 拷贝输入图像到主机buffer
    np.copyto(host_inputs[0], input_image.ravel())
    # 将输入数据转到GPU.
    cuda.memcpy_htod_async(cuda_inputs[0], host_inputs[0], stream)
    # 推理.
    context.execute_async(bindings=bindings, stream_handle=stream.handle)
    # 将推理结果传到CPU.
    cuda.memcpy_dtoh_async(host_outputs[0], cuda_outputs[0], stream)
    # 同步 stream
    stream.synchronize()
    # 拿到推理结果 batch_size = 1
    output = host_outputs[0]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
  • 相关阅读:
    设备巡检管理系统有什么用?企业如何提高生产效率和生产安全?
    haproxy参数选择问题
    Linux 基础 之 用户管理
    顾问们眼中的顾问
    【生命周期】
    Re:从零开始的C++世界——类和对象(中)
    ASP.NET添加MVC控制器报错
    【MySQL8入门到精通】基础篇- Linux系统静默安装MySQL,跨版本升级
    uni-app 开发的H5 定位功能部署注意事项
    马斯克打了个响指,推特50%员工被裁....
  • 原文地址:https://blog.csdn.net/weixin_41693877/article/details/122069565