模型压缩--剪枝,tensorrt实验调研

https://www.cnblogs.com/yanghailin/p/14213464.html
未经允许不得转载。
最近在搞模型压缩方面的一些东西,初步调研下来感觉要学的,要看的,要实验的很多很多啊,无底洞啊。
这里是初步记录,因为有些东西最近看了,先暂停了一部分工作又去搞其他的了,那么这些天调研实验的东东后面就会忘记。
先是整的模型剪枝方面,有很多论文,其中一篇2017的论文,Learning Efficient Convolutional Networks through Network Slimming。
将L1正则化施加到BN层的缩放因子上,L1正则化推动BN层的缩放因子趋向于零,这使得我们能够鉴别出不重要的通道或者神经元,因为每一个缩放因子都和一个特定的CNN卷积通道(或者全连接层的一个神经元)相关联。这有助于后续的通道剪枝,另外正则化也很少损伤性能,甚至一些情况下它会导致更高的泛化准确率,剪掉不重要的通道有时候虽然会暂时降低性能,但是通过之后对剪枝网络的微调可以对精度补偿。在剪枝之后,更窄的网络会在模型大小、运行阶段的内存占用、计算量方面都会更显得紧凑。将上面的过程重复几次,就可以通过多阶段的网络瘦身获得更紧凑的模型。


通过正则化bn系数,训练到后面通道贡献较大的值就大,反之就小,就是根据这个系数后面把趋于0的通道直接不要,达到剪枝的目的。
但是网络是复杂的,上下层之间通道数量都是关联的,还有shortcut连接,所以其实实际代码操作起来还是蛮复杂的,当然github上面已经有人根据自己网络实现了。
这里推荐两个github链接实现。
https://github.com/BADBADBADBOY/pse-lite.pytorch
https://github.com/Lam1360/YOLOv3-model-pruning
这两个我都跑过。
其中pse-lite.pytorch里面有大坑,这个确实是可以跑通,保存的模型原始是114M,剪枝之后我是51M,但是!!用剪枝之后的模型推理,显存反而变大!!!弄了很久,还是这个样子,把网络打印出来,也显示网络的通道数减少了,但是就是显存变大啊,问了作者,作者也不知道,然后我提了issue,也有其他人也遇到和我一样的问题。
https://github.com/BADBADBADBOY/pse-lite.pytorch/issues/2
这个人怀疑是torch可能哪里有bug,我怀疑是剪枝之后的通道数不是2的次方数,torch内部做卷积是不是哪里自动优化调整,具体不知道,太难了。。。
然后这个就搁置了。
YOLOv3-model-pruning这个我跑了,模型大小,显存确实能降低,精度下降了5个点,问题不大,可能哪里没有训练好,然后在研究里面的实现细节,由于yolov3代码之前没有研究过,需要时间。他里面实现的还蛮仔细的,把bn的y=kx+b
就是比如k=0,还会把偏置b转移到其他层。代码值得学习。
准备研究透彻之后就可以对我们自己的网络用这套。感觉是可行的路子,就网络通道数变化了,部署还是和之前一样。

然后因为领导让研究量化,上面这些一时半会也弄不好,然后又去调研量化了,发现量化大部分都是基于tensorrt的啊,又是一个大坑啊。需要看onnx和tensorrt。各种版本,各种环境,一开始无从下手,然后看各种教程。看的都凌乱了,任何东西拿过来跑一跑,就有个初步认识。github是个好东西啊!
github一搜tensorrt,可以看到有很多实现,其中最多的yolo,然后我还看到了centernet,因为之前把centernet研究完了,对这个比较熟悉。
https://github.com/CaoWGG/TensorRT-CenterNet
作者给出的环境就是

pytorch 1.0-1.1
ubuntu 1604
TensorRT 5.0
onnx-tensorrt v5.0
cuda 9.0

这里需要先conda创建环境,我这里环境是torch1.0.1.post2,python3.5,cuda10.0

可是我是cuda10,先不管把,然后就直接配置TensorRT 5.0
先去官网下载对应压缩包,最新版本是7,我们这里用5
https://developer.nvidia.com/nvidia-tensorrt-download
https://developer.nvidia.com/nvidia-tensorrt-5x-download
我选择的如下版本下载的
TensorRT 5.0.2.6 GA for Ubuntu 16.04 and CUDA 10.0 tar package
安装步骤参考:https://www.jianshu.com/p/eb18fe0caa9d

cd /python/
pip install tensorrt-5.0.2.6-py2.py3-none-any.whl
pip install pycuda

验证:先输入python,然后输入import tensorrt及import pycuda
我一开始报错的,后来根据错误百度,说只支持py3.5或者py2.7.因为我python是3.6的,然后重新配置了3.5的conda环境。
我conda环境配置如下:

