cuda、cudann、tensorrt、torch2trt

1.cuda、cudann、tensorrt、torch2trt

对于具备了 root 权限的用户而言,在安装有多版本 cuda 的 Linux 系统上,只需切换 /usr/local/cuda 所指向的 cuda 目录,让其指向所需的 cuda 版本的安装位置,即可让 Pytorch 在运行时使用指定版本的 cuda 运行程序。修改软链接的方法如下命令所示,命令删除原有的软链接,并新建指向新路径的软链接。

或者直接强制修改原始的软链接

sudo ln -sf cuda_path /usr/local/cuda    //修改或创建软链接 /usr/local/cuda 使其指向指定版本的 cuda 目录
   export CUDA_HOME=/home/test/cuda-10.1/           //设置全局变量 CUDA_HOME
    export PATH=$PATH:/home/test/cuda-10.1/bin/        //在 PATH 变量中加入需要使用的 cuda 版本的路径,使得系统可以使用 cuda 提供的可执行文件,包括 nvcc

  想要永久设置上述 cuda 设置,用户可以直接在自己的 bash 设置文件 ~/.bashrc 文件尾部加入上述命令,保存后再通过 source ~/.bashrc 执行文件,即可完成当前终端的环境变量修改。如果需要使用新的 cuda 来编译文件,还可以通过 LD_LIBRARY_PATH 变量指定进行链接的 cuda 库文件的路径。

  img

  位于 ~/.bashrc 文件中的指令在每次终端启动时均会自动运行,后续本用户所打开的终端中的环境变量均会首先执行上述文件中的命令,从而获得对应的 cuda 变量。

查看cuda版本:

nvcc -V

cat /usr/local/cuda/version.txt

配置cudann:

cp cuda/include/cudnn.h /usr/local/cuda/include
cp cuda/lib64/libcudnn* /usr/local/cuda/lib64
chmod a+r /usr/local/cuda/include/cudnn.h
chmod /usr/local/cuda/lib64/libcudnn*

cp /mnt/dell/kangbowei/cuda/include/cudnn.h /usr/local/cuda-10.0/include

查看cudann版本:

cat /usr/local/cuda/include/cudnn_version.h | grep CUDNN_MAJOR -A 2

cp cudnn-10.0-linux-x64-v7.3.1.20.solitairetheme8 cudnn-10.0-linux-x64-v7.3.1.20.tgz
tar -xvf cudnn-10.0-linux-x64-v7.3.1.20.tgz

查看 CentOS 系统的版本

cat /etc/redhat-release

tensorrt设置环境变量:tensorrt安装注意版本兼容

export LD_LIBRARY_PATH=/mnt/dell/kangbowei/tensorRT/TensorRT-7.2.3.4/lib:$LD_LIBARARY_PATH

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/mnt/dell/kangbowei/tensorRT/TensorRT-7.2.3.4/lib

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/cuda-11.2/targets/x86_64-linux/lib/

修改软链接

ln -snf /usr/local/cuda-11.2 /usr/local/cuda

ln -sf libcudnn.so.8.1.0 libcudnn.so.8

ln -sf libcudnn.so.8 libcudnn.so

linux环境变量Linux环境变量配置全攻略 - 牛奔 - 博客园 (cnblogs.com)

用户级别环境变量定义文件:~/.bashrc~/.profile

系统级别环境变量定义文件:/etc/bashrc/etc/profile/etc/environment

系统找到不到cuda

export PATH=/usr/local/cuda/bin\({PATH:+:\){PATH}}

export LD_LIBRARY_PATH=/usr/local/cuda/lib64\({LD_LIBRARY_PATH:+:\){LD_LIBRARY_PATH}}

export CUDA_HOME=/usr/local/cuda

torch2trthttps://github.com/NVIDIA-AI-IOT/torch2trt

torch2trt报错AttributeError: module ‘torch.jit‘ has no attribute ‘_script_if_tracing‘

更换 torchversion版本到 0.4.0,输入如下命令
pip install torchvision==0.4.0 -f https://download.pytorch.org/whl/torch_stable.html

2.tensorRT

TensorRT是一个高性能的深度学习推理(Inference)优化器,可以为深度学习应用提供低延迟、高吞吐率的部署推理。ensorRT现已能支持TensorFlow、Caffe、Mxnet、Pytorch等几乎所有的深度学习框架,将TensorRT和NVIDIA的GPU结合起来,能在几乎所有的框架中进行快速和高效的部署推理。

TensorRT 是一个C++库,从 TensorRT 3 开始提供C++ API和Python API,主要用来针对 NVIDIA GPU进行 高性能推理(Inference)加速。现在最新版TensorRT是4.0版本。

目前TensorRT4.0 几乎可以支持所有常用的深度学习框架,对于caffe和TensorFlow来说,tensorRT可以直接解析他们的网络模型;对于caffe2,pytorch,mxnet,chainer,CNTK等框架则是首先要将模型转为 ONNX 的通用深度学习模型然后对ONNX模型做解析。而tensorflow和MATLAB已经将TensorRT集成到框架中去了。

ONNX(Open Neural Network Exchange )是微软和Facebook携手开发的开放式神经网络交换工具,也就是说不管用什么框架训练,只要转换为ONNX模型,就可以放在其他框架上面去inference。这是一种统一的神经网络模型定义和保存方式,上面提到的除了tensorflow之外的其他框架官方应该都对onnx做了支持,而ONNX自己开发了对tensorflow的支持

