RT-DETR改进策略【模型轻量化】| PP-LCNet:轻量级的CPU卷积神经网络
一、本文介绍
本文记录的是利用
PP-LCNet
中的
DepSepConv模块
优化
RT-DETR
。本文利用
DepSepConv模块
改善模型结构,
使模型在几乎不增加延迟的情况下提升网络准确度。
| 模型 | 参数量 | 计算量 | 推理速度 |
|---|---|---|---|
| rtdetr-l | 32.8M | 108.0GFLOPs | 11.6ms |
| Improved | 30.2M | 86.5GFLOPs | 10.7ms |
二、PP-LCNet介绍
PP-LCNet
:一个轻量级的CPU卷积神经网络
2.1 PP-LCNet结构设计
2.1.1 特点
论文中总结了一系列在不增加推理时间的情况下提高精度的方法,并结合这些方法实现了精度和速度的更好平衡。基于此提出了设计轻量级CNN的一些通用规则。
2.1.2 模块原理
-
基本块 :使用
MobileNetV1中提到的DepthSepConv作为基本块,该模块不会增加模型的推理速度和额外的操作,且已被英特尔CPU加速库深度优化,推理速度可超越其他轻量级块。 -
激活函数 :将
BaseNet中的激活函数从ReLU替换为H-Swish,大大提高了性能,同时推理时间几乎不变。 -
SE模块 :
SE模块有助于对网络通道进行加权以获得更好的特征,但在英特尔CPU上会增加推理时间。通过实验发现,将SE模块添加到网络尾部附近的块中,可以发挥更好的作用,实现更好的精度 - 速度平衡。 -
卷积核大小 :实验发现,在网络尾部用5×5卷积核替换3×3卷积核,可以在低延迟和高准确性的情况下达到替换几乎所有层的效果,因此只在尾部进行此替换操作。
-
1×1卷积层 :在
GAP后的网络输出维度较小,直接添加最终分类层会丢失特征的组合。为了给网络更强的拟合能力,在最终GAP层后添加了一个1280维大小的1×1卷积(相当于FC层),可以在几乎不增加推理时间的情况下让模型存储更多信息。
论文: https://arxiv.org/pdf/2109.15099.pdf
源码: https://github.com/PaddlePaddle/PaddleClas
三、PP-LC模块的实现代码
PP-LC模块
的实现代码如下:
import torch.nn as nn
class SELayer(nn.Module):
def __init__(self, in_channel, reduction=16):
super(SELayer, self).__init__()
assert reduction >= 16
self.avg_pool = nn.AdaptiveAvgPool2d(1)
self.fc = nn.Sequential(
nn.Linear(in_channel, in_channel // reduction, bias=False),
nn.ReLU(inplace=True),
nn.Linear(in_channel // reduction, in_channel, bias=False),
nn.Sigmoid()
)
def forward(self, x):
b, c, _, _ = x.size()
y = self.avg_pool(x).view(b, c)
y = self.fc(y).view(b, c, 1, 1)
return x * y.expand_as(x)
class DepSepConv(nn.Module):
def __init__(self, inp, oup, kernel_size, stride, use_se):
super(DepSepConv, self).__init__()
assert stride in [1, 2]
padding = (kernel_size - 1) // 2
if use_se:
self.conv = nn.Sequential(
# dw
nn.Conv2d(inp, inp, kernel_size, stride, padding, groups=inp, bias=False),
nn.BatchNorm2d(inp),
nn.Hardswish(),
# SE
SELayer(inp, inp),
# pw-linear
nn.Conv2d(inp, oup, 1, 1, 0, bias=False),
nn.BatchNorm2d(oup),
nn.Hardswish(),
)
else:
self.conv = nn.Sequential(
# dw
nn.Conv2d(inp, inp, kernel_size, stride, padding, groups=inp, bias=False),
nn.BatchNorm2d(inp),
nn.Hardswish(),
# pw-linear
nn.Conv2d(inp, oup, 1, 1, 0, bias=False),
nn.BatchNorm2d(oup),
nn.Hardswish()
)
def forward(self, x):
return self.conv(x)
DepSepConv模块
参数详解
| 参数 | 解释 |
|---|---|
| inp | 输入通道数 |
| oup | 输出通道数 |
| kernel_size | 卷积核大小 |
| stride | 步长 |
| use_se | 是否使用SE注意力 |
四、模块声明
4.1 改进点⭐
将上方代码按照
5.1小节
的说明加载到文件中
📌 模型的修改方法是将
骨干网络
中的所有
HGBlock模块
替换成
DepSepConv模块
,使
RT-DETR
在不增加延迟的情况下促进网络学习更强的特征表示、提高轻量级模型精度,并且
DepSepConv模块
中的
SE模块
有助于对网络通道进行加权以获得更好的特征表示。
注意❗:需要声明的模块名称为:
DepSepConv
。
五、添加步骤
5.1 修改一
① 在
ultralytics/nn/
目录下新建
AddModules
文件夹用于存放模块代码
② 在
AddModules
文件夹下新建
PPLCNet.py
,将
第三节
中的代码粘贴到此处
5.2 修改二
在
AddModules
文件夹下新建
__init__.py
(已有则不用新建),在文件内导入模块:
from .PPLCNet import *
5.3 修改三
在
ultralytics/nn/modules/tasks.py
文件中,需要在两处位置添加各模块类名称。
首先:导入模块
其次:在
parse_model函数
中注册
DepSepConv
:
六、yaml模型文件
6.1 模型改进版本
此处以
ultralytics/cfg/models/rt-detr/rtdetr-l.yaml
为例,在同目录下创建一个用于自己数据集训练的模型文件
rtdetr-PPLCNet.yaml
。
将
rtdetr-l.yaml
中的内容复制到
rtdetr-PPLCNet.yaml
文件下,修改
nc
数量等于自己数据中目标的数量。
📌 模型的修改方法是将
骨干网络
中添加
DepSepConv模块
。
# Ultralytics YOLO 🚀, AGPL-3.0 license
# RT-DETR-l object detection model with P3-P5 outputs. For details see https://docs.ultralytics.com/models/rtdetr
# Parameters
nc: 1 # number of classes
scales: # model compound scaling constants, i.e. 'model=yolov8n-cls.yaml' will call yolov8-cls.yaml with scale 'n'
# [depth, width, max_channels]
l: [1.00, 1.00, 1024]
backbone:
# [from, repeats, module, args]
- [-1, 1, HGStem, [32, 48]] # 0-P2/4
- [-1, 6, DepSepConv, [48, 3, 1, True]] # stage 1
- [-1, 1, DWConv, [128, 3, 2, 1, False]] # 2-P3/8
- [-1, 6, DepSepConv, [128, 3, 1, True]] # stage 2
- [-1, 1, DWConv, [512, 3, 2, 1, False]] # 4-P4/16
- [-1, 6, DepSepConv, [512, 3, 1, True]] # cm, c2, k, light, shortcut
- [-1, 6, DepSepConv, [512, 3, 1, True]]
- [-1, 6, DepSepConv, [512, 3, 1, True]] # stage 3
- [-1, 1, DWConv, [1024, 3, 2, 1, False]] # 8-P5/32
- [-1, 6, DepSepConv, [1024, 3, 1, True]] # stage 4
head:
- [-1, 1, Conv, [256, 1, 1, None, 1, 1, False]] # 10 input_proj.2
- [-1, 1, AIFI, [1024, 8]]
- [-1, 1, Conv, [256, 1, 1]] # 12, Y5, lateral_convs.0
- [-1, 1, nn.Upsample, [None, 2, "nearest"]]
- [7, 1, Conv, [256, 1, 1, None, 1, 1, False]] # 14 input_proj.1
- [[-2, -1], 1, Concat, [1]]
- [-1, 3, RepC3, [256]] # 16, fpn_blocks.0
- [-1, 1, Conv, [256, 1, 1]] # 17, Y4, lateral_convs.1
- [-1, 1, nn.Upsample, [None, 2, "nearest"]]
- [3, 1, Conv, [256, 1, 1, None, 1, 1, False]] # 19 input_proj.0
- [[-2, -1], 1, Concat, [1]] # cat backbone P4
- [-1, 3, RepC3, [256]] # X3 (21), fpn_blocks.1
- [-1, 1, Conv, [256, 3, 2]] # 22, downsample_convs.0
- [[-1, 17], 1, Concat, [1]] # cat Y4
- [-1, 3, RepC3, [256]] # F4 (24), pan_blocks.0
- [-1, 1, Conv, [256, 3, 2]] # 25, downsample_convs.1
- [[-1, 12], 1, Concat, [1]] # cat Y5
- [-1, 3, RepC3, [256]] # F5 (27), pan_blocks.1
- [[21, 24, 27], 1, RTDETRDecoder, [nc]] # Detect(P3, P4, P5)
七、成功运行结果
打印网络模型可以看到
DepSepConv
已经加入到模型中,并可以进行训练了。
**rtdetr-l-PPLCNet **:
rtdetr-PPLCNet summary: 950 layers, 30,177,507 parameters, 30,177,507 gradients, 86.5 GFLOPs
from n params module arguments
0 -1 1 25248 ultralytics.nn.modules.block.HGStem [3, 32, 48]
1 -1 6 18144 ultralytics.nn.AddModules.PPLCNet.DepSepConv [48, 48, 3, 1, True]
2 -1 1 3712 ultralytics.nn.modules.conv.DWConv [48, 128, 3, 2, 1, False]
3 -1 6 109824 ultralytics.nn.AddModules.PPLCNet.DepSepConv [128, 128, 3, 1, True]
4 -1 1 5632 ultralytics.nn.modules.conv.DWConv [128, 512, 3, 2, 1, False]
5 -1 6 1618944 ultralytics.nn.AddModules.PPLCNet.DepSepConv [512, 512, 3, 1, True]
6 -1 6 1618944 ultralytics.nn.AddModules.PPLCNet.DepSepConv [512, 512, 3, 1, True]
7 -1 6 1618944 ultralytics.nn.AddModules.PPLCNet.DepSepConv [512, 512, 3, 1, True]
8 -1 1 11264 ultralytics.nn.modules.conv.DWConv [512, 1024, 3, 2, 1, False]
9 -1 6 6383616 ultralytics.nn.AddModules.PPLCNet.DepSepConv [1024, 1024, 3, 1, True]
10 -1 1 262656 ultralytics.nn.modules.conv.Conv [1024, 256, 1, 1, None, 1, 1, False]
11 -1 1 789760 ultralytics.nn.modules.transformer.AIFI [256, 1024, 8]
12 -1 1 66048 ultralytics.nn.modules.conv.Conv [256, 256, 1, 1]
13 -1 1 0 torch.nn.modules.upsampling.Upsample [None, 2, 'nearest']
14 7 1 131584 ultralytics.nn.modules.conv.Conv [512, 256, 1, 1, None, 1, 1, False]
15 [-2, -1] 1 0 ultralytics.nn.modules.conv.Concat [1]
16 -1 3 2232320 ultralytics.nn.modules.block.RepC3 [512, 256, 3]
17 -1 1 66048 ultralytics.nn.modules.conv.Conv [256, 256, 1, 1]
18 -1 1 0 torch.nn.modules.upsampling.Upsample [None, 2, 'nearest']
19 3 1 33280 ultralytics.nn.modules.conv.Conv [128, 256, 1, 1, None, 1, 1, False]
20 [-2, -1] 1 0 ultralytics.nn.modules.conv.Concat [1]
21 -1 3 2232320 ultralytics.nn.modules.block.RepC3 [512, 256, 3]
22 -1 1 590336 ultralytics.nn.modules.conv.Conv [256, 256, 3, 2]
23 [-1, 17] 1 0 ultralytics.nn.modules.conv.Concat [1]
24 -1 3 2232320 ultralytics.nn.modules.block.RepC3 [512, 256, 3]
25 -1 1 590336 ultralytics.nn.modules.conv.Conv [256, 256, 3, 2]
26 [-1, 12] 1 0 ultralytics.nn.modules.conv.Concat [1]
27 -1 3 2232320 ultralytics.nn.modules.block.RepC3 [512, 256, 3]
28 [21, 24, 27] 1 7303907 ultralytics.nn.modules.head.RTDETRDecoder [1, [256, 256, 256]]
rtdetr-PPLCNet summary: 950 layers, 30,177,507 parameters, 30,177,507 gradients, 86.5 GFLOPs