TensorRT安装部署指南(Windows10)

TensorRT安装部署指南

时间:2023/11/01
说明:本指南针对在装有NVIDIA显卡的Windows10系统的计算机上,安装TensorRT推理加速工具,将pytorch中导出的onnx模型转换为trt模型文件,并在python语言中进行模型推理。


一、TensorRT安装

TensorRT官方安装指南:https://docs.nvidia.com/deeplearning/tensorrt/install-guide/index.html#installing-zip

注意

  1. 官方给出了pip、RPM、Tar文件、Zip文件等多种安装方式。需要注意,通过zip文件安装是目前 Windows 的唯一选项。Zip文件从官网下载。下载完成后,只需要按照官方安装指南中给出的步骤进行安装和环境配置。
  2. 安装TensorRT前需要确保已经安装对应的CUDA和cuDNN依赖。CUDA和cuDNN的安装方法可以参考:CUDA和cudnn安装教程

二、将ONNX模型转换为TensorRT引擎

将ONNX模型转换成TensorRT的最简单的方法就是使用{TensorRT}/bin下的命令行工具trtexec

trtexec --onnx=model.onnx --saveEngine=model.trt

这里的--onnx--saveEngine分别代表onnx模型的路径保存trt模型的路径。此外,再介绍两个比较常用的trtexec命令行工具参数:

  • --explicitBatch:告诉trtexec在优化时固定输入的 batch size(将从onnx文件中推断batch size的具体值,即与导出onnx文件时传入的batch size一致)。当确定模型的输入batch size时,推荐采用此参数,因为固定batch size大小可以使得trtexec进行额外的优化,且省去了指定“优化配置文件”这一额外步骤(采用动态batch size时需要提供“优化配置文件”来指定希望接收的可能的batch size大小的范围);
  • --fp16:采用FP16精度,通过牺牲部分模型准确率来简化模型(减少显存占用和加速模型推理)。TensorRT支持TF32/FP32/FP16/INT8多种精度(具体还要看GPU是否支持)。FP32是多数框架训练模型的默认精度,FP16对模型推理速度和显存占用有较大优化,且准确率损失往往可以忽略不计。INT8进一步牺牲了准确率,同时也进一步降低模型的延迟和显存要求,但需要额外的步骤来仔细校准,来使其精度损耗较小。

三、在Python API使用TensorRT模型进行推理

使用Python API运行TensorRT模型推理需要安装pycuda包:

pip install pycuda

注:若pycuda安装失败,尝试到https://www.lfd.uci.edu/~gohlke/pythonlibs/#pycuda 下载python版本对应的最新的本地安装文件安装
然后参照官方给的示例代码运行TensorRT模型推理:tutorial-runtime.ipynb
下面给出在python中执行tensorrt推理的主要代码:

import pycuda.driver as cuda
# import pycuda.autoinit	
# 如果不是在线程中运行,可以使用这行代码自动创建上下文,而不需要使用cuda.Device(0).make_context()手动创建上下文
import tensorrt as trt
cuda.init()


class HostDeviceMem(object):
    def __init__(self, host_mem, device_mem):
        """Within this context, host_mom means the cpu memory and device means the GPU memory
        """
        self.host = host_mem
        self.device = device_mem

    def __str__(self):
        return "Host:\n" + str(self.host) + "\nDevice:\n" + str(self.device)

    def __repr__(self):
        return self.__str__()


