YOLOv5改进 | 主干网络 | 将主干网络替换为轻量化的ShuffleNetv2【原理 + 完整代码】

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

目标检测是计算机视觉中一个重要的下游任务。对于边缘盒子的计算平台来说,一个大型模型很难实现实时检测的要求。基于一系列消融实验,研究者提出了几个实用的网络设计效率指导原则。据此,提出了一种新的架构,称为ShuffleNetV2。在本文中,给大家带来的教程是将原来的主干网络中的特征提取网络替换为shufflenetv2网络。文章在介绍主要的原理后,将手把手教学如何进行模块的代码添加和修改,并将修改后的完整代码放在文章的最后,方便大家一键运行,小白也可轻松上手实践。以帮助您更好地学习深度学习目标检测YOLO系列的挑战。

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

1.原理

论文地址ShuffleNet V2: Practical Guidelines for Efficient CNN Architecture Design——点击即可跳转

官方代码pytorch官方代码仓库——点击即可跳转

ShuffleNetV2 是一种高效的卷积神经网络架构,旨在优化计算资源的使用,尤其适用于移动设备和嵌入式系统。ShuffleNetV2 由 Megvii (Face++) 团队在 2018 年提出,是 ShuffleNet 的改进版本,主要通过优化信息流和减少内存访问成本来提升性能。

ShuffleNetV2 的设计理念

ShuffleNetV2 主要针对以下几个方面进行了优化:

  1. 模型计算复杂度

    通过减少冗余计算和优化卷积操作,降低计算复杂度,使得模型在计算资源有限的情况下仍能保持较高性能。
  2. 内存访问成本

    内存访问成本是影响模型效率的一个重要因素。ShuffleNetV2 通过优化特征图的传递和处理,减少内存带宽的占用。
  3. 并行度

    设计上考虑了硬件的并行计算能力,确保模型在并行处理器上的高效运行。

核心模块

  1. Channel Split(通道分割)

    输入特征图被分成两部分,一部分直接传递到下一层,另一部分经过复杂的卷积操作。这种设计减少了每一层的计算量,同时保留了原始信息。
  2. Channel Shuffle(通道打乱)

    在通道级别打乱特征图。通过这种操作,确保在网络的后续层中,各个通道之间的信息能够充分混合,提高特征的表达能力。
  3. Pointwise Group Convolution(逐点分组卷积)

    使用逐点分组卷积(1x1 卷积)来减少特征图通道数,同时分组卷积进一步降低计算量。
  4. Depthwise Convolution(深度可分离卷积)

    深度可分离卷积将标准卷积分解为深度卷积和逐点卷积两部分,从而减少计算量。
  5. Element-wise Add(元素级加法)

    使用元素级加法而非拼接操作,将原始输入与处理后的特征图结合,这样不仅减少了特征图的数量,还避免了高计算复杂度。

ShuffleNetV2 的性能优化

  1. 降低内存访问成本

    通过减少特征图的传递和拼接操作,减少了内存访问成本。这一点对资源受限的移动设备尤为重要。
  2. 提升计算效率

    通过优化卷积操作和特征图处理方式,ShuffleNetV2 能够在计算资源有限的情况下保持高效的计算性能。
  3. 增加并行度

    ShuffleNetV2 的设计充分考虑了硬件的并行计算能力,通过优化计算操作和数据流,提高了模型的并行计算效率。

总结

ShuffleNetV2 通过优化计算复杂度、内存访问成本和并行度,提供了一种高效的卷积神经网络架构。其设计理念和核心模块,如 Channel Split、Channel Shuffle 和 Depthwise Convolution 等,不仅降低了计算和内存开销,还提高了模型的性能和效率。这使得 ShuffleNetV2 成为一种非常适合在资源受限设备上部署的深度学习模型。

2. ShuffleNetv2的代码实现

2.1 将ShuffleNetv2添加到YOLOv5中

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

def channel_shuffle(x, groups):
    batchsize, num_channels, height, width = x.data.size()
    channels_per_group = num_channels // groups

    # reshape
    x = x.view(batchsize, groups,
               channels_per_group, height, width)

    x = torch.transpose(x, 1, 2).contiguous()

    # flatten
    x = x.view(batchsize, -1, height, width)

    return x


