YOLOv5改进 | 主干篇 | ShuffleNetV1轻量化网络助力FPS提高(附代码+修改教程)

news2025/2/5 12:54:08

一、本文内容

本文给大家带来的改进内容是ShuffleNetV1,这是一种为移动设备设计的高效CNN架构。它通过使用点群卷积和通道混洗等操作,减少了计算成本,同时保持了准确性,通过这些技术,ShuffleNet在降低计算复杂度的同时,也优化了内存使用,使其更适合低功耗的移动设备,其非常适合轻量化的读者来使用)。本文通过介绍其主要框架原理,然后展示其效果图(基础版本和修改了本文改进机制的mAP对比图),然后手把手教你如何添加该网络结构到网络模型中。

适用检测目标:这个模型非常适合轻量化的读者来使用。

推荐指数:⭐⭐⭐⭐

 专栏回顾:YOLOv5改进专栏——持续复现各种顶会内容——内含100+创新

效果回顾展示->

(注意轻量化网络结构掉点是正常现象)

目录

一、本文内容

二、ShuffleNetV1框架原理​编辑​

三、ShuffleNetV1核心代码

 四、手把手教你添加ShuffleNetV1网络结构

修改一

修改二

修改三

修改四

修改五

修改五 

修改六 

修改七

五、ShuffleNetV1的yaml文件

六、成功运行记录 

七、本文总结


二、ShuffleNetV1框架原理

官方论文地址: 官方论文地址

官方代码地址: 官方代码地址


ShuffleNet的创新机制为点群卷积和通道混:使用了新的操作点群卷积(pointwise group convolution)和通道混洗(channel shuffle),以减少计算成本,同时保持网络精度

您上传的图片展示的是ShuffleNet架构中的通道混洗机制。这一机制通过两个堆叠的分组卷积(GConv)来实现:

