一、环境搭建
ubantu20.04搭建cuda11.3+TensorRT8.4.15+opencv4.5.5
1、TensorRT:下载TensorRT
解压到/usr/local下面,将tensorrt的头文件和库文件加入到环境变量中,配置~/.bashrc文件
vim ~/.bashrc
# 配置环境如下:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/TensorRT-8.4.1.5/lib
export C_INCLUDE_PATH=$C_INCLUDE_PATH:/usr/local/TensorRT-8.4.1.5/include
export CPLUS_INCLUDE_PATH=$CPLUS_INCLUDE_PATH:/usr/local/TensorRT-8.4.1.5/include
# 使配置生效
source ~/.bashrc
# 验证是否配置成功,进入 /usr/local/TensorRT-8.4.1.5/samples/sampleOnnxMNIST
sudo make
出现如下即配置成功:
2:OpenCV:下载opencv
# 安装依赖
sudo apt-get install build-essential
sudo apt-get install cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev
sudo apt-get install python-dev python-numpy libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff-dev libjasper-dev libdc1394-22-dev
#在build下cmake,OpenCV4以上默认不使用pkg-config,该编译选项yes表示生成opencv4.pc文件,支持pkg-config功能
cd opencv-4.5.5
mkdir build
cd build
sudo cmake -D CMAKE_BUILD_TYPE=Release -D OPENCV_GENERATE_PKGCONFIG=YES ..
# 编译,时间有点久
sudo make -j8
# 安装
sudo make install
# 修改opencv4.pc名字,不然后面不识别
sudo mv /usr/local/lib/pkgconfig/opencv4.pc /usr/local/lib/pkgconfig/opencv.pc
# 配置环境
vim ~/.bashrc
export PKG_CONFIG_PATH=~/usr/local/lib/pkgconfig
# 生效配置、查看版本
source ~/.bashrc
pkg-config --modversion opencv
3.Protobuf安装:
# 先下载好3.11.4版本的压缩包
cd /usr/local/protobuf-3.11.4/
# 安装必要的库
sudo apt-get install autoconf automake libtool curl make g++ unzip
# 生成配置文件
./autogen.sh
# 配置环境、编译、安装
./configure --prefix=/usr/local/protobuf-3.11.4
make
make check
sudo make install
# 更改环境
vim ~/.bashrc
export PATH=/usr/local/protobuf-3.11.4/bin:$PATH
export PKG_CONFIG_PATH=/usr/local/protobuf-3.11.4/lib/pkgconfig/
# 查看版本
protoc --version
二、如何正确导出ONNX
- 对于任何用到shape、size返回值的参数时,例如:tensor.view(tensor..size(0),-1)这类操作,避免直接使用tensor.size的返回值,而是加上int转换,tensor.view(int(tensor.size(0)),-1)
- 对于nn.Upsample或nn.functional.interpolate函数,使用scale_factor指定倍率,而不是使用size参数指定大小
- 对于reshape、view操作时,-1的指定请放到batch维度。其他维度可以计算出来即可。batch维度禁止指定为大于-1的明确数字
- torch.onnx.export指定dynamic_axes参数,并且只指定batch维度,不指定其他维度。我们只需要动态batch,相对动态的宽高有其他方案
- 使用Opset_Version=11,不要低于11,(低于的话为unsample,不是resize)
- 避免使用inplace操作
这些做法的必要性体现在,简化过程的复杂度,去掉gather、shape类的节点。
例如:将reshape的batch的维度指定为-1
# bs, _, ny, nx = x[i].shape # x(bs,255,20,20) to x(bs,3,20,20,85)
bs, _, ny, nx = map(int,x[i].shape)
# x[i] = x[i].view(bs, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous()
x[i] = x[i].view(-1, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous()
# z.append(y.view(-1, int(y.size(1)*y.size(2)*y.size(3)), self.no))
z.append(y.view(-1, int(torch.prod(torch.tensor(y.shape[1:-1]))), self.no))
去除多余的输出:
return x if self.training else torch.cat(z, 1)
三、高性能注意点
单模型推理时的性能问题:
- 尽量使得GPU高密集度运行,避免出现CPU、GPU相互交换运行
- 尽可能使tensorRT:运行多个batch数据。与第一点相合
- 预处理尽量cuda化,例如图像需要做normalize、reisze、warpaffine、bgr2rgb等,在这里,采用cuda核实现warpaffine+normalize等操作,集中在一起性能好
- 后处理尽量cuda化,例如decode、nms等。在这里用cuda核实现了decode和nms
- 善于使用cudaStream,将操作加入流中,采用异步操作避免等待
- 内存复用
系统级别的性能问题:
- 如何实现尽可能让单模型使用多batch,此时future、promise就是很好的工具
- 时序图要尽可能优化,分析并绘制出来,不必的等待应该消除,同样是promise、future带来的好处
- 尤其是图像读取和模型推理最常用的场景下,可以分析时序图,缓存一帧的结果,即可实现帧率的大幅提升