class conv_bn_relu_maxpool(nn.Module):
    def __init__(self, c1, c2):  # ch_in, ch_out
        super(conv_bn_relu_maxpool, self).__init__()
        self.conv = nn.Sequential(
            nn.Conv2d(c1, c2, kernel_size=3, stride=2, padding=1, bias=False),
            nn.BatchNorm2d(c2),
            nn.ReLU(inplace=True),
        )
        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1, dilation=1, ceil_mode=False)

    def forward(self, x):
        return self.maxpool(self.conv(x))


class Shuffle_Block(nn.Module):
    def __init__(self, inp, oup, stride):
        super(Shuffle_Block, self).__init__()

        if not (1 <= stride <= 3):
            raise ValueError('illegal stride value')
        self.stride = stride

        branch_features = oup // 2
        assert (self.stride != 1) or (inp == branch_features << 1)

        if self.stride > 1:
            self.branch1 = nn.Sequential(
                self.depthwise_conv(inp, inp, kernel_size=3, stride=self.stride, padding=1),
                nn.BatchNorm2d(inp),
                nn.Conv2d(inp, branch_features, kernel_size=1, stride=1, padding=0, bias=False),
                nn.BatchNorm2d(branch_features),
                nn.ReLU(inplace=True),
            )

        self.branch2 = nn.Sequential(
            nn.Conv2d(inp if (self.stride > 1) else branch_features,
                      branch_features, kernel_size=1, stride=1, padding=0, bias=False),
            nn.BatchNorm2d(branch_features),
            nn.ReLU(inplace=True),
            self.depthwise_conv(branch_features, branch_features, kernel_size=3, stride=self.stride, padding=1),
            nn.BatchNorm2d(branch_features),
            nn.Conv2d(branch_features, branch_features, kernel_size=1, stride=1, padding=0, bias=False),
            nn.BatchNorm2d(branch_features),
            nn.ReLU(inplace=True),
        )

    @staticmethod
    def depthwise_conv(i, o, kernel_size, stride=1, padding=0, bias=False):
        return nn.Conv2d(i, o, kernel_size, stride, padding, bias=bias, groups=i)

    def forward(self, x):
        if self.stride == 1:
            x1, x2 = x.chunk(2, dim=1)  # 按照维度1进行split
            out = torch.cat((x1, self.branch2(x2)), dim=1)
        else:
            out = torch.cat((self.branch1(x), self.branch2(x)), dim=1)

        out = channel_shuffle(out, 2)

        return out

ShuffleNet 的主要流程涉及一系列精心设计的操作,以最大限度地提高计算效率,同时确保模型的表达能力。以下是 ShuffleNet 的主要流程:

1. 初始卷积层

  1. 输入处理

    • 输入图像首先通过一个标准的卷积层进行处理,这个卷积层的作用是提取初始特征。

  2. 特征图大小和通道数调整

    • 初始卷积层通常会调整特征图的大小和通道数,以便于后续层的处理。

2. 分组卷积(Grouped Convolution)

  1. 特征图分组

    • 特征图在通道维度上分成多个组。分组卷积将特征图分为若干小组,并对每一小组单独进行卷积操作。这种方式可以大幅减少计算量。

  2. 1x1 卷积(Pointwise Convolution)

    • 使用 1x1 卷积调整每一组特征图的通道数。1x1 卷积主要用于减少通道数,以减小计算量。

3. 通道打乱(Channel Shuffle)

  1. 通道打乱操作

    • 为了确保不同组之间的信息可以相互交流,使用通道打乱操作。具体来说,将特征图的通道重新排列,以便在下一次分组卷积时,不同组之间的信息能够充分混合。

4. 深度可分离卷积(Depthwise Separable Convolution)

  1. 深度卷积(Depthwise Convolution)

    • 在深度卷积中,每个卷积核只在单个通道上进行卷积操作,不改变通道数。这样可以显著减少计算量。

  2. 逐点卷积(Pointwise Convolution)

    • 在深度卷积之后,使用逐点卷积(1x1 卷积)来调整通道数。这一步结合了所有通道的信息。

5. 残差连接(Residual Connection)

  1. 特征图合并

    • 将原始输入特征图与经过上述处理后的特征图通过元素级加法(或拼接操作)合并。这一步类似于残差网络(ResNet)中的残差连接,帮助梯度在网络中更好地传播。

6. 通道分割(Channel Split)

  1. 特征图分割

    • 在某些版本中(如 ShuffleNetV2),特征图被分成两部分,一部分直接传递到下一层,另一部分经过复杂操作后再与第一部分合并。这种设计进一步减少了计算量。

