点击我爱计算机视觉标星,更快获取CVML新技术


尽管目标检测新算法层出不穷,但在实际工程项目中不少52CV群友还是念着YOLOv3的好。将其部署到边缘设备等时,模型剪枝是非常有必要的,毕竟有原始模型有239M的参数,剪枝后往往也能提速不少。

比如之前52CV曾经分享过:

YOLOv3模型剪枝,瘦身80%,提速100%,精度基本不变

YOLOv3剪枝再升级!

上述剪枝是减少模型通道数,而今天向大家介绍的工程可以实现通道和层的双向剪枝,在oxford hand 数据集hand检测问题中,作者实验中可以实现精度下降很小而参数减少 98%,砍掉 48 个层,提速 2 倍!(不同问题参数减少和提速比例不同,在作者另一个私人项目中,实现了提速 3 倍。)

作者已将其开源,仅需要几行命令就可以在自己数据集中实现更加灵活和搜索空间更大的剪枝。

工程地址:

https://github.com/tanluren/yolov3-channel-and-layer-pruning

欢迎大家给大佬加Star!

以下内容为原作者52CV群友漂的投稿。

本项目以ultralytics/yolov3(https://github.com/ultralytics/yolov3)为基础实现,根据论文Learning Efficient Convolutional Networks Through Network Slimming (ICCV 2017)原理基于bn层Gmma系数进行通道剪枝,下面引用了几种不同的通道剪枝策略,并对原策略进行了改进,提高了剪枝率和精度;在这些工作基础上,又衍生出了层剪枝,本身通道剪枝已经大大减小了模型参数和计算量,降低了模型对资源的占用,而层剪枝可以进一步减小了计算量,并大大提高了模型推理速度;通过层剪枝和通道剪枝结合,可以压缩模型的深度和宽度,某种意义上实现了针对不同数据集的小模型搜索。

项目的基本工作流程是,使用yolov3训练自己数据集,达到理想精度后进行稀疏训练,稀疏训练是重中之重,对需要剪枝的层对应的bn gamma系数进行大幅压缩,理想的压缩情况如下图,然后就可以对不重要的通道或者层进行剪枝,剪枝后可以对模型进行微调恢复精度,后续会写篇博客记录一些实验过程及调参经验,在此感谢行云大佬(https://github.com/zbyuan)的讨论和合作!

更新

1. 增加了对yolov3-spp结构的支持,基础训练可以直接使用yolov3-spp.weights初始化权重,各个层剪枝及通道剪枝脚本的使用也和yolov3一致。

2. 增加了多尺度推理支持,train.py和各剪枝脚本都可以指定命令行参数, 如 --img_size 608 .

基础训练

环境配置查看requirements.txt,数据准备参考这里(https://github.com/ultralytics/yolov3/wiki/Train-Custom-Data),预训练权重可以从darknet官网下载。

用yolov3训练自己的数据集,修改cfg,配置好data,用yolov3.weights初始化权重。

python train.py --cfg cfg/my_cfg.cfg --data data/my_data.data --weights weights/yolov3.weights --epochs 100 --batch-size 32

稀疏训练

scale参数默认0.001,根据数据集,mAP,BN分布调整,数据分布广类别多的,或者稀疏时掉点厉害的适当调小s;-sr用于开启稀疏训练;--prune 0适用于prune.py,--prune 1 适用于其他剪枝策略。稀疏训练就是精度和稀疏度的博弈过程,如何寻找好的策略让稀疏后的模型保持高精度同时实现高稀疏度是值得研究的问题,大的s一般稀疏较快但精度掉的快,小的s一般稀疏较慢但精度掉的慢;配合大学习率会稀疏加快,后期小学习率有助于精度回升。

注意:训练保存的pt权重包含epoch信息,可通过python -c "from models import *; convert('cfg/yolov3.cfg', 'weights/last.pt')"转换为darknet weights去除掉epoch信息,使用darknet weights从epoch 0开始稀疏训练。

python train.py --cfg cfg/my_cfg.cfg --data data/my_data.data --weights weights/last.weights --epochs 300 --batch-size 32 -sr --s 0.001 --prune 1

通道剪枝策略一

策略源自Lam1360/YOLOv3-model-pruning(https://github.com/Lam1360/YOLOv3-model-pruning),这是一种保守的策略,因为yolov3中有五组共23处shortcut连接,对应的是add操作,通道剪枝后如何保证shortcut的两个输入维度一致,这是必须考虑的问题。而Lam1360/YOLOv3-model-pruning对shortcut直连的层不进行剪枝,避免了维度处理问题,但它同样实现了较高剪枝率,对模型参数的减小有很大帮助。虽然它剪枝率最低,但是它对剪枝各细节的处理非常优雅,后面的代码也较多参考了原始项目。在本项目中还更改了它的阈值规则,可以设置更高的剪枝阈值。

python prune.py --cfg cfg/my_cfg.cfg --data data/my_data.data --weights weights/last.pt --percent 0.85

通道剪枝策略二

策略源自coldlarry/YOLOv3-complete-pruning(https://github.com/coldlarry/YOLOv3-complete-pruning),这个策略对涉及shortcut的卷积层也进行了剪枝,剪枝采用每组shortcut中第一个卷积层的mask,一共使用五种mask实现了五组shortcut相关卷积层的剪枝,进一步提高了剪枝率。本项目中对涉及shortcut的剪枝后激活偏移值处理进行了完善,并修改了阈值规则,可以设置更高剪枝率,当然剪枝率的设置和剪枝后的精度变化跟稀疏训练有很大关系,这里再次强调稀疏训练的重要性。

python shortcut_prune.py --cfg cfg/my_cfg.cfg --data data/my_data.data --weights weights/last.pt --percent 0.6

通道剪枝策略三

策略参考自PengyiZhang/SlimYOLOv3(https://github.com/PengyiZhang/SlimYOLOv3),这个策略的通道剪枝率最高,先以全局阈值找出各卷积层的mask,然后对于每组shortcut,它将相连的各卷积层的剪枝mask取并集,用merge后的mask进行剪枝,这样对每一个相关层都做了考虑,同时它还对每一个层的保留通道做了限制,实验中它的剪枝效果最好。在本项目中还对激活偏移值添加了处理,降低剪枝时的精度损失。

python slim_prune.py --cfg cfg/my_cfg.cfg --data data/my_data.data --weights weights/last.pt --global_percent 0.8 --layer_keep 0.01

层剪枝

这个策略是在之前的通道剪枝策略基础上衍生出来的,针对每一个shortcut层前一个CBL进行评价,对各层的Gmma最高值进行排序,取最小的进行层剪枝。为保证yolov3结构完整,这里每剪一个shortcut结构,会同时剪掉一个shortcut层和它前面的两个卷积层。是的,这里只考虑剪主干中的shortcut模块。但是yolov3中有23处shortcut,剪掉8个shortcut就是剪掉了24个层,剪掉16个shortcut就是剪掉了48个层,总共有69个层的剪层空间;实验中对简单的数据集剪掉了较多shortcut而精度降低很少。

python layer_prune.py --cfg cfg/my_cfg.cfg --data data/my_data.data --weights weights/last.pt --shortcuts 12

同时剪层和通道

前面的通道剪枝和层剪枝已经分别压缩了模型的宽度和深度,可以自由搭配使用,甚至迭代式剪枝,调配出针对自己数据集的一副良药。这里整合了一个同时剪层和通道的脚本,方便对比剪枝效果,有需要的可以使用这个脚本进行剪枝。

python layer_channel_prune.py --cfg cfg/my_cfg.cfg --data data/my_data.data --weights weights/last.pt --shortcuts 12 --global_percent 0.8 --layer_keep 0.1

微调finetune

剪枝的效果好不好首先还是要看稀疏情况,而不同的剪枝策略和阈值设置在剪枝后的效果表现也不一样,有时剪枝后模型精度甚至可能上升,而一般而言剪枝会损害模型精度,这时候需要对剪枝后的模型进行微调,让精度回升。训练代码中默认了前6个epoch进行warmup,这对微调有好处,有需要的可以自行调整超参学习率。

python train.py --cfg cfg/prune_0.85_my_cfg.cfg --data data/my_data.data --weights weights/prune_0.85_last.weights --epochs 100 --batch-size 32

tensorboard实时查看训练过程

tensorboard --logdir runs

欢迎使用和测试,有问题或者交流实验过程可以发issue或者q我1806380874

案例

使用yolov3-spp训练oxford hand数据集并剪枝。下载数据集(http://www.robots.ox.ac.uk/~vgg/data/hands/downloads/hand_dataset.tar.gz),解压到data文件夹,运行converter.py,把得到的train.txt和valid.txt路径更新在oxfordhand.data中。通过以下代码分别进行基础训练和稀疏训练:

python train.py --cfg cfg/yolov3-spp-hand.cfg --data data/oxfordhand.data --weights weights/yolov3-spp.weights --batch-size 20 --epochs 100
python -c "from models import *; convert('cfg/yolov3.cfg', 'weights/last.pt')"
python train.py --cfg cfg/yolov3-spp-hand.cfg --data data/oxfordhand.data --weights weights/converted.weights --batch-size 20 --epochs 300 -sr --s 0.001 --prune 1

训练的情况如下图,蓝色线是基础训练,红色线是稀疏训练。其中基础训练跑了100个epoch,后半段已经出现了过拟合,最终得到的baseline模型mAP为0.84;稀疏训练以s0.001跑了300个epoch,选择的稀疏类型为prune 1全局稀疏,为包括shortcut的剪枝做准备,并且在总epochs的0.7和0.9阶段进行了Gmma为0.1的学习率衰减,稀疏过程中模型精度起伏较大,在学习率降低后精度出现了回升,最终稀疏模型mAP 0.797。

再来看看bn的稀疏情况,代码使用tensorboard记录了参与稀疏的bn层的Gmma权重变化,下图左边看到正常训练时Gmma总体上分布在1附近类似正态分布,右边可以看到稀疏过程Gmma大部分逐渐被压到接近0,接近0的通道其输出值近似于常量,可以将其剪掉。

这时候便可以进行剪枝,这里例子使用layer_channel_prune.py同时进行剪通道和剪层,这个脚本融合了slim_prune剪通道策略和layer_prune剪层策略。Global perent剪通道的全局比例为0.93,layer keep每层最低保持通道数比例为0.01,shortcuts剪了16个,相当于剪了48个层(32个CBL,16个shortcut);下图结果可以看到剪通道后模型掉了一个点,而大小从239M压缩到5.2M,剪层后mAP掉到0.53,大小压缩到4.6M,模型参数减少了98%,推理速度也从16毫秒减到6毫秒(Tesla P100测试结果)。

python layer_channel_prune.py --cfg cfg/yolov3-spp-hand.cfg --data data/oxfordhand.data --weights weights/last.pt --global_percent 0.93 --layer_keep 0.01 --shortcuts 16

鉴于模型精度出现了下跌,我们来进行微调,下面是微调50个epoch的结果,精度恢复到了0.793,bn也开始呈正态分布,这个结果相对于baseline掉了几个点,但是模型大幅压缩减少了资源占用,提高了运行速度。如果想提高精度,可以尝试降低剪枝率,比如这里只剪10个shortcut的话,同样微调50epoch精度可以回到0.81;而想追求速度的话,这里有个极端例子,全局剪0.95,层剪掉54个,模型压缩到了2.8M,推理时间降到5毫秒,而mAP降到了0,但是微调50 epochs 后依然回到了0.75。

python train.py --cfg cfg/prune_16_shortcut_prune_0.93_keep_0.01_yolov3-spp-hand.cfg --data data/oxfordhand.data --weights weights/prune_16_shortcut_prune_0.93_keep_0.01_last.weights --batch-size 52 --epochs 50

可以猜测,剪枝得到的cfg是针对该数据集相对合理的结构,而保留的权重可以让模型快速训练接近这个结构的能力上限,这个过程类似于一种有限范围的结构搜索。而不同的训练策略,稀疏策略,剪枝策略会得到不同的结果,相信即使是这个例子也可以进一步压缩并保持良好精度。yolov3有众多优化项目和工程项目,可以利用这个剪枝得到的cfg和weights放到其他项目中做进一步优化和应用。

这里(https://pan.baidu.com/s/1APUfwO4L69u28Wt9gFNAYw)分享了这个例子的权重和cfg,包括baseline,稀疏,不同剪枝设置后的结果。

工程地址:

https://github.com/tanluren/yolov3-channel-and-layer-pruning

欢迎大家给大佬加Star!


模型压缩与应用部署交流群

关注最新最前沿的模型压缩、网络剪枝、轻量级网络设计、知识蒸馏、推理加速技术,扫码添加CV君拉你入群,(如已为CV君其他账号好友请直接私信)

(请务必注明:部署)

喜欢在QQ交流的童鞋,可以加52CV官方QQ群:805388940。

(不会时时在线,如果没能及时通过验证还请见谅)


长按关注我爱计算机视觉

YOLOv3通道+层剪枝,参数压缩98%,砍掉48个层,提速2倍!相关推荐

  1. 卷积层和全连接层的区别_CNN卷积层、全连接层的参数量、计算量

    我们以VGG-16为例,来探讨一下如何计算卷积层.全连接层的参数量.计算量.为了简单.直观地理解,以下讨论中我们都会忽略偏置项,实践中必须考虑偏置项. [卷积层的参数量] 什么是卷积层的参数? 卷积层 ...

  2. 深度学习面试题:怎么减少卷积层的参数数量?

    卷积神经网络的参数量是个可以优化的点,尤其在网络层数很深的情况下,以及网络结构很复杂的时候,那么怎么减少卷积层的参数数量呢? 减少卷积层参数量的方法可以简要地归为以下几点: 使用堆叠小卷积核代替大卷积 ...

  3. 详细解释CNN卷积神经网络各层的参数和链接个数的计算

    卷积神经网络是一个多层的神经网络,每层由多个二维平面组成,而每个平面由多个独立神经元组成. 图:卷积神经网络的概念示范:输入图像通过和三个可训练的滤波器和可加偏置进行卷积,滤波过程如图一,卷积后在C1 ...

  4. 遗传算法优化LSTM网络结构(实现自动根据适应度函数:即准确率来全局搜索最佳网络结构):主要被优化参数:网络层数,每层的神经元个数,全连接的层数,全连接层的神经元个数。代码有详细注解

    代码视频链接:https://www.bilibili.com/video/BV19q4y1Q7DR/ 代码效果图: 1.优化参数 本文优化的是LSTM的层数参数和各层神经元参数,其中包含了lstm层 ...

  5. [pytorch]yolov3.cfg参数详解(每层输出及route、yolo、shortcut层详解)

    文章目录 Backbone(Darknet53) 第一次下采样(to 208) 第二次下采样(to 104) 第三次下采样(to 52) 第四次下采样(to 26) 第五次下采样(to 13) YOL ...

  6. Lesson 16.1016.1116.1216.13 卷积层的参数量计算,1x1卷积核分组卷积与深度可分离卷积全连接层 nn.Sequential全局平均池化,NiN网络复现

    二 架构对参数量/计算量的影响 在自建架构的时候,除了模型效果之外,我们还需要关注模型整体的计算效率.深度学习模型天生就需要大量数据进行训练,因此每次训练中的参数量和计算量就格外关键,因此在设计卷积网 ...

  7. DEEP COMPRESSION(深度学习网络参数压缩)

    DEEP COMPRESSION: DEEP COMPRESSION: COMPRESSING DEEP NEURAL NETWORKS WITH PRUNING, TRAINED QUANTIZAT ...

  8. 深度学习 卷积层与全连接层权重参数个数的计算

    1.卷积网络实例分析 构建卷积网络如下: from tensorflow.python.keras import datasets, models, layers class CNN(object): ...

  9. Caffe学习系列(2):数据层及参数

    要运行caffe,需要先创建一个模型(model),如比较常用的Lenet,Alex等, 而一个模型由多个屋(layer)构成,每一屋又由许多参数组成.所有的参数都定义在caffe.proto这个文件 ...

最新文章

  1. CTFshow php特性 web130
  2. 错误: libstdc++.so.6: cannot open shared object file: No such file or directory
  3. linux 电源管理 regulator,Linux内核电源管理综述
  4. 二十八、Node.js中的RESTful API和接口设计
  5. boost::hana::int_c用法的测试程序
  6. xpath以某个字符开始_XPATH技术补充-实例
  7. 问题步骤记录器——“懒教师”的好帮手
  8. linux连接建立的时间,用timedatectl在Linux中检查当前时区及更改时区(创建符号链接来更改时区)...
  9. 学习笔记之进程管理的相关重要命令
  10. php 使用csv海量数据导出类
  11. android6.0如何锁定程序,如何在Android中制作应用程序锁定应用程序?
  12. cad电气工程量计算机,CAD电气管线快速算量方法
  13. 高超声速飞行器的建模与控制器设计
  14. 【汇编】DOS系统功能调用(INT 21H)
  15. 亲测Trimble X7三维扫描仪
  16. NISP-SO安全运维工程师需要掌握的核心能力
  17. densepose与SMPL之IUV坐标转XYZ坐标
  18. python发邮件给女朋友代码_Python群发邮件实例代码
  19. Spark 2.4.8 提交应用
  20. Oracle中的chr()函数 和 ascii()函数

热门文章

  1. Git远端库(GitHub)及Git流
  2. 推荐系统——Item2vec
  3. 设计模式学习笔记——状态(State)模式框架
  4. Servlet文件下载模板
  5. 软件工程 - 版本管理 - git 的基本实用方法 - 添加一个完整的项目目录的命令的细微差别
  6. 字典排序什么意思_列表及字典的排序
  7. 苹果cms安装 php映射,苹果cmsV10安装过程中的常见问题处理办法
  8. thinkphp v5.0.11漏洞_ThinkPHP(5.1.x ~ 5.1.31 5.0.x ~ 5.0.23)GetShell漏洞
  9. amaplocation无法获取高度_kali一款高度可定制的WiFi钓鱼工具 WiFiPhisher
  10. html自动图片墙,基于html5实现的图片墙效果