图示(a):展示了两个具有相同分组数量的堆叠卷积层。每个输出通道仅与同一组内的输入通道相关联。
图示(b):
在不使用通道混洗的情况下,展示了在GConv1之后,GConv2从不同分组获取数据时输入和输出通道是如何完全相关联的。
图示(c:提供了与(b)相同的实现,但使用了通道混洗来允许跨组通信,从而使网络内更有效和强大的特征学习成为可能。

上面的图片描述了ShuffleNet架构中的ShuffleNet单元。这些单元是网络中的基本构建块,具体包括:

图示(a):一个基本的瓶颈单元,使用了深度可分离卷积(DWConv)和一个简单的加法(Add)来融合特征。
图示(b):在标准瓶颈单元的基础上,引入了点群卷积(GConv)和通道混洗操作,以增强特征的表达能力。
图示(c):适用于空间下采样的ShuffleNet单元,使用步长为2的平均池化(AVG Pool)和深度可分离卷积,再通过通道混洗和点群卷积进一步处理特征,最后通过连接操作(Concat)合并特征。


三、ShuffleNetV1核心代码

下面的代码是整个ShuffleNetV1的核心代码,其中有个版本,对应的GFLOPs也不相同,使用方式看章节四。

# Copyright 2022 Dakewe Biotech Corporation. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
from typing import Any, List, Optional

import torch
from torch import Tensor
from torch import nn

__all__ = [
    "ShuffleNetV1",
    "shufflenet_v1_x0_5", "shufflenet_v1_x1_0", "shufflenet_v1_x1_5", "shufflenet_v1_x2_0",
]


class ShuffleNetV1(nn.Module):

    def __init__(
            self,
            repeats_times: List[int],
            stages_out_channels: List[int],
            groups: int = 8,
            num_classes: int = 1000,
    ) -> None:
        super(ShuffleNetV1, self).__init__()
        in_channels = stages_out_channels[0]

        self.first_conv = nn.Sequential(
            nn.Conv2d(3, in_channels, (3, 3), (2, 2), (1, 1), bias=False),
            nn.BatchNorm2d(in_channels),
            nn.ReLU(True),
        )
        self.maxpool = nn.MaxPool2d((3, 3), (2, 2), (1, 1))

        features = []
        for state_repeats_times_index in range(len(repeats_times)):
            out_channels = stages_out_channels[state_repeats_times_index + 1]

            for i in range(repeats_times[state_repeats_times_index]):
                stride = 2 if i == 0 else 1
                first_group = state_repeats_times_index == 0 and i == 0
                features.append(
                    ShuffleNetV1Unit(
                        in_channels,
                        out_channels,
                        stride,
                        groups,
                        first_group,
                    )
                )
                in_channels = out_channels
        self.features = nn.Sequential(*features)

        self.globalpool = nn.AvgPool2d((7, 7))

        self.classifier = nn.Sequential(
            nn.Linear(stages_out_channels[-1], num_classes, bias=False),
        )

        # Initialize neural network weights
        self._initialize_weights()
        self.index = stages_out_channels[-3:]
        self.width_list = [i.size(1) for i in self.forward(torch.randn(1, 3, 640, 640))]

    def forward(self, x: Tensor) -> list[Optional[Any]]:
        x = self.first_conv(x)
        x = self.maxpool(x)
        results = [None, None, None, None]
        for index, model in enumerate(self.features):
            x = model(x)
            # results.append(x)
            if index == 0:
                results[index] = x
            if x.size(1) in self.index:
                position = self.index.index(x.size(1))  # Find the position in the index list
                results[position + 1] = x
        return results

    def _initialize_weights(self) -> None:
        for name, module in self.named_modules():
            if isinstance(module, nn.Conv2d):
                if 'first' in name:
                    nn.init.normal_(module.weight, 0, 0.01)
                else:
                    nn.init.normal_(module.weight, 0, 1.0 / module.weight.shape[1])
                if module.bias is not None:
                    nn.init.constant_(module.bias, 0)
            elif isinstance(module, nn.BatchNorm2d):
                nn.init.constant_(module.weight, 1)
                if module.bias is not None:
                    nn.init.constant_(module.bias, 0.0001)
                nn.init.constant_(module.running_mean, 0)
            elif isinstance(module, nn.BatchNorm1d):
                nn.init.constant_(module.weight, 1)
                if module.bias is not None:
                    nn.init.constant_(module.bias, 0.0001)
                nn.init.constant_(module.running_mean, 0)
            elif isinstance(module, nn.Linear):
                nn.init.normal_(module.weight, 0, 0.01)
                if module.bias is not None:
                    nn.init.constant_(module.bias, 0)


class ShuffleNetV1Unit(nn.Module):
    def __init__(
            self,
            in_channels: int,
            out_channels: int,
            stride: int,
            groups: int,
            first_groups: bool = False,
    ) -> None:
        super(ShuffleNetV1Unit, self).__init__()
        self.stride = stride
        self.groups = groups
        self.first_groups = first_groups
        hidden_channels = out_channels // 4

        if stride == 2:
            out_channels -= in_channels
            self.branch_proj = nn.AvgPool2d((3, 3), (2, 2), (1, 1))

        self.branch_main_1 = nn.Sequential(
            # pw
            nn.Conv2d(in_channels, hidden_channels, (1, 1), (1, 1), (0, 0), groups=1 if first_groups else groups,
                      bias=False),
            nn.BatchNorm2d(hidden_channels),
            nn.ReLU(True),
            # dw
            nn.Conv2d(hidden_channels, hidden_channels, (3, 3), (stride, stride), (1, 1), groups=hidden_channels,
                      bias=False),
            nn.BatchNorm2d(hidden_channels),
        )
        self.branch_main_2 = nn.Sequential(
            # pw-linear
            nn.Conv2d(hidden_channels, out_channels, (1, 1), (1, 1), (0, 0), groups=groups, bias=False),
            nn.BatchNorm2d(out_channels),
        )

        self.relu = nn.ReLU(True)

    def channel_shuffle(self, x):
        batch_size, channels, height, width = x.data.size()
        assert channels % self.groups == 0
        group_channels = channels // self.groups

        out = x.reshape(batch_size, group_channels, self.groups, height, width)
        out = out.permute(0, 2, 1, 3, 4)
        out = out.reshape(batch_size, channels, height, width)

        return out

    def forward(self, x: Tensor) -> Tensor:
        identify = x

        out = self.branch_main_1(x)
        out = self.channel_shuffle(out)
        out = self.branch_main_2(out)

        if self.stride == 2:
            branch_proj = self.branch_proj(x)
            out = self.relu(out)
            out = torch.cat([branch_proj, out], 1)
            return out
        else:
            out = torch.add(out, identify)
            out = self.relu(out)
            return out


def shufflenet_v1_x0_5(**kwargs: Any) -> ShuffleNetV1:
    model = ShuffleNetV1([4, 8, 4], [16, 192, 384, 768], 8, **kwargs)

    return model


def shufflenet_v1_x1_0(**kwargs: Any) -> ShuffleNetV1:
    model = ShuffleNetV1([4, 8, 4], [24, 384, 768, 1536], 8, **kwargs)

    return model


def shufflenet_v1_x1_5(**kwargs: Any) -> ShuffleNetV1:
    model = ShuffleNetV1([4, 8, 4], [24, 576, 1152, 2304], 8, **kwargs)

    return model


def shufflenet_v1_x2_0(**kwargs: Any) -> ShuffleNetV1:
    model = ShuffleNetV1([4, 8, 4], [48, 768, 1536, 3072], 8, **kwargs)

    return model


if __name__ == "__main__":

    # Generating Sample image
    image_size = (1, 3, 640, 640)
    image = torch.rand(*image_size)

    # Model
    model = shufflenet_v1_x0_5()

    out = model(image)
    print(out)

 四、手把手教你添加ShuffleNetV1网络结构

这个主干的网络结构添加起来算是所有的改进机制里最麻烦的了,因为有一些网略结构可以用yaml文件搭建出来,有一些网络结构其中的一些细节根本没有办法用yaml文件去搭建,用yaml文件去搭建会损失一些细节部分(而且一个网络结构设计很多细节的结构修改方式都不一样,一个一个去修改大家难免会出错),所以这里让网络直接返回整个网络,然后修改部分 yolo代码以后就都以这种形式添加了,以后我提出的网络模型基本上都会通过这种方式修改,我也会进行一些模型细节改进。创新出新的网络结构大家直接拿来用就可以的。下面开始添加教程->

(同时每一个后面都有代码,大家拿来复制粘贴替换即可,但是要看好了不要复制粘贴替换多了)


修改一

我们复制网络结构代码到“yolov5-master/models”目录下创建一个目录,我这里的名字是modules(如果将文件放在models下面随着改进机制越来越多不太好区分,所以创建一个文件目录将改进机制全部放在里面) ,然后创建一个py文件将代码复制粘贴到里面我这里起的名字是ShuffleNetV1。


修改二

然后我们在我们创建的目录里面创建一个初始化文件'__init__.py',然后在里面导入我们同级目录的所有改进机制

​​

修改三

我们找到如下文件'models/yolo.py'在开头里面导入我们的模块,这里需要注意要将代码放在common导入的文件上面,否则有一些模块会使用我们modules里面的,可能同名导致报错,这里如果你使用多个我的改进机制填写一个即可,不用重复添加。


修改四

添加如下两行代码,根据行数找相似的代码进行添加


修改五

找到七百多行大概把具体看图片,按照图片来修改就行,添加红框内的部分,注意没有()只是函数名,我这里只添加了部分的版本,大家有兴趣这个ShuffleNetV1还有更多的版本可以添加,看我给的代码函数头即可。

        elif m in {自行添加对应的模型即可,下面都是一样的}:
            m = m()
            c2 = m.width_list  # 返回通道列表
            backbone = True


修改五 

下面的两个红框内都是需要改动的。 

        if isinstance(c2, list):
            m_ = m
            m_.backbone = True
        else:
            m_ = nn.Sequential(*(m(*args) for _ in range(n))) if n > 1 else m(*args)  # module
            t = str(m)[8:-2].replace('__main__.', '')  # module type


        np = sum(x.numel() for x in m_.parameters())  # number params
        m_.i, m_.f, m_.type, m_.np = i + 4 if backbone else i, f, t, np # attach index, 'from' index, type


修改六 

如下的也需要修改,全部按照我的来。

代码如下把原先的代码替换了即可。 

        save.extend(x % (i + 4 if backbone else i) for x in ([f] if isinstance(f, int) else f) if x != -1)  # append to savelist
        layers.append(m_)
        if i == 0:
            ch = []
        if isinstance(c2, list):
            ch.extend(c2)
            if len(c2) != 5:
                ch.insert(0, 0)
        else:
            ch.append(c2)

修改七

修改七和前面的都不太一样,需要修改前向传播中的一个部分, 已经离开了parse_model方法了。

可以在图片中开代码行数,没有离开task.py文件都是同一个文件。 同时这个部分有好几个前向传播都很相似,大家不要看错了,是70多行左右的!!!,同时我后面提供了代码,大家直接复制粘贴即可,有时间我针对这里会出一个视频。

找到如下的代码,这里不太好找,我给大家上传一个原始的样子。

然后我们用后面的代码进行替换,替换完之后的样子如下-> 

​​

代码如下->

    def _forward_once(self, x, profile=False, visualize=False):
        y, dt = [], []  # outputs
        for m in self.model:
            if m.f != -1:  # if not from previous layer
                x = y[m.f] if isinstance(m.f, int) else [x if j == -1 else y[j] for j in m.f]  # from earlier layers
            if profile:
                self._profile_one_layer(m, x, dt)
            if hasattr(m, 'backbone'):
                x = m(x)
                if len(x) != 5:  # 0 - 5
                    x.insert(0, None)
                for index, i in enumerate(x):
                    if index in self.save:
                        y.append(i)
                    else:
                        y.append(None)
                x = x[-1]  # 最后一个输出传给下一层
            else:
                x = m(x)  # run
                y.append(x if m.i in self.save else None)  # save output
            if visualize:
                feature_visualization(x, m.type, m.i, save_dir=visualize)
        return x

到这里就完成了修改部分,但是这里面细节很多,大家千万要注意不要替换多余的代码,导致报错,也不要拉下任何一部,都会导致运行失败,而且报错很难排查!!!很难排查!!! 

五、ShuffleNetV1的yaml文件

复制如下yaml文件进行运行!!! 

# YOLOv5 🚀 by Ultralytics, AGPL-3.0 license

# Parameters
nc: 80  # number of classes
depth_multiple: 0.33  # model depth multiple
width_multiple: 0.25  # 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

backbone:
  # [from, number, module, args]
  [[-1, 1, shufflenet_v1_x1_0, []],  # 0-4-P1/
   [-1, 1, SPPF, [1024, 5]],  # 5
  ]

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

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

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

   [-1, 1, Conv, [512, 3, 2]],
   [[-1, 5], 1, Concat, [1]],  # cat head P5
   [-1, 3, C3, [1024, False]],  # 19 (P5/32-large)

   [[13, 16, 19], 1, Detect, [nc, anchors]],  # Detect(P3, P4, P5)
  ]


