前言:Hello大家好,我是小哥谈。GhostNet是一种针对计算机视觉任务的深度神经网络架构,它于2020年由中国科学院大学的研究人员提出。GhostNet的设计目标是在保持高精度的同时,减少模型的计算和存储成本。GhostNet通过引入Ghost模块来实现高效的网络设计,Ghost模块是一种新颖的特征重用机制,它可以在网络中引入更多的轻量级子网络,这些子网络与主干网络以并行的方式连接,通过共享卷积核来提高计算效率。GhostNet在ImageNet图像分类任务上取得了较好的性能,并且在计算和存储方面比一些流行的网络模型如MobileNetV3和EfficientNet要更高效。因此,GhostNet被认为是一种具有潜力的轻量级神经网络架构,在计算资源有限的设备上具有广泛的应用前景。🌈
前期回顾:
YOLOv5算法改进(1)— 如何去改进YOLOv5算法
YOLOv5算法改进(2)— 添加SE注意力机制
YOLOv5算法改进(3)— 添加CBAM注意力机制
YOLOv5算法改进(4)— 添加CA注意力机制
YOLOv5算法改进(5)— 添加ECA注意力机制
YOLOv5算法改进(6)— 添加SOCA注意力机制
YOLOv5算法改进(7)— 添加SimAM注意力机制
YOLOv5算法改进(8)— 替换主干网络之MobileNetV3
YOLOv5算法改进(9)— 替换主干网络之ShuffleNetV2
目录
🚀1.论文
🚀2.GhostNet网络架构及原理
💥💥2.1 Ghost Module
💥💥2.2 Ghost Bottlenecks
💥💥2.3 Ghostnet的构建
🚀3.YOLOv5结合Ghostnet
💥💥步骤1:在common.py中添加Ghostnet模块
💥💥步骤2:在yolo.py文件中加入类名
💥💥步骤3:创建自定义yaml文件
💥💥步骤4:验证是否加入成功
💥💥步骤5:修改train.py中的'--cfg'默认参数
🚀1.论文
GhostNet是2019年由华为诺亚方舟实验室发布的轻量级网络,速度和MobileNetV3相似,但是识别的准确率比MobileNetV3高,在ImageNet ILSVRC-2012分类数据集的达到了75.7%的top-1精度。该论文提除了Ghost模块,通过廉价操作生成更多的特征图。基于一组原始的特征图,作者应用一系列线性变换,以很小的代价生成许多能从原始特征发掘所需信息的“Ghost”特征图(Ghost feature maps)。Ghost模块是一种即插即用的模块,通过堆叠Ghost模块得出Ghost bottleneck,进而搭建轻量级神经网络——GhostNet。🍃
论文题目:《GhostNet: More Features from Cheap Operations》目:
论文地址: https://arxiv.org/abs/1911.11907
代码实现: https://github.com/huawei-noah/Efficient-AI-Backbones/releases/tag/GhostNetV2
🚀2.GhostNet网络架构及原理
💥💥2.1 Ghost Module
通过上述的介绍,我们了解到了,GhostNet的核心思想就是使用一些计算量更低(Cheap Operations)的操作去生成这些冗余的特征图。在论文中,作者设计了一个名为Ghost Module的模块,他的功能是代替普通卷积。📚
Ghost Module将普通卷积分为两部分:
首先,进行一个普通的1x1卷积,这是一个少量卷积,比如正常使用32通道的卷积,这里就用16通道的卷积,这个1x1卷积的作用类似于特征整合,生成输入特征层的特征浓缩。
然后,我们再进行深度可分离卷积,这个深度可分离卷积是逐层卷积,它也就是论文上面提到的Cheap Operations。它利用上一步获得的特征浓缩生成Ghost特征图。
因此,如果我们从整体上去看这个Ghost Module,它其实就是两步简单思想的汇总:
💞(1)利用1x1卷积获得输入特征的必要特征浓缩。
💞(2)利用深度可分离卷积获得特征浓缩的相似特征图(Ghost)。
Ghost-Module分成三个步骤:
🍀(1)先通过普通的conv生成一些特征图。
🍀(2)对生成的特征图进行cheap操作生成冗余特征图,这步使用的卷积是DW 卷积。
🍀(3)将conv生成的特征图与cheap操作生成的特征图进行concat操作。
如下图(b)所示,展示了Ghost模块和普通卷积的过程。👇
💥💥2.2 Ghost Bottlenecks
实现了Ghost 模块,接下来开始搭建 Ghost Bottlenecks。
Ghost Bottlenecks是由Ghost Module组成的瓶颈结构,其实本质上就是用Ghost Module,来代替瓶颈结构里面的普通卷积。
Ghost Bottlenecks可以分为两个部分,分别是主干部分和残差边部分,包含Ghost Module的,我们称它为主干部分。
Ghost Bottlenecks有两个种类,如下图所示,当我们需要对特征层的宽高进行压缩的时候,我们会设置这个Ghost Bottlenecks的Stride=2,即步长为2。此时我们会Bottlenecks里面多添加一些卷积层,在主干部分里,我们会在两个Ghost Module中添加一个步长为2x2的深度可分离卷积进行特征层的宽高压缩。在残差边部分,我们也会添加上一个步长为2x2的深度可分离卷积和1x1的普通卷积。🌹
接下来实现GhostNet。🔖
💥💥2.3 Ghostnet的构建
GhostNet的参数结构参考论文中的图,如下图:
可以看到,整个Ghostnet都是由Ghost Bottlenecks进行组成的。
当一张图片输入到Ghostnet当中时,我们首先进行一个16通道的普通1x1卷积块(卷积+标准化+激活函数)。之后我们就开始Ghost Bottlenecks的堆叠了,利用Ghost Bottlenecks,我们最终获得了一个7x7x160的特征层(当输入是224x224x3的时候)。然后我们会利用一个1x1的卷积块进行通道数的调整,此时我们可以获得一个7x7x960的特征层。之后我们进行一次全局平均池化,然后再利用一个1x1的卷积块进行通道数的调整,获得一个1x1x1280的特征层。然后平铺后进行全连接就可以进行分类了。🌿
🚀3.YOLOv5结合Ghostnet
💥💥步骤1:在common.py中添加Ghostnet模块
将下面Ghostnet模块的代码复制粘贴到common.py文件的末尾。
class SeBlock(nn.Module):
def __init__(self, in_channel, reduction=4):
super().__init__()
self.Squeeze = nn.AdaptiveAvgPool2d(1)
self.Excitation = nn.Sequential()
self.Excitation.add_module('FC1', nn.Conv2d(in_channel, in_channel // reduction, kernel_size=1)) # 1*1卷积与此效果相同
self.Excitation.add_module('ReLU', nn.ReLU())
self.Excitation.add_module('FC2', nn.Conv2d(in_channel // reduction, in_channel, kernel_size=1))
self.Excitation.add_module('Sigmoid', nn.Sigmoid())
def forward(self, x):
y = self.Squeeze(x)
ouput = self.Excitation(y)
return x * (ouput.expand_as(x))
class G_bneck(nn.Module):
# Ghost Bottleneck https://github.com/huawei-noah/ghostnet
def __init__(self, c1, c2, midc, k=5, s=1, use_se = False): # ch_in, ch_mid, ch_out, kernel, stride, use_se
super().__init__()
assert s in [1, 2]
c_ = midc
self.conv = nn.Sequential(GhostConv(c1, c_, 1, 1), # Expansion
Conv(c_, c_, 3, s=2, p=1, g=c_, act=False) if s == 2 else nn.Identity(), # dw
# Squeeze-and-Excite
SeBlock(c_) if use_se else nn.Sequential(),
GhostConv(c_, c2, 1, 1, act=False)) # Squeeze pw-linear
self.shortcut = nn.Identity() if (c1 == c2 and s == 1) else \
nn.Sequential(Conv(c1, c1, 3, s=s, p=1, g=c1, act=False), \
Conv(c1, c2, 1, 1, act=False)) # 避免stride=2时 通道数改变的情况
def forward(self, x):
# print(self.conv(x).shape)
# print(self.shortcut(x).shape)
return self.conv(x) + self.shortcut(x)
具体如下图所示:
💥💥步骤2:在yolo.py文件中加入类名
首先在yolo.py文件中找到parse_model函数这一行,加入G_bneck模块。
💥💥步骤3:创建自定义yaml文件
在models文件夹中复制yolov5s.yaml,粘贴并重命名为yolov5s_Ghostnet.yaml。
然后根据Ghostnet的网络架构来修改配置文件。
yaml文件修改后的完整代码如下:
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
# Parameters
nc: 80 # number of classes
depth_multiple: 1.0 # model depth multiple
width_multiple: 1.0 # layer channel multiple
anchors:
- [10,13, 16,30, 33,23] # P3/8
- [30,61, 62,45, 59,119] # P4/16
- [116,90, 156,198, 373,326] # P5/32
# Ghostnet backbone
backbone:
# [from, number, module, args]
[[-1, 1, Conv, [16, 3, 2, 1]], # 0-P1/2 ch_out, kernel, stride, padding, groups
[-1, 1, G_bneck, [16, 16, 3, 1]], # 1 ch_out, ch_mid, dw-kernel, stride
[-1, 1, G_bneck, [24, 48, 3, 2]], # 2-P2/4
[-1, 1, G_bneck, [24, 72, 3, 1]], # 3
[-1, 1, G_bneck, [40, 72, 3, 2, True]], # 4-P3/8
[-1, 1, G_bneck, [40, 120, 3, 1, True]], # 5
[-1, 1, G_bneck, [80, 240, 3, 2]], # 6-P4/16
[-1, 3, G_bneck, [80, 184, 3, 1]], # 7
[-1, 1, G_bneck, [112, 480, 3, 1, True]],
[-1, 1, G_bneck, [112, 480, 3, 1, True]],
[-1, 1, G_bneck, [160, 672, 3, 2, True]], # 10-P5/32
[-1, 1, G_bneck, [160, 960, 3, 1]], # 11
[-1, 1, G_bneck, [160, 960, 3, 1, True]],
[-1, 1, G_bneck, [160, 960, 3, 1]],
[-1, 1, G_bneck, [160, 960, 3, 1, True]],
[-1, 1, Conv, [960]],
]
# YOLOv5 v6.0 head
head:
[[-1, 1, Conv, [480, 1, 1]],
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
[[-1, 9], 1, Concat, [1]], # cat backbone P4
[-1, 3, C3, [480, False]], # 19
[-1, 1, Conv, [240, 1, 1]],
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
[[-1, 5], 1, Concat, [1]], # cat backbone P3
[-1, 3, C3, [240, False]], # 23 (P3/8-small)
[-1, 1, Conv, [240, 3, 2]],
[[-1, 20], 1, Concat, [1]], # cat head P4
[-1, 3, C3, [480, False]], # 26 (P4/16-medium)
[-1, 1, Conv, [480, 3, 2]],
[[-1, 15], 1, Concat, [1]], # cat head P5
[-1, 3, C3, [960, False]], # 29 (P5/32-large)
[[23, 26, 29], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
]
💥💥步骤4:验证是否加入成功
在yolo.py文件里,配置我们刚才自定义的yolov5s_Ghostnet.yaml。
然后运行yolo.py,得到结果。
这样就算添加成功了。🎉🎉🎉
💥💥步骤5:修改train.py中的'--cfg'默认参数
在train.py文件中找到 parse_opt函数,然后将第二行 '--cfg' 的default改为 'models/yolov5s_Ghostnet.yaml',然后就可以开始进行训练了。🎈🎈🎈