基本上比较经典的层比如,卷积,反卷积,全连接,RNN,softmax等,在tensorRT中都是有对应的实现方式的,tensorRT是可以直接解析的。

各种不同结构的自定义层(比如:STN)层出不穷,所以tensorRT是不可能全部支持当前存在的所有层的。那对于这些自定义的层该怎么办?

tensorRT中有一个 Plugin 层,这个层提供了 API 可以由用户自己定义tensorRT不支持的层。

TensorRT优化方法主要有以下几种方式,最主要的是前面两种。

层间融合或张量融合

数据精度校准

大部分深度学习框架在训练神经网络时网络中的张量(Tensor)都是32位浮点数的精度(Full 32-bit precision,FP32),一旦网络训练完成,在部署推理的过程中由于不需要反向传播,完全可以适当降低数据精度,比如降为FP16或INT8的精度。

TensorRT 使用流程

builder:构建器,搜索cuda内核目录以获得最快的可用实现,必须使用和运行时的GPU相同的GPU来构建优化引擎。在构建引擎时,TensorRT会复制权重。

engine:引擎,不能跨平台和TensorRT版本移植。若要存储,需要将引擎转化为一种格式,即序列化,若要推理,需要反序列化引擎。引擎用于保存网络定义和模型参数。

context:上下文,创建一些空间来存储中间值。一个engine可以创建多个context,分别执行多个推理任务。

runtime:用于反序列化引擎。

pytorch转tensorRT步骤:

  1. 使用pytorch训练得到pt文件;
  2. 将pt文件转换为onnx中间件;
  3. 使用onnxsim.simplify对转换后的onnx进行简化;
  4. 解析onnx文件构建trt推理引擎;
  5. 加载引擎执行推理,为引擎输入、输出、模型分配空间;
  6. 将待推理的数据(预处理后的img数据)赋值给inputs(引擎输入);
  7. 执行推理,拿到outputs;
  8. 对outputs后处理,根据构建引擎时的格式取出输出,reshape到指定格式(和torch推理后的格式一样);
load_model()  # 模型加载

export_onnx(model, batch_size)  # 模型转onnx

simplify_onnx(onnx_path)  # onnx模型简化

build_engine(onnx_path, using_half)  #ONNX模型转换为trt序列化模型

allocate_buffers(engine, is_explicit_batch=False, dynamic_shapes=[])  # 分配缓冲区,这一步是通用的

def profile_trt(engine, batch_size, num_warmups=10, num_iters=100):         # trt推理
    assert(engine is not None)  
    input_img_array = np.array([input_img] * batch_size)      # 输入

    yolo_inputs, yolo_outputs, yolo_bindings = allocate_buffers(engine, True)       # 内存分配
    
    stream = cuda.Stream()     # pycuda 操作缓冲区
    with engine.create_execution_context() as context:
        
        total_duration = 0.
        total_compute_duration = 0.
        total_pre_duration = 0.
        total_post_duration = 0.
        for iteration in range(num_iters):
            pre_t = time.time()
            # set host data
            img = torch.from_numpy(input_img_array).float().numpy()
            yolo_inputs[0].host = img         # 输入赋值到inputs
            #  Transfer data from CPU to the GPU.  将数据从CPU转移到GPU。
            [cuda.memcpy_htod_async(inp.device, inp.host, stream) for inp in yolo_inputs]
            # 线程同步
            stream.synchronize()
            start_t = time.time()
            # 执行模型推理
            context.execute_async_v2(bindings=yolo_bindings, stream_handle=stream.handle)
            stream.synchronize()
            end_t = time.time()
            # Transfer predictions back from the GPU.从GPU传回的传输预测。
            [cuda.memcpy_dtoh_async(out.host, out.device, stream) for out in yolo_outputs]
            stream.synchronize()
            post_t = time.time()

            duration = post_t - pre_t
            compute_duration = end_t - start_t
            pre_duration = start_t - pre_t
            post_duration = post_t - end_t
            if iteration >= num_warmups:
                total_duration += duration
                total_compute_duration += compute_duration
                total_post_duration += post_duration
                total_pre_duration += pre_duration
        
        print("avg GPU time: {}".format(total_duration/(num_iters - num_warmups)))
        print("avg GPU compute time: {}".format(total_compute_duration/(num_iters - num_warmups)))
        print("avg pre time: {}".format(total_pre_duration/(num_iters - num_warmups)))
        print("avg post time: {}".format(total_post_duration/(num_iters - num_warmups)))
        
        # 取出outputs,reshape成输出格式
        num_det = int(yolo_outputs[0].host[0, ...])
        boxes = np.array(yolo_outputs[1].host).reshape(batch_size, -1, 4)[0, 0:num_det, 0:4]
        scores = np.array(yolo_outputs[2].host).reshape(batch_size, -1, 1)[0, 0:num_det, 0:1]
        classes = np.array(yolo_outputs[3].host).reshape(batch_size, -1, 1)[0, 0:num_det, 0:1]
        
        return [np.concatenate([boxes, scores, classes], -1)]

profile_torch(model, using_half, batch_size, num_warmups=10, num_iters=100):         # 正常的torch推理,和trt推理做对比
posted @ 2022-05-23 22:59  killens  阅读(1112)  评论(0)    收藏  举报