email:likedge.top
我们接触到的dropout就是一个剪枝的过程,他随机弃置了一些神经元连接,帮助模型增加了权重的稀疏性,有效地防止过拟合。从dropout到dropoutconnect,都在做着同一件事情,我们弃置一些神经网络局部连接,使得其余的信息更集中于剩下的神经元。
Dropout随机弃置一些神经元的输出,而对应的dropconnect则是随机讲一些神经元的连接弃置,使得权重变得稀疏。这其实是细粒度剪枝的一种,这种剪枝方法有一个不好的地方就是,只能通过随机弃置来测试得出最好的剪枝策略,没办法通过衡量对应的输出影响能力去清除冗余。
所以比起细粒度的剪枝,更常用且高效的方式是在更高一层,即针对滤波器为粒度进行剪枝,可以直接去除部分卷积核。
1.L1 norm prune
这是很经典的一篇工作,他的核心思想是移除对最终结果贡献很小的卷积核,以达到剪枝优化速度的目的。
对每个卷积核,计算一个sensitivity:
按照敏感程度进行排名,将topk个卷积核以及其对应的featuremap删除,第二层与上层被删掉的featuremap相关的部分也删除。
原理就是,我们假设整个网络的所有layer都是独立的,他们之前没有强相关性,那么我们选中的topk个卷积核,其L1范数值最小,得到一个新的信息就是,这个卷积核对整个模型的共享比较少。
得到剪枝完的模型权重以后,使用原来数据集进行finetune就可以很快恢复精度了
Code (by nni):
xxxxxxxxxx
import torch, torchvision
from nni.algorithms.compression.v2.pytorch.pruning import L1NormPruner, L2NormPruner,FPGMPruner,ActivationAPoZRankPruner
from nni.compression.pytorch.speedup import ModelSpeedup
from rich import print
from utils.activations import SiLU
import torch.nn as nn
from nni.compression.pytorch.utils.counter import count_flops_params
cfg_list = [{'sparsity': 0.3, 'op_types': ['Conv2d'],'op_names': ['model.conv.1']}]
pruner = L1NormPruner(model, cfg_list)
_, masks = pruner.compress()
pruner.export_model(model_path='deepsort_yolov5m.pt', mask_path='deepsort_mask.pt')
pruner.show_pruned_weights()
pruner._unwrap_model()
for _ in range(100):
use_mask_out = model(dummy_input)
nni使用了一个mask对每个卷积的层进行遮盖,每一次输入都会经过一个mask,mask会把对应剪枝掉的卷积核置0,乘上以后,得到最后的输出,这对于模型结构很深以及冗余结构很多的模型有很好的效果。