Package           Version     
----------------- ------------
albumentations    0.4.6       
anyconfig         0.9.11      
appdirs           1.4.4       
certifi           2020.6.20   
cffi              1.11.5      
chardet           3.0.4       
click             7.1.2       
cycler            0.10.0      
Cython            0.29.21     
decorator         4.4.2       
easydict          1.7         
editdistance      0.5.3       
Flask             1.1.2       
gevent            20.9.0      
gevent-websocket  0.10.1      
greenlet          0.4.17      
idna              2.10        
imageio           2.9.0       
imgaug            0.4.0       
itsdangerous      1.1.0       
Jinja2            2.11.2      
joblib            0.14.1      
json-tricks       3.15.2      
jsonpatch         1.26        
jsonpointer       2.0         
Mako              1.1.3       
MarkupSafe        1.1.1       
matplotlib        2.0.2       
mkl-fft           1.0.6       
mkl-random        1.0.1       
munch             2.5.0       
networkx          2.4         
ninja             1.10.0.post1
numpy             1.13.3      
olefile           0.46        
onnx              1.1.1       
pandas            0.25.3      
Pillow            5.2.0       
pip               10.0.1      
Polygon3          3.0.8       
pretrainedmodels  0.7.4       
protobuf          3.12.1      
pyclipper         1.2.0       
pycocotools       2.0         
pycparser         2.20        
pycuda            2019.1.2    
pyparsing         2.4.7       
python-dateutil   2.8.1       
pytools           2020.3      
pytz              2020.1      
PyWavelets        1.1.1       
PyYAML            5.3.1       
pyzmq             19.0.2      
requests          2.24.0      
scikit-image      0.15.0      
scikit-learn      0.22.2.post1
scipy             0.19.1      
setuptools        40.2.0      
Shapely           1.6.4       
six               1.11.0      
sklearn           0.0         
sortedcontainers  2.3.0       
tabulate          0.8.7       
TBB               0.1         
tensorboardX      2.1         
tensorrt          5.0.2.6     
terminaltables    3.1.0       
torch             1.0.1.post2 
torchfile         0.1.0       
torchvision       0.2.1       
tornado           6.0.4       
tqdm              4.48.2      
typing-extensions 3.7.4.2     
urllib3           1.25.10     
visdom            0.1.8.9     
websocket-client  0.57.0      
Werkzeug          1.0.1       
wget              3.2         
wheel             0.31.1      
yacs              0.1.8       
zope.event        4.5.0       
zope.interface    5.2.0       
You are using pip version 10.0.1, however version 20.3.3 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.

然后安装onnx-tensorrt v5.0
我也不知道这个玩意是干啥的,随便根据网上的安装教程配置,但是报错,不知道咋解决,然后也没有解决,直接跑代码。后来问了作者,作者说这个onnx-tensorrt v5.0不需要配置,下载下来的TensorRT-CenterNet-master文件夹下面本身就有个onnx-tensorrt。
然后编译工程。
cmakelist我就改了一个路径:

cmake_minimum_required(VERSION 3.5)
project(ctdet_trt)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})

set(CMAKE_BUILD_TYPE Debug)
set(GPU_ARCHS 61)  ## config your GPU_ARCHS,See [here](https://developer.nvidia.com/cuda-gpus) for finding what maximum compute capability your specific GPU supports.
#set(TENSORRT_ROOT /usr/local/TensorRT-5.0.2.6) ############
#set(TENSORRT_ROOT /usr/lib) ########
set(TENSORRT_ROOT /data_1/everyday/1228/TensorRT-5.0.2.6) ########
## build
add_subdirectory(onnx-tensorrt)
add_subdirectory(src)
add_subdirectory(example)

然后按照作者提供的步骤编译:

cd TensorRT-CenterNet
mkdir build
cd build && cmake .. && make
cd ..

##ctdet | config include/ctdetConfig.h 
## float32
./buildEngine -i model/ctdet_coco_dla_2x.onnx -o model/ctdet_coco_dla_2x.engine 
./runDet -e model/ctdet_coco_dla_2x.engine -i test.jpg -c test.h264

##cthelmet   | config include/ctdetConfig.h
## flaot32
./buildEngine -i model/ctdet_helmet.onnx -o model/ctdet_helmet.engine -m 0
./runDet -e model/ctdet_helmet.engine -i test.jpg -c test.h264

一开始也是无厘头一顿操作,
但是运行
./buildEngine -i model/ctdet_coco_dla_2x.onnx -o model/ctdet_coco_dla_2x.engine
就直接报段错误,其他什么都不提示,也不知道怎么排查原因,真是头大,无从下手,然后用gdb,不会用,不知道怎么用gdb,然后,用clion,把工程拖到clion里面跑,就在cmakelist里面改成Debug,然后居然可以跑了,没有报错,也是奇怪。