六、成功运行记录 

下面是成功运行的截图,已经完成了有1个epochs的训练,图片太大截不全第2个epochs了。 

​ 


七、本文总结

到此本文的正式分享内容就结束了,在这里给大家推荐我的YOLOv5改进有效涨点专栏,本专栏目前为新开的平均质量分97分,后期我会根据各种最新的前沿顶会进行论文复现,也会对一些老的改进机制进行补充,目前本专栏免费阅读(暂时,大家尽早关注不迷路~),如果大家觉得本文帮助到你了,订阅本专栏,关注后续更多的更新~

 专栏回顾:YOLOv5改进专栏——持续复现各种顶会内容——内含100+创新

​​​

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C/S医院检验LIS系统源码

一、检验科LIS系统概述: LIS系统即实验室信息管理系统。LIS系统能实现临床检验信息化,检验科信息管理自动化。其主要功能是将检验科的实验仪器传出的检验数据经数据分析后,自动生成打印报告,通过网络存储在数据库中&#xff…

Modbus TCP转Profinet协议网关应用

YC-TCP-PN通讯网关:引领工业通讯新篇章 在工业4.0的浪潮下,高效、稳定的通讯技术成为各行业的核心竞争力。作为行业领军者,我们自豪地推出YC-TCP-PN通讯网关,以其卓越性能打破行业技术壁垒,引领工业通讯进入新篇章。 …

