YOLOv5改进 | 卷积模块 | 用DWConv卷积替换Conv【轻量化网络】

 💡💡💡本专栏所有程序均经过测试,可成功执行💡💡💡

在YOLOv5的GFLOPs计算量中,卷积占了其中大多数的比列,为了减少计算量,研究人员提出了用DwConv代替Conv。本文给大家带来的教程是将原来的Conv替换为DwConv。文章在介绍主要的原理后,将手把手教学如何进行模块的代码添加和修改,并将修改后的完整代码放在文章的最后,方便大家一键运行,小白也可轻松上手实践。以帮助您更好地学习深度学习目标检测YOLO系列的挑战。


专栏地址YOLOv5改进+入门——持续更新各种有效涨点方法

目录

1. 原理

2. 代码实现

2.1 添加DwConv到YOLOv5代码中

2.2 新增yaml文件

2.3 注册模块

2.4 执行程序

3. 完整代码分享

4.GFLOPs对比

5. 总结


1. 原理

Dwconv,或深度可分离卷积(Depthwise Separable Convolution),是一种在神经网络中常用的卷积操作。它由两部分组成:深度卷积和逐点卷积。

1. 深度卷积(Depthwise Convolution):在深度卷积中,每个输入通道都与一个单独的滤波器(kernel)进行卷积操作。这意味着每个输入通道都会生成一个对应的输出通道。深度卷积主要用于捕捉输入数据的空间信息。

2. 逐点卷积(Pointwise Convolution):逐点卷积是一个 1×1 的卷积操作,它在每个位置上对输入的所有通道进行卷积。逐点卷积可以看作是在输入数据的通道维度上进行的卷积操作,而不涉及空间信息。它用于将深度卷积生成的各个通道的特征图进行线性组合。

使用深度可分离卷积的主要优势在于减少了计算量和参数数量,同时提高了模型的效率和速度。因为在深度卷积中,每个通道只需要与一个滤波器进行卷积,而不是像传统卷积那样每个通道都需要与所有滤波器进行卷积。逐点卷积进一步减少了参数数量,通过降低输入通道的维度。这种结构可以在保持相对较高的性能的同时,显著减少模型的大小和计算需求,特别是在移动设备等资源受限的环境中。 

深度卷积(Depthwise Convolution)

2. 代码实现

2.1 添加DwConv到YOLOv5代码中

关键步骤一:将下面代码粘贴到/projects/yolov5-6.1/models/common.py文件中

class Conv(nn.Module):
    # Standard convolution
    def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True):  # ch_in, ch_out, kernel, stride, padding, groups
        super().__init__()
        self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False)
        self.bn = nn.BatchNorm2d(c2)
        self.act = nn.SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity())

    def forward(self, x):
        return self.act(self.bn(self.conv(x)))

    def forward_fuse(self, x):
        return self.act(self.conv(x))


class DWConv(Conv):
    # Depth-wise convolution class
    def __init__(self, c1, c2, k=1, s=1, act=True):  # ch_in, ch_out, kernel, stride, padding, groups
        super().__init__(c1, c2, k, s, g=math.gcd(c1, c2), act=act)

Dwconv的流程可以分为以下几个步骤:

1. 输入数据:首先,我们有一个输入数据张量,通常是一个多通道的特征图。这个特征图可以是来自于之前的层级输出或者是网络的输入。

2. 深度卷积(Depthwise Convolution):对输入数据的每个通道应用深度卷积操作。对于每个输入通道,使用一个单独的滤波器(kernel)进行卷积操作。这个滤波器的大小通常是一个小的正方形,比如3x3或5x5。这个操作会生成与输入通道数相同的输出通道数,每个通道都包含了该通道的卷积结果。

3. 逐点卷积(Pointwise Convolution):接下来,对深度卷积生成的特征图应用逐点卷积操作。逐点卷积使用1x1的卷积核,在每个位置上对所有通道进行卷积操作。这个操作实际上是对特征图的每个像素点进行线性组合,将深度卷积生成的各个通道特征进行混合。逐点卷积的输出通道数量可以由设计者灵活指定,通常用来控制输出特征图的深度。

4. 非线性激活函数:通常,在逐点卷积之后,会应用一个非线性激活函数,比如ReLU,来增加网络的非线性表达能力。

5. 可选的池化或下采样:在需要的情况下,可以添加池化层或者其他下采样操作,以减小特征图的空间维度,同时保留重要的特征。

6. 输出:最终的输出是经过深度可分离卷积和非线性激活后得到的特征图。这些特征图可以传递到网络的下一层,也可以作为最终的输出。

Dwconv通过将传统的卷积操作拆分成深度卷积和逐点卷积两个步骤,从而降低了参数数量和计算复杂度,同时保持了模型的表达能力和性能。这种结构特别适用于移动端和嵌入式设备等资源受限的场景。

2.2 新增yaml文件

关键步骤二:在下/projects/yolov5-6.1/models下新建文件 yolov5_dw.yaml并将下面代码复制进去,当然你也可以自己修改,替换不同数量不同位置的Conv

# 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

# YOLOv5 v6.0 backbone
backbone:
  # [from, number, module, args]
  [[-1, 1, Conv, [64, 6, 2, 2]],  # 0-P1/2
   [-1, 1, DWConv, [128, 3, 2]],  # 1-P2/4
   [-1, 3, C3, [128]],
   [-1, 1, DWConv, [256, 3, 2]],  # 3-P3/8
   [-1, 6, C3, [256]],
   [-1, 1, DWConv, [512, 3, 2]],  # 5-P4/16
   [-1, 9, C3, [512]],
   [-1, 1, DWConv, [1024, 3, 2]],  # 7-P5/32
   [-1, 3, C3, [1024]],
   [-1, 1, SPPF, [1024, 5]],  # 9
  ]

