B站:https://space.bilibili.com/470550823
CSDN:https://blog.csdn.net/weixin_44936889
AI Studio:https://aistudio.baidu.com/aistudio/personalcenter/thirdview/67156
Github:https://github.com/Sharpiless
模型剪枝主要是裁剪掉全连接或者CNN层中不必要的参数,这里主要分析一下四种方法:FPGMFilterPruner、L1NormFilterPruner、L2NormFilterPruner和SlimFilterPruner。
计算每层权重的卷积核权值绝对值之和(L1/L2 Norm)作为重要性指标,删除权重数值较小的卷积核,并对删除后的卷积核重新组合成新的卷积和矩阵,最后在进行微调。
二者的区别就在于在计算(求权重绝对值之和)时不考虑上一层的修剪情况,可视化为下图中黄色区域代表的卷积核是否参与第二次修剪:
很难同时满足两个条件:
下图为FPGM论文的分析结果,蓝色是理想分布,绿色为可能的实际分布:
该算法基于几何中值FPGM通过修剪冗余的卷积核而不是重要性相对较小的卷积核来压缩CNN模型。即该策略通过统计Filters两两之间的几何距离来评估单个卷积内的Filters的重要性。直觉上理解,离其它Filters平均距离越远的Filter越重要。
几何中位数即是在d维欧氏空间中,使得一个点集 {a1, a2, …, an} 到他的欧式距离之和最小的点,称之为几何中位数点。
该算法给每一层所有的卷积核定义一个信息估计中心:
对于第i层,选取距离其信息估计中心最近的卷积核:
该卷积核就可以用同一层的其他卷积核来表示,即可以认为这个滤波器的信息跟其他滤波器重合,甚至是冗余的,因此修剪它对于模型的总体性能影响应当很小。这样我们就得到了一个与范数无关的衡量指标。
我们同时可以假设:
那么问题就可以变成在所有filters中寻找一个filter,确保其与剩余filters的欧氏距离之和最小:
基于Geometric Median的剪枝算法如下所示,类似于Soft Filter Pruning的实施过程:在训练过程中的第t个epoch,首先更新网络参数,然后根据每层的Geometric Median以及预先设定的剪枝率,将冗余的卷积核置零;置零的卷积核,在下一个epoch重新接受参数更新;如此反复迭代,最终冗余的filters将趋于稀疏化,达到正则化的目的。
该算法采用一种channel-level的裁剪方案,可以通过稀疏化尺度因子(BN层的scaling factor)来裁掉“不重要”的channel。并且该算法可以在训练时实现自动剪枝。
该算法引入一个尺度因子γ,对应每个channel,可以与每一个channel的输出相乘。然后联合训练权重和尺度因子,并对尺度因子稀疏正则化。最后prune掉较小的尺度因子及其对应的channel(权重)。原论文直接使用BN层的γ作为稀疏化裁剪的尺度因子:
https://github.com/Sharpiless/Pytorch-Auto-Slim-Tools
A pytorch toolkit for structured neural network pruning automatically
完全自动化的模型剪枝工具
用户层:人人都会用的剪枝工具,仅需二行代码即可完成全自动化剪枝
中间层:提供统一接口,让开发者可以自己封装SOTA剪枝算法,不断更新工具
系统底层:自动分析网络结构并构建剪枝关系
模型类型 |
|
|
---|---|---|
分类模型 | √ | AlexNet,VGG,ResNet系列等 |
检测模型 | √ | CenterNet,YOLO系列等 |
分割模型 | √ | 正在测试 |
函数名 |
|
---|---|
l1_norm_pruning | Pruning Filters for Efficient ConvNets |
l2_norm_pruning | Pruning Filters for Efficient ConvNets |
fpgm_pruning | Filter Pruning via Geometric Median for Deep Convolutional Neural Networks Acceleration |
在原始剪枝算法上,做了部分调整。此外,后续会支持更多的剪枝算法。
pip install -e ./
model可以来源于torchvision,也可以是自己在Pytorch中构建的model
import torch_pruning as pruning
from torchvision.models import resnet18
import torch
# 模型建立
model = resnet18()
flops_raw, params_raw = pruning.get_model_complexity_info(
model, (3, 224, 224), as_strings=True, print_per_layer_stat=False)
print('-[INFO] before pruning flops: ' + flops_raw)
print('-[INFO] before pruning params: ' + params_raw)
# 选择裁剪方式
mod = 'fpgm'
# 剪枝引擎建立
slim = pruning.Autoslim(model, inputs=torch.randn(
1, 3, 224, 224), compression_ratio=0.5)
if mod == 'fpgm':
config = {
'layer_compression_ratio': None,
'norm_rate': 1.0, 'prune_shortcut': 1,
'dist_type': 'l1', 'pruning_func': 'fpgm'
}
elif mod == 'l1':
config = {
'layer_compression_ratio': None,
'norm_rate': 1.0, 'prune_shortcut': 1,
'global_pruning': False, 'pruning_func': 'l1'
}
slim.base_prunging(config)
flops_new, params_new = pruning.get_model_complexity_info(
model, (3, 224, 224), as_strings=True, print_per_layer_stat=False)
print('\n-[INFO] after pruning flops: ' + flops_new)
print('-[INFO] after pruning params: ' + params_new)
python prune_resnet18_cifar10.py --mode train --round 0
python prune_resnet18_cifar10.py --mode prune --round 1 --total_epochs 60
python cifar100_prune.py --mode train --round 2 --total_epochs 10 --batch_size 512
感兴趣的同学关注我的公众号——可达鸭的深度学习教程:
B站:https://space.bilibili.com/470550823
CSDN:https://blog.csdn.net/weixin_44936889
AI Studio:https://aistudio.baidu.com/aistudio/personalcenter/thirdview/67156
Github:https://github.com/Sharpiless