1. 程式人生 > 實用技巧 >模型壓縮--剪枝,tensorrt實驗調研

模型壓縮--剪枝,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的。
恩!需要好好研究研究!!