7. 多次重复上述模块

  1. 模块重复

    • 上述操作模块会被重复多次,以构建深层网络。这些模块的重复次数和具体配置会根据网络的设计需求而定。

8. 最终卷积层和分类层

  1. 全局池化(Global Pooling)

    • 在网络的最后,通常会使用全局平均池化层,将特征图的空间维度压缩到单个数值。

  2. 全连接层

    • 最后,通过一个全连接层(或完全卷积层),将特征映射到分类标签空间,以完成分类任务。

2.2 新增yaml文件

关键步骤二在下/projects/yolov5-6.1/models下新建文件 yolov5-shufflenetv2.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

# YOLOv5 v6.0 backbone
backbone:
  # [from, number, module, args]
  # Shuffle_Block: [out, stride]
  [[ -1, 1, conv_bn_relu_maxpool, [ 32 ] ], # 0-P2/4
   [ -1, 1, Shuffle_Block, [ 128, 2 ] ],  # 1-P3/8
   [ -1, 3, Shuffle_Block, [ 128, 1 ] ],  # 2
   [ -1, 1, Shuffle_Block, [ 256, 2 ] ],  # 3-P4/16
   [ -1, 7, Shuffle_Block, [ 256, 1 ] ],  # 4
   [ -1, 1, Shuffle_Block, [ 512, 2 ] ],  # 5-P5/32
   [ -1, 3, Shuffle_Block, [ 512, 1 ] ],  # 6
  ]