使用 Python 和 PyQt 实现路由算法模拟器

项目地址 GitHub - 944613709/Shortest-Path-Simulation: 网络拓扑图最短路径模拟 引言 在现代网络技术中,路由算法扮演着至关重要的角色。它们决定了数据包在网络中的传输路径,从而影响整个网络的效率和性能。为了更好地理解这些算法的工作原理&…

UG模型的显示与隐藏

在UG中,除了通过图层的方式控制模型的显示与隐藏外,还可以直接通过显示与隐藏命令,位置在菜单-编辑-显示与隐藏,需要注意的是这些命令只能对可视图层中的模型进行控制 显示与隐藏:ctrl w 可以通过模型的类别&#xf…

2014年第三届数学建模国际赛小美赛B题全地形伪装解题全过程文档及程序

2014年第三届数学建模国际赛小美赛 B题 全地形伪装 原题再现: 破坏性着色在军事用途中很常见,用于军用车辆、士兵制服和装备。视觉是人类的主要方向感,伪装的主要功能是欺骗人眼。军事服装中存在大量的伪装图案,以适应作战服装与…

概率论中的 50 个具有挑战性的问题 [第 6 部分]:Chuck-a-Luck

一、说明 我最近对与概率有关的问题产生了兴趣。我偶然读到了弗雷德里克莫斯特勒(Frederick Mosteller)的《概率论中的五十个具有挑战性的问题与解决方案》)一书。我认为创建一个系列来讨论这些可能作为面试问题出现的迷人问题会很有趣。每篇…