# YOLOv5 v6.0 head
head:
  [[-1, 1, DWConv, [512, 1, 1]],
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
   [[-1, 6], 1, Concat, [1]],  # cat backbone P4
   [-1, 3, C3, [512, False]],  # 13

   [-1, 1, DWConv, [256, 1, 1]],
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
   [[-1, 4], 1, Concat, [1]],  # cat backbone P3
   [-1, 3, C3, [256, False]],  # 17 (P3/8-small)

   [-1, 1, DWConv, [256, 3, 2]],
   [[-1, 14], 1, Concat, [1]],  # cat head P4
   [-1, 3, C3, [512, False]],  # 20 (P4/16-medium)

   [-1, 1, DWConv, [512, 3, 2]],
   [[-1, 10], 1, Concat, [1]],  # cat head P5
   [-1, 3, C3, [1024, False]],  # 23 (P5/32-large)

   [[17, 20, 23], 1, Detect, [nc, anchors]],  # Detect(P3, P4, P5)
  ]

温馨提示:本文只是对yolov5l基础上添加swin模块,如果要对yolov8n/l/m/x进行添加则只需要指定对应的depth_multiple 和 width_multiple。


# YOLOv5n
depth_multiple: 0.33  # model depth multiple
width_multiple: 0.25  # layer channel multiple
 
# YOLOv5s
depth_multiple: 0.33  # model depth multiple
width_multiple: 0.50  # layer channel multiple
 
# YOLOv5l 
depth_multiple: 1.0  # model depth multiple
width_multiple: 1.0  # layer channel multiple
 
# YOLOv5m
depth_multiple: 0.67  # model depth multiple
width_multiple: 0.75  # layer channel multiple
 
# YOLOv5x
depth_multiple: 1.33  # model depth multiple
width_multiple: 1.25  # layer channel multiple
2.3 注册模块

关键步骤三:在yolo.py中注册, 大概在260行左右添加 ‘DwConv’

2.4 执行程序

在train.py中,将cfg的参数路径设置为yolov5_dwconv.yaml的路径

建议大家写绝对路径,确保一定能找到

 🚀运行程序,如果出现下面的内容则说明添加成功🚀

3. 完整代码分享

https://pan.baidu.com/s/1KbdSobXl8_asS2w_860iUA?pwd=7zh4

👆我修改后的代码, 提取码: 7zh4

4.GFLOPs对比

未改进的YOLOv5l的GFLOPs

 改进后的YOLOv5l的GFLOPs

5. 总结

Dwconv卷积(深度可分离卷积)是一种将传统卷积操作分解为深度卷积和逐点卷积两个步骤的技术。首先,深度卷积独立地对输入数据的每个通道进行卷积操作,生成与输入通道数相同的特征图通道。然后,逐点卷积使用1x1的卷积核对深度卷积生成的特征图进行线性组合,以减少参数数量和计算复杂度。逐点卷积的输出通道数量可以根据需要调整,控制输出特征图的深度。这种结构不仅有效地降低了模型的计算负载,同时也保持了模型的表达能力,特别适用于资源受限的移动端和嵌入式设备等场景。

### YOLOv5s轻量化网络优化技术与方法 对于YOLOv5s模型而言,实现轻量化的途径多种多样。通过减少参数数量和计算复杂度来提升推理速度并降低资源消耗是一项重要工作。 #### 参数剪枝 一种有效的方法是对神经网络中的冗余连接进行修剪。这不仅能够显著减小模型大小还能加快预测过程的速度[^1]。具体操作上可以采用基于权重绝对值设定阈值的方式去除那些贡献较小的链接;也可以利用敏感度分析找出不影响整体性能的关键路径之外的部分予以删除。 #### 低秩分解 另一个可行方案就是应用矩阵或张量上的低阶近似理论——即所谓的“低秩分解”。这种方法试图把原始高维空间映射到较低维度子空间内表示,在保持原有特征表达能力的同时极大地简化了内部结构从而达到加速目的。 #### 知识蒸馏 引入教师-学生框架下的迁移学习机制同样有助于构建更紧凑高效的检测器版本。“知识蒸馏”的核心在于让小型的学生模型模仿大型预训练好的老师的行为模式进而继承其大部分优势特性而无需承担过多负担。 ```python import torch.nn as nn class SlimYOLOv5(nn.Module): def __init__(self, original_model): super(SlimYOLOv5, self).__init__() # 剪枝后的卷积层定义 pruned_conv_layers = [] for layer in original_model.model: if isinstance(layer, nn.Conv2d): new_layer = prune_conv_layer(layer) pruned_conv_layers.append(new_layer) self.backbone = nn.Sequential(*pruned_conv_layers) def prune_conv_layer(conv_layer, threshold=0.01): weight_copy = conv_layer.weight.data.abs().clone() mask = weight_copy.gt(threshold).float().cuda() pruned_weight = conv_layer.weight * mask return nn.Conv2d( in_channels=conv_layer.in_channels, out_channels=int(mask.sum()), kernel_size=conv_layer.kernel_size, stride=conv_layer.stride, padding=conv_layer.padding, bias=(conv_layer.bias is not None), ).cuda() ```
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kay_545

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值