# YOLOv5 v6.0 head
head:
  [[-1, 1, Conv, [256, 1, 1]],
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
   [[-1, 4], 1, Concat, [1]],  # cat backbone P4
   [-1, 1, C3, [256, False]],  # 10

   [-1, 1, Conv, [128, 1, 1]],
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
   [[-1, 2], 1, Concat, [1]],  # cat backbone P3
   [-1, 1, C3, [128, False]],  # 14 (P3/8-small)

   [-1, 1, Conv, [128, 3, 2]],
   [[-1, 11], 1, Concat, [1]],  # cat head P4
   [-1, 1, C3, [256, False]],  # 17 (P4/16-medium)

   [-1, 1, Conv, [256, 3, 2]],
   [[-1, 7], 1, Concat, [1]],  # cat head P5
   [-1, 1, C3, [512, False]],  # 20 (P5/32-large)

   [[14, 17, 20], 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行左右分别添加 ‘ conv_bn_relu_maxpool, Shuffle_Block’

2.4 执行程序

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

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

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

 3. 完整代码分享

https://pan.baidu.com/s/1EKQQOCdwIUWrR3LcFaCk8A?pwd=aaus

提取码: aaus 

4. GFLOPs

关于GFLOPs的计算方式可以查看:百面算法工程师 | 卷积基础知识——Convolution

未改进的GFLOPs

img

改进后的GFLOPs

5.总结

ShuffleNet的设计理念主要集中在提高计算效率和减少参数量,同时保持较高的准确性。为了实现这一目标,ShuffleNet引入了两个关键的操作:点卷积(Pointwise Convolution)和通道洗牌(Channel Shuffle)。首先,ShuffleNet采用了一种被称为深度可分离卷积(Depthwise Separable Convolution)的操作。这种操作将标准卷积分解为两个更简单的操作:深度卷积(Depthwise Convolution)和点卷积(Pointwise Convolution)。深度卷积在每个输入通道上独立执行卷积操作,而点卷积则用于将这些独立处理后的通道进行线性组合。通过这种分解方式,计算量和参数量都得到了大幅度的减少。在此基础上,ShuffleNet进一步引入了通道洗牌的概念。在常规的卷积操作中,不同通道之间的信息交流是有限的。为了增强通道间的信息交流,ShuffleNet将通道划分为若干组,并在每一组内进行卷积操作后,将通道重新排列(洗牌),使得下一组操作能够接收到来自不同组的通道信息。这样,通过多层的叠加和洗牌操作,网络能够更有效地捕捉不同通道之间的关联特征,从而提升模型的表现力。此外,ShuffleNet采用了分组卷积(Grouped Convolution)的策略,即将通道划分为多个组,每组独立进行卷积操作。结合通道洗牌,这种策略进一步降低了计算复杂度和参数量,适合在移动设备等资源受限的环境中应用。总的来说,ShuffleNet通过巧妙地设计卷积操作和通道处理方式,显著提升了计算效率和模型的轻量化程度,使得其在保持较高准确率的同时,能够在计算资源有限的设备上高效运行。这种创新的架构设计不仅为移动设备上的实时图像处理提供了可能,也为其他需要高效视觉处理的应用场景提供了有力支持。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/674903.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

直流电机工作原理与控制电路解析

工作原理&#xff1a; 洛伦兹力原理&#xff1a; 直流电机中&#xff0c;定子上通电产生磁场&#xff0c;而转子内导体通电后在磁场中受到洛伦兹力的作用&#xff0c;使其产生转动力矩。 转子内的导体通电后会在磁场中受到力的作用&#xff0c;根据洛伦兹力的方向规则&#…

【metricbeat】通过metricbeat采集prometheus指标

通过metricbeat采集prometheus指标 通过beat采集prometheus内的单个指标。 低版本beat只能全量 环境 # 低版本metricbeat只能全量采集 软件版本&#xff1a;metricbeat8.11.1 解压 tar zxvf metricbeat-8.11.1-linux-x86_64.tar.gz -C /usr/local配置 首先&#xff0c;修改…

数据库索引的理解

目录 1.索引是什么&#xff0c;解决了什么问题 2.索引付出了什么代价 3.如何使用sql索引&#xff0c;有何注意事项 普通索引&#xff1a; 唯一索引&#xff1a; 主键索引(Primary Key Index)&#xff1a; 删除索引: 创建主键索引的基本语法: 4.索引背后的数据结构 1.索…

Spring Boot 集成 zxing 生成条形码与二维码

前面我们知道了怎么通过 使用 zxing 生成二维码以及条形码&#xff0c; 由于我们现在都是 web 端的项目了&#xff0c;那么我们看下怎么使用 Spring Boot 集成然后返回给前端展示&#xff1a; 工程源码 对应的工程源码我放到了这里&#xff1a;github源码路径&#xff0c;点击…

80V高耐压低静态线性稳压器/LDO,Vout 1v-65v 3.3V及5V方案最佳选择

概述 PC93XX系列专为动力而设计-敏感应用程序。它包括一个精度第二个高压输入级&#xff0c;超低功率 偏置电流分支&#xff0c;并产生超低功率和低压差线性调节器。PC93XX通过输入电压工作VOUT1V至65V&#xff0c;仅消耗1.8μA的静态电流&#xff0c;并提供1%的初始精度和低…

Maven项目打包成jar项目后运行报错误: 找不到或无法加载主类 Main.Main 和 jar中没有主清单属性解决方案

已经用maven工程的package功能进行了打包 找不到或无法加载主类 Main.Main 规定主类 主要在maven的配置文件当中 这边一定要绑定自己的启动类 jar中没有主清单属性 删掉这一行就行哈 正确的插件代码 <plugin><groupId>org.springframework.boot</groupId&…

孩子出生后为什么要做听力筛查?

孩子出生后为什么要做听力筛查&#xff1f; 新生儿听力筛查&#xff0c;就是对所有新生儿在尽早的时间&#xff08;出生48小时后&#xff09;进行系统的听力筛查测试。据相关文献报道&#xff0c;在我国&#xff0c;正常分娩的新生儿听力障碍的发生率约为0.1&#xff5e;0.3%&a…

gomail发送邮件的参数如何设置?如何使用?

gomail发送邮件的认证方式有哪些&#xff1f;怎么设置邮件发信&#xff1f; Gomail是一个常用的Go语言邮件发送库&#xff0c;它提供了简单易用的接口&#xff0c;使得邮件发送变得非常方便。AokSend将详细介绍如何设置gomail发送邮件的参数&#xff0c;帮助开发者更好地理解和…

Window11开放端口

&#xff08;1&#xff09;打开控制面板&#xff0c;进入【控制面板\系统和安全\Windows Defender 防火墙】 &#xff08;2&#xff09;点击左侧菜单【高级设置】&#xff0c;进入防火墙设置页面 &#xff08;3&#xff09;根据需要选择【入站规则】或者【出站规则】&#xff…

《猎杀:对决》是适合什么样的人玩 Mac电脑怎么玩《猎杀:对决》

《猎杀&#xff1a;对决》是一款集合了生存、竞技和恐怖元素的多人在线游戏&#xff0c;自推出以来受到了广大玩家的热爱。本文将详细探讨《猎杀&#xff1a;对决》适合什么样的人玩以及Mac电脑怎么玩《猎杀&#xff1a;对决》。本文将一一解析&#xff0c;帮助你了解这款游戏是…

全球家纺热潮持续!有哪些家纺爆款类目推荐入驻沃尔玛?

在全球家纺热潮中&#xff0c;选择沃尔玛家纺爆款类目入驻是明智之举。沃尔玛作为全球零售巨头&#xff0c;拥有庞大的消费者群体和销售渠道&#xff0c;对家纺产品销售具有显著优势。 ​以下是一些推荐的家纺爆款类目&#xff0c;供您参考&#xff1a; 一、高品质床品套件 床…

迪普微震撼发布:全新Xilinx Kintex-7 XC7K325 FPGA开发板!

01 产品概述 本公司基于Xilinx Kintex-7系列的开发平台采用核心板加扩展主板的方式&#xff0c;方便用户对相关板卡的二次开发利用。 其中&#xff0c;核心板包含一片芯片XC7K325T&#xff0c;使用FFG900封装&#xff0c;外挂4片512MB的高速DDR3芯片和1片256Mb的QSPI Flash芯…

mysql 如果有按照时间范围查询结果

1.前端传2024-06-21 到我们xml sql 文件中默认实际是 2024-06-21 00:00:00 <if test"dto.startTime ! null">and ps.created_at > #{dto.startTime}</if><if test"dto.endTime ! null">and ps.created_at < #{dto.endTime}</if…

(2) qml诞生的原因 和Qt Creator开发环境的介绍

文章目录 qml诞生原因Qt Quick应⽤程序Qt Creator环境1、MSVC2、MinGWMSVC的优缺点MinGW的优缺点 最后的选择延伸阅读 一些常用的快捷键统一格式化代码统一qml 语言的格式Locator 定位器帮助 qml诞生原因 可以在Qt5中开发的不同类型的经典应⽤程序。桌⾯应⽤程 序正在发⽣着改…

如何制作不限扫描次数的视频二维码?1分钟教程

不可否认的是&#xff0c;视频二维码正在各行各业发挥重要作用。不论是在线上宣传、线下活动&#xff0c;还是产品包装、展览展会上&#xff0c;只需要扫描视频二维码&#xff0c;就能在手机端获得专业的视频教程、查看品牌宣传视频、了解活动流程、快速获取关键信息等&#xf…

AI精选付费资料包【37GB】

课程介绍 一、人工智能论文合集 二、AI必读经典书籍 三、超详细人工智能学习大纲 四、机器学习基础算法教程 五、深度学习神经网络基础教程 六、计算机视觉实战项目 课程获取 资料&#xff1a;AI精选付费资料包&#xff08;37.4GB&#xff09;获取&#xff1a;扫码关注公z号…

msconfig修改引导导致电脑无法进入系统 和 启动蓝屏问题

目录 解决方法遇到的问题开机蓝屏进入【指定UFEI固件设置】进入bios启动系统选择界面&#xff08;忘了拍照&#xff09; 解决方法 找到一个移动U盘&#xff08;最好大于10G&#xff09;格式化&#xff1b;找一个好的电脑安装WEPE软件&#xff08;wepe安装链接&#xff09;&…

[leetcode hot150]第五十七题,插入区间

题目&#xff1a; 给你一个 无重叠的 &#xff0c;按照区间起始端点排序的区间列表 intervals&#xff0c;其中 intervals[i] [starti, endi] 表示第 i 个区间的开始和结束&#xff0c;并且 intervals 按照 starti 升序排列。同样给定一个区间 newInterval [start, end] 表示…

解决odbc 数据源创建之后删除失败问题

问题描述 该问题一般存在于用32位odbc管理程序删除了64位管理程序创建的数据源&#xff0c;被提示失败&#xff0c;然后再用64位odbc管理程序删除时发现删除不掉。怎么都删除不掉&#xff0c;不会影响体验&#xff0c;但是很膈应 问题解决 将该路径下的对应的ODBC数据源&…

判断JavaScript对象是否为空,最佳方法与性能对比

引言&#xff1a;在日常开发中&#xff0c;你是否经常需要判断一个对象是否为空&#xff1f;你知道有哪些高效的方法可以做到这一点吗&#xff1f;今天&#xff0c;我们不仅要探讨这些方法&#xff0c;还要通过性能对比找出最优解。快来看看&#xff0c;你平常用的方法是最佳选…