class TRTModel(object):
    def __init__(self, trt_path):
    	"""
    	在类初始化中预载入模型并申请锁页内存,避免每次推理重复载入和申请锁页内存,
    	前者会增加耗时,后者会导致显存占用持续增加
    	"""
    	# 手动创建context上下文
        self.cfx = cuda.Device(0).make_context()
        # 预加载模型
        self.engine = self.load_engine(trt_path)
        # 在CPU和GPU中申请锁页内存
        self.inputs, self.outputs, self.bindings, self.stream = self.allocate_buffers(self.engine)
        self.cfx.pop()

    def load_engine(self, engine_file_path):
        TRT_LOGGER = trt.Logger()
        assert os.path.exists(engine_file_path)
        print("Reading engine from file {}".format(engine_file_path))
        with open(engine_file_path, "rb") as f, trt.Runtime(TRT_LOGGER) as runtime:
            return runtime.deserialize_cuda_engine(f.read())

    def allocate_buffers(self, engine):
        """
        Allocate host(CPU) and device(GPU) buffers.
        """
        inputs = []
        outputs = []
        bindings = []
        stream = cuda.Stream()
        for binding in engine:
            binding_idx = engine.get_binding_index(binding)
            size = trt.volume(engine.get_binding_shape(binding_idx))
            dims = engine.get_binding_shape(binding)
            if dims[0] < 0:
                size *= -1
            dtype = trt.nptype(engine.get_binding_dtype(binding))
            # Allocate host and device buffers
            host_mem = cuda.pagelocked_empty(size, dtype)
            device_mem = cuda.mem_alloc(host_mem.nbytes)
            # Append the device buffer to device bindings.
            bindings.append(int(device_mem))
            # Append to the appropriate list.
            if engine.binding_is_input(binding):
                inputs.append(HostDeviceMem(host_mem, device_mem))
            else:
                outputs.append(HostDeviceMem(host_mem, device_mem))
        return inputs, outputs, bindings, stream

    def engine_infer(self, context):
        """
        engine: load_engine函数返回的trt模型引擎
        """
        # Transfer input data to the GPU.
        [cuda.memcpy_htod_async(inp.device, inp.host, self.stream) for inp in self.inputs]
        # Run inference
        context.execute_async_v2(bindings=self.bindings, stream_handle=self.stream.handle)
        # Transfer prediction output from the GPU.
        [cuda.memcpy_dtoh_async(out.host, out.device, self.stream) for out in self.outputs]
        # Synchronize the stream
        self.stream.synchronize()
        outputs = [out.host for out in self.outputs]
        return outputs

    def postprocess_the_outputs(self, output_host, shape):
        """
        Postprocess the outputs.
        Trt模型推理后输出1维数组,需要reshape回正常形式
        """
        output_host = output_host.reshape(*shape)
        return output_host

	def model_predict(self, data):
		"""
		1. 将输入数据传入GPU中申请的锁页内存
		2. 执行推理
		3. 取出一维的推理结果,并做后处理
		"""
		# 将输入数据传入在GPU中申请好的内存中
        self.inputs[0].host = np.ascontiguousarray(data)
        # 调用trt模型进行推理
        self.cfx.push()
        with self.engine.create_execution_context() as context:
            trt_outputs = self.engine_infer(context)
        self.cfx.pop()
        # 后处理output
        shape = (data.shape[0], num_classes, data.shape[2], data.shape[3])
        trt_output = self.postprocess_the_outputs(trt_outputs[0], shape=shape)
        return trt_output

注意事项

(1)TensorRT是硬件相关的

不同显卡(不同GPU),其核心数量、频率、架构、设计都是不一样的,TensorRT需要对特定的硬件进行优化,不同硬件之间的优化是不能共享的。

(2)TensorRT支持哪几种权重精度

支持FP32、FP16、INT8、TF32等,这几种类型都比较常用。

  • FP32:单精度浮点型,深度学习中最常见的数据格式,训练推理都会用到;
  • FP16:半精度浮点型,相比FP32占用内存减少一半,有相应的指令值,速度比FP32要快很多;
  • TF32:第三代Tensor Core支持的一种数据类型,是一种截短的 Float32 数据格式,将FP32中23个尾数位截短为10bits,而指数位仍为8bits,总长度为19(=1+8 +10)。保持了与FP16同样的精度(尾数位都是 10 位),同时还保持了FP32的动态范围指数位都是8位);
  • INT8:整型,相比FP16占用内存减小一半,有相应的指令集,模型量化后可以利用INT8进行加速。
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

本初-ben

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值