我这里是1080卡,显存占用才349M,时间2-3ms,真优秀!!!
这里有个地方要注意的是跑哪个就需要把头文件配置改下,上面作者也写了config include/ctdetConfig.h
然后准备跑自己训练好的数据,之前训练过没有dcn的25类的模型。参考作者提供的教程,
https://github.com/CaoWGG/TensorRT-CenterNet/blob/master/readme/ctdet2onnx.md
这个代码是在原始的CenterNet-master工程里面跑的,但是这里需要改一些东西,因为不用dcn,需要把和dcn相关的代码去掉,然后一定是需要python3.5
我自己命名的pth2onnx_small25.py

from lib.opts import opts
from lib.models.model import create_model, load_model
from types import MethodType
import torch.onnx as onnx
import torch
from torch.onnx import OperatorExportTypes
from collections import OrderedDict
## onnx is not support dict return value
## for dla34
def pose_dla_forward(self, x):
    x = self.base(x)
    x = self.dla_up(x)
    y = []
    for i in range(self.last_level - self.first_level):
        y.append(x[i].clone())
    self.ida_up(y, 0, len(y))
    ret = []  ## change dict to list
    for head in self.heads:
        ret.append(self.__getattr__(head)(y[-1]))
    return ret
## for dla34v0
def dlav0_forward(self, x):
    x = self.base(x)
    x = self.dla_up(x[self.first_level:])
    # x = self.fc(x)
    # y = self.softmax(self.up(x))
    ret = []  ## change dict to list
    for head in self.heads:
        ret.append(self.__getattr__(head)(x))
    return ret
## for resdcn
def resnet_dcn_forward(self, x):
    x = self.conv1(x)
    x = self.bn1(x)
    x = self.relu(x)
    x = self.maxpool(x)

    x = self.layer1(x)
    x = self.layer2(x)
    x = self.layer3(x)
    x = self.layer4(x)
    x = self.deconv_layers(x)
    ret = []  ## change dict to list
    for head in self.heads:
        ret.append(self.__getattr__(head)(x))
    return ret

forward = {'dla':pose_dla_forward,'dlav0':dlav0_forward,'resdcn':resnet_dcn_forward}

path_model = "/data_2/project_202009/pytorch_project/CenterNet/000000experiment_2020/0_1112/CenterNet-master_objvehicle_small_new_test/myfile/save_model/1123/small/model_last.pth"
opt = opts().init()  ## change lib/opts.py add_argument('task', default='ctdet'....) to add_argument('--task', default='ctdet'....)
opt.arch = 'dlav0_34'   #'dla_34'
opt.heads = OrderedDict([('hm', 25), ('reg', 2), ('wh', 2)])
opt.head_conv = 256 if 'dla' in opt.arch else 64
print(opt)
model = create_model(opt.arch, opt.heads, opt.head_conv)
model.forward = MethodType(forward[opt.arch.split('_')[0]], model)
load_model(model, path_model)
model.eval()
model.cuda()
input = torch.zeros([1, 3, 512, 512]).cuda()
onnx.export(model, input, "small.onnx", verbose=True,
            operator_export_type=OperatorExportTypes.ONNX)

这个果真可以生成了small.onnx。
然后

./buildEngine -i model/small.onnx -o model/small.engine 

这个果真生成了small.engine,然后在头文件/TensorRT-CenterNet-master/include/ctdetConfig.h仿照写好的,写mean,std,类别数量和类别名。
重新编译工程,运行

./runDet -e model/small.engine -i test.jpg -c test.h264

果真可以,运行这个显存551M,时间10ms!

然后仔细阅读这个代码,感觉作者好牛逼,各种cuda编程,cmakelist啊。
再次感谢一下作者开源如此优秀的代码!

然后找资料学习tensorrt,越看越不懂。
跟着这个链接跑通了TensorRT-5.0.2.6下面的yolov3的demo。
https://www.cnblogs.com/shouhuxianjian/p/10550262.html
但是期间也是有报错,下载不了,然后就手动下载。
还有其他错误忘记了
一定需要python2.7

├── coco_labels.txt
├── data_processing.py
├── data_processing.pyc
├── dog_bboxes.png
├── dog.jpg
├── onnx_to_tensorrt.py
├── README.md
├── requirements.txt
├── yolov3.cfg
├── yolov3.cfg1
├── yolov3.onnx
├── yolov3_to_onnx.py
├── yolov3_to_onnx.pyc
├── yolov3.trt
└── yolov3.weights

这个链接是整个流程,把pytorch模型转onnx再转.trt模型推理。不过是python的,值得学习。
然后看了TensorRT-5.0.2.6/samples这个文件夹下面demo,c++的代码,和上面看的centernet代码差不多,作者应该就是看懂了这里面的demo再写centernet的。
恩!需要好好研究研究!!

原文地址:https://www.cnblogs.com/yanghailin/p/14213464.html