深度学习模型剪枝、量化和TensorRT推理
模型剪枝算法
- 参考文献:Rethinking the Value of Network Pruning (ICLR 2019)
- github:https://github.com/Eric-mingjie/rethinking-network-pruning
Rethinking the Value of Network Pruning这篇文献主要介绍了以下几种剪枝算法,并在github上开源了代码,在ImageNet和cifar两个数据集上进行了测试,论文也验证了对比了对剪枝模型进行fine tune和根据剪枝后的网络结构从头训练网络的准确率,在作者提供的准确率中,一般情况下,对于分类网络,从头训练的准确率比剪枝后fine tune后的准确率高。
预定义网络结构剪枝
剪枝方法 | 参考文献 | github | 剪枝原理 |
---|---|---|---|
L1-norm based Filter Pruning | Pruning Filters for Efficient ConvNets | l1-norm-pruning | 在每一个卷积层,根据卷积核的权重值的L1 norm大小,修剪掉相应百分比的通道数。 |
ThiNet | ThiNet: A Filter Level Pruning Method for Deep Neural Network Compression | https://github.com/Roll920/ThiNet | 剪枝时,不根据当前层进行剪枝,而是修剪去对下一卷积层激活值影响最小的权重值。 |
Regression based Feature Reconstruction | Channel Pruning for Accelerating Very Deep Neural Networks | https://github.com/yihui-he/channel-pruning | 根据LASSO回归算法选出有代表性的卷积层通道,减枝去掉冗余通道,使用最小二乘法重建剩余通道。 |
自动网络结构剪枝
剪枝方法 | 参考文献 | github | 剪枝原理 |
---|---|---|---|
Network Slimming | (1) Learning Efficient Convolutional Networks through Network Slimming (2) SlimYOLOv3: Narrower, Faster and Better for Real-Time UAV Applications | (1) network-slimming (2) https://github.com/PengyiZhang/SlimYOLOv3 | 在剪枝时,对BN层的gamma参数进行惩罚进行稀疏训练,剪枝时选择稀疏训练后BN层gamma值较大的参数进行保留 |
Sparse Structure Selection | Data-Driven Sparse Structure Selection for Deep Neural Networks | https://github.com/TuSimple/sparse-structure-selection | 除了对通道进行稀疏训练,还可以对残差模块进行稀疏训练。 |
- 稀疏训练代码
def updateBN():
for m in model.modules():
if isinstance(m, nn.BatchNorm2d):
m.weight.grad.data.add_(args.s*torch.sign(m.weight.data))
- 注意:剪枝时对残差模块通道的剪枝策略需要考虑,方案有不剪枝(减去的参数较少)或者根据残差模块的某一层进行剪枝,也有对所有残差通道的mask进行或运算。剪枝后的效果如下图所示,在剪枝时必须保证shortcut连接的两个卷积层通道数相等。
- 使用network slimming算法时,每个卷积层都必须剩下至少1个通道。
TensorRT int8量化算法
参考连接:8-bit Inference with TensorRT
量化概述
- 目标:将fp32的卷积神经网络转换为int8而不会造成明显的精度损失;
- 原因:int8方法具有更高的吞吐量和更低的内存需求;
- 挑战:int8的精度和动态范围明显低于fp32;
动态范围 | 最小精度 | |
---|---|---|
fp32 | -3.4 x 1038 ~ +3.4 x 1038 | 1.4 x 10-45 |
fp16 | -65504 ~ +65504 | 5.96 x 10-8 |
int8 | -128 ~ +127 | 1 |
- 解决方案:在将训练的模型权重量化为int8时以及在int8计算激活时,最大限度地减少信息损失;
- 结果:在TensorRT中实现了int8方法,并且不需要任何额外的fine tune或重训练。
- 问题:
为什么要量化而不使用int8直接训练?
模型训练是需要反向传播和梯度下降的,训练时的超参一般都是浮点型,如学习率等,int8类型无法进行训练。
线性量化
公式:
Tensor Values = fp32 scale factor * int8 array
根据量化公式仅需求fp32 scale factor即可进行int8量化,那么如何求fp32 scale factor?
从上图中可以看出,有两种int8量化方法,一种是非饱和映射(左),另一种是饱和映射(右)。经过验证,非饱和映射会造成严重的准确率损失,原因是由于卷积层计算的正负分布很不均匀,如果按照对称非饱和映射(原意是为了尽可能多地保留原信息)的话,那么+max那边有一块区域就浪费了,也就是说scale到int8后,int8的动态范围就更小了,举个极限的例子就是量化后正的样本没有,负的全部扎堆在一个很小的值附近,这样会造成严重的精度损失。饱和映射方法是先找一个阈值T,将低于最低阈值的值全部都饱和映射到-127上,如右上图的左边的三个红色的点。
如何选择量化阈值?
1. 标定步骤:
- 在标定数据集上进行fp32推理。
- 对每一个卷积层:
- 收集激活的直方图;
- 生成具有不同饱和阈值的量化分布;
- 选择使KL发散最小化的阈值。
- 一般来说,整个标定过程需要几分钟的时间
2. 标定数据集选择:
- 有代表性
- 多样化
- 理想情况下是验证数据集的子集
- 1000+样例
深度学习模型转TensorRT
【1】 深度学习模型PyTorch训练并转ONNX与TensorRT部署
【2】 darknet YOLOv4模型转ONNX转TensorRT部署
【3】 yolov5 PyTorch模型转TensorRT
【4】 CenterFace模型转TensorRT
【5】 RetinaFace MXNet模型转ONNX转TensorRT