审视AI Agent:追捧、落地,和2024年的To B破局点

“十个AI应用里面,五个办公Agent,三个AIGC,还有两成是回春的数字人。”那么,Agent到底是不是大模型的AGI终局产品? 作者|斗斗 编辑|皮爷 出品|产业家 今年4月份,斯坦福和谷歌的研究者共同创建了一个…

账号和权限管理

大家无论安装完linux系统和windows系统后,都会要求你去新建一个用户去登录,不会让你们直接使用超级管理员身份去登录自己的系统,这样是为了安全性的考虑。 一、用户账号和组账号概述 1、用户账号类型: linux用户账号类型&#x…

基于openGauss5.0.0全密态数据库等值查询小案例

基于openGauss5.0.0全密态数据库等值查询小案例 一、全密态数据库简介二、环境说明三、测试步骤四、使用约束 一、全密态数据库简介 价值体现: 密态数据库意在解决数据全生命周期的隐私保护问题,使得系统无论在何种业务场景和环境下,数据在传…

跟着LearnOpenGL学习10--基础光照

文章目录 一、前言二、环境光照三、漫反射光照3.1、法向量3.2、计算漫反射光照3.3、全部代码3.4、法线矩阵 四、镜面光照4.1、全部代码 一、前言 现实世界的光照是极其复杂的,而且会受到诸多因素的影响,这是我们有限的计算能力所无法模拟的。 因此Open…

企业微信自建应用获取用户信息

一.前言 开发企业微信自建应用的时候难免会有获取企微个人信息的业务需求,这篇博客将详细说明企微自建应用获取userId的具体流程. 二.基本概念介绍 2.1 corpid 每个企业都拥有唯一的corpid,获取此信息可在管理后台“我的企业”-“企业信息”下查看“企业…

leetcode 1419. 数青蛙(medium)(优质解法)

链接:力扣(LeetCode)官网 - 全球极客挚的技术成长平台 代码: class Solution {public int minNumberOfFrogs(String croakOfFrogs) {String t"croak";int tLengtht.length();// hash 数组用来存放青蛙喊的过程&#xf…

企业网银 相关注意事项合辑 不断更新中...

山西省农村信用社 (shanxinj.com) 企业网上银行 山西省农村信用社 企业网上银行,注意事项: 1、通过安装【网银向导】修复网银安全控件、密码控件等; 2、登录界面无Ukey验证,也就是输入企业号、用户编号、登录密码及验证码即可进…

ARM12.26

整理三个按键中断代码 key_it.h #ifndef __KEY_IT_H__ #define __KEY_IT_H__ #include"stm32mp1xx_gpio.h" #include"stm32mp1xx_gic.h" #include"stm32mp1xx_exti.h" #include"stm32mp1xx_rcc.h" #include"led.h" void k…

九、Seata的AT模式

目录 9.1 什么是弱一致性 ?9.2 Seata的弱一致性9.3 Seata的AT模式介绍9.4 AT模式流程图9.5 AT模式注意点9.6 全局锁的理解1、认识全局锁2、注册全局锁3、校验(获取)全局锁4、释放锁5、结论 9.7 AT的多数据源场景 9.1 什么是弱一致性 &#xf…

算符优先语法分析设计原理与实现

前言: 作者的词法分析程序以及算符优先语法分析设计程序仓库链接 1、目标任务 **[实验项目] **以专题 1 词法分析程序的输出为语法分析的输入,实现算符优先分析算法,完成以下描述算术表达式的算符优先文法的算符优先分析过程。 G[E]:E→E…

PgSQL技术内幕 - ereport ERROR跳转机制

PgSQL技术内幕 - ereport ERROR跳转机制 使用客户端执行SQL的时候经常遇到报ERROR错误,然后SQL语句就退出了。当然,事务也会回滚掉。本文我们看下它是如何做到退出SQL语句并回滚事务的。 1、以insert一个numeric类型值为例 表一个字段为numeric(10,2)类型…

账号与权限管理

一、Linux用户 1.1用户类型 1.普通用户:权限受到限制的用户 2.超级管理员:至高无上的权限 3.程序用户:是给程序使用的,不允许登录(为了安全性考虑) 能不能打开文件和用户有关,用户有自己的权限 运行程序不能使用超…

关于chatglm3 function calling的理解

ChatGLM3-6B开源了工具调用,好奇他是怎么实现的,所以写了这个文章记录。 一、测试官方的示例 官方给的示例很简单,只不过给的两个函数 track 和 text-to-speech 没有具体的实现,模型的输出也只是给出了需要调用的函数名和参数。剩…