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

一、本文内容

本文给大家带来的改进内容是ShuffleNetV2,这是一种为移动设备设计的高效CNN架构。其在ShuffleNetV1的基础上强调除了FLOPs之外,还应考虑速度、内存访问成本和平台特性。(我在YOLOv5n上修改该主干降低了GFLOPs,但是参数量还是有一定上涨,其非常适合轻量化的读者来使用)。本文通过介绍其主要框架原理,然后展示其效果图(基础版本和修改了本文改进机制的mAP对比图)然后手把手教你如何添加该网络结构到网络模型中。

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

推荐指数:⭐⭐⭐⭐⭐

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

效果回顾展示->

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

目录

一、本文内容

二、ShuffleNetV2框架原理​

三、ShuffleNetV2核心代码

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

修改一

修改二

修改三

修改四

修改五

修改五 

修改六 

修改七

五、ShuffleNetV2的yaml文件

六、成功运行记录 

七、本文总结


二、ShuffleNetV2框架原理

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

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


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)合并特征。


三、ShuffleNetV2核心代码

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

import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.autograd import Variable
from collections import OrderedDict
from torch.nn import init
import math


def conv_bn(inp, oup, stride):
    return nn.Sequential(
        nn.Conv2d(inp, oup, 3, stride, 1, bias=False),
        nn.BatchNorm2d(oup),
        nn.ReLU(inplace=True)
    )


def conv_1x1_bn(inp, oup):
    return nn.Sequential(
        nn.Conv2d(inp, oup, 1, 1, 0, bias=False),
        nn.BatchNorm2d(oup),
        nn.ReLU(inplace=True)
    )


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 InvertedResidual(nn.Module):
    def __init__(self, inp, oup, stride, benchmodel):
        super(InvertedResidual, self).__init__()
        self.benchmodel = benchmodel
        self.stride = stride
        assert stride in [1, 2]

        oup_inc = oup // 2

        if self.benchmodel == 1:
            # assert inp == oup_inc
            self.banch2 = nn.Sequential(
                # pw
                nn.Conv2d(oup_inc, oup_inc, 1, 1, 0, bias=False),
                nn.BatchNorm2d(oup_inc),
                nn.ReLU(inplace=True),
                # dw
                nn.Conv2d(oup_inc, oup_inc, 3, stride, 1, groups=oup_inc, bias=False),
                nn.BatchNorm2d(oup_inc),
                # pw-linear
                nn.Conv2d(oup_inc, oup_inc, 1, 1, 0, bias=False),
                nn.BatchNorm2d(oup_inc),
                nn.ReLU(inplace=True),
            )
        else:
            self.banch1 = nn.Sequential(
                # dw
                nn.Conv2d(inp, inp, 3, stride, 1, groups=inp, bias=False),
                nn.BatchNorm2d(inp),
                # pw-linear
                nn.Conv2d(inp, oup_inc, 1, 1, 0, bias=False),
                nn.BatchNorm2d(oup_inc),
                nn.ReLU(inplace=True),
            )

            self.banch2 = nn.Sequential(
                # pw
                nn.Conv2d(inp, oup_inc, 1, 1, 0, bias=False),
                nn.BatchNorm2d(oup_inc),
                nn.ReLU(inplace=True),
                # dw
                nn.Conv2d(oup_inc, oup_inc, 3, stride, 1, groups=oup_inc, bias=False),
                nn.BatchNorm2d(oup_inc),
                # pw-linear
                nn.Conv2d(oup_inc, oup_inc, 1, 1, 0, bias=False),
                nn.BatchNorm2d(oup_inc),
                nn.ReLU(inplace=True),
            )

    @staticmethod
    def _concat(x, out):
        # concatenate along channel axis
        return torch.cat((x, out), 1)

    def forward(self, x):
        if 1 == self.benchmodel:
            x1 = x[:, :(x.shape[1] // 2), :, :]
            x2 = x[:, (x.shape[1] // 2):, :, :]
            out = self._concat(x1, self.banch2(x2))
        elif 2 == self.benchmodel:
            out = self._concat(self.banch1(x), self.banch2(x))

        return channel_shuffle(out, 2)


class ShuffleNetV2(nn.Module):
    def __init__(self, n_class=1000, input_size=224, width_mult=1.):
        super(ShuffleNetV2, self).__init__()

        assert input_size % 32 == 0

        self.stage_repeats = [8, 4, 4]
        # index 0 is invalid and should never be called.
        # only used for indexing convenience.
        if width_mult == 0.5:
            self.stage_out_channels = [-1, 24, 48, 96, 192, 1024]
        elif width_mult == 1.0:
            self.stage_out_channels = [-1, 24, 116, 232, 464, 1024]
        elif width_mult == 1.5:
            self.stage_out_channels = [-1, 24, 176, 352, 704, 1024]
        elif width_mult == 2.0:
            self.stage_out_channels = [-1, 24, 224, 488, 976, 2048]
        else:
            raise ValueError(
                """groups is not supported for
                       1x1 Grouped Convolutions""")

        # building first layer
        input_channel = self.stage_out_channels[1]
        self.conv1 = conv_bn(3, input_channel, 2)
        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)

        self.features = []
        # building inverted residual blocks
        for idxstage in range(len(self.stage_repeats)):
            numrepeat = self.stage_repeats[idxstage]
            output_channel = self.stage_out_channels[idxstage + 2]
            for i in range(numrepeat):
                if i == 0:
                    # inp, oup, stride, benchmodel):
                    self.features.append(InvertedResidual(input_channel, output_channel, 2, 2))
                else:
                    self.features.append(InvertedResidual(input_channel, output_channel, 1, 1))
                input_channel = output_channel

        # make it nn.Sequential
        self.features = nn.Sequential(*self.features)

        self.index = self.stage_out_channels[2: 2 + len(self.stage_repeats)]
        self.width_list = [i.size(1) for i in self.forward(torch.randn(1, 3, 640, 640))]
    def forward(self, x):
        x = self.conv1(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 shufflenetv2(width_mult=1.):
    model = ShuffleNetV2(width_mult=width_mult)
    return model


if __name__ == "__main__":
    """Testing
    """
    model = ShuffleNetV2()
    print(model)

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

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

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


修改一

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


修改二

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

​​

修改三

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


修改四

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


修改五

找到七百多行大概把具体看图片,按照图片来修改就行,添加红框内的部分,注意没有()只是函数名,我这里只添加了部分的版本,大家有兴趣这个ShuffleNetV2还有更多的版本可以添加,这个ShuffleNetV2的版本集成在类里,所以可以修改模型宽度来改编版本即可,想改的可以看一下模型代码就明白。

        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

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

五、ShuffleNetV2的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, ShuffleNetV2, []],  # 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+创新

​​​

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

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

相关文章

『JavaScript』全面掌握JavaScript数组的操作、方法与高级技巧

📣读完这篇文章里你能收获到 学习JavaScript中数组的基本操作掌握JavaScript数组的多种内置方法了解JavaScript中的数组扩展运算符、Array.from()和Array.of()等实用技巧熟悉如何在JavaScript中使用数组方法进行数据处理 文章目录 一、基本操作1. 创建数组2. 访问和…

【Spring实战】07 JPA

文章目录 1. 定义2. 出现原因3. 添加依赖4. 使用1)创建 Repository 接口2)自定义查询方法(非必须)3)创建实体类4)调用方法 5. 验证6. 优点7. 缺点8. 详细代码总结 1. 定义 Spring Data JPA 是 Spring 提供…

IP 地址归属地查询

IP 地址归属地查询 1. IP 地址归属地查询2. IP 地址归属地查询References 1. IP 地址归属地查询 https://tool.lu/ip/index.html 2. IP 地址归属地查询 https://www.ip.cn/ip/.html References [1] Yongqiang Cheng, https://yongqiang.blog.csdn.net/

OpenAI GPT 模型 API 接口新增参数 top_logprobs 和 logprobs

文章目录 一、前言二、主要内容三、总结 🍉 CSDN 叶庭云:https://yetingyun.blog.csdn.net/ 一、前言 在最新的 OpenAI 官方 APIs 文档中,Create chat completion 中新增了 top_logprobs 和 logprobs 这两个关键参数。 官方文档地址&#xff…

掌握ElasticSearch(一):Elasticsearch安装与配置、Kibana安装

文章目录 〇、简介1.Elasticsearch简介2.典型业务场景3.数据采集工具4.名词解释 一、安装1.使用docker(1)创建虚拟网络(2)Elasticsearch安装步骤 2.使用压缩包 二、配置1.目录介绍2.配置文件介绍3.elasticsearch.yml节点配置4.jvm.options堆配置 二、可视化工具Kibana1.介绍2.安…

MuJava提供的方法级别的7类变异算子总结

MuJava简洁 Java (muJava) 是 Java 程序的变异系统。 它自动生成用于传统突变测试和类级别突变测试的突变体。 Java 可以测试单个类和多个类的包。 用户以对封装在单独 JUnit 类的方法中的被测类的方法调用序列的形式提供测试。 官网地址:Java Home Page 归档表格…

计算机网络面试题目

概述: 1、协议和服务之间的区别(协议是水平的,服务是垂直的) 2、计算机网络有那些层 应用层通过主机中进程的交互完成特定的网络应用。运输层提供两台主机间的进程之间的通信。网络层对传输层传下来的数据包封装,通过路…

使用 GPT4V+AI Agent 做自动 UI 测试的探索

一、背景 从 Web 诞生之日起,UI 自动化就成了测试的难点,到现在近 30 年,一直没有有效的手段解决Web UI测试的问题,尽管发展了很多的 webdriver 驱动,图片 diff 驱动的工具,但是这些工具的投入产出比一直被…

VMware虚拟机的安装配置

目录 一. VMware虚拟机的安装 二. VMware配置虚拟机 三. VMware安装windows server 2012 一. VMware虚拟机的安装 1. 双击安装,点击下一步 2. 勾选接受许可,点击下一步 3. 选择安装位置,点击下一步 4. 用户体验设置(可选&#…

【三维目标检测】【自动驾驶】IA-BEV:基于结构先验和自增强学习的实例感知三维目标检测(AAAI 2024)

系列文章目录 论文:Instance-aware Multi-Camera 3D Object Detection with Structural Priors Mining and Self-Boosting Learning 地址:https://arxiv.org/pdf/2312.08004.pdf 来源:复旦大学 英特尔Shanghai Key Lab /美团 文章目录 系列文…

张江智荟毁约offer

毕业8年后,找工作被国企歧视学历!已经收到了offer,在入职前一周被通知要撤回offer,拒绝录用,理由居然是他们只要本科211以上的人 这是我今天(2023-12-26)亲身经历的事,听说过面试前…

string类的函数讲解

标准库中的string类 首先关于string类的了解,我先给出官方的string类的讲解,以便于大家的学习:链接: http://www.cplusplus.com/reference/string/string/?kwstring 这个网站是C官方网站,里面对于各个关键字和库函数的讲解都是很…

鸿蒙开发中的一些小问题

这是我在学习鸿蒙开发中遇见的小问题 Q1&#xff1a;This custom component must have a build function. <etsLint>Q2&#xff1a;page_title is not translated into en_US(American English)Q3&#xff1a;Module "../CustomComponent/CustomButton" declar…

【论文解读】Learning based fast H.264 to H.265 transcoding

时间&#xff1a; 2015 年 级别&#xff1a; APSIPA 机构&#xff1a; 上海电力大学 摘要 新提出的视频编码标准HEVC (High Efficiency video coding)以其比H.264/AVC更好的编码效率&#xff0c;被工业界和学术界广泛接受和采用。在HEVC实现了约40%的编码效率提升的同时&…

CP2102 驱动安装指南

现在的电脑上已经很少有串口了&#xff0c;在嵌入式开发中经常使用 USB 转串口芯片作为电脑与嵌入式板卡通信的桥梁&#xff0c;CP2102 是一款常见的高端 USB 转串口芯片&#xff0c;要在电脑上使用该芯片功能需要安装驱动程序。 驱动下载 从 官网下载&#xff1a; 驱动安装 安…

HTML5+CSS3+JS小实例:网页手电筒

实例:网页手电筒 技术栈:HTML+CSS+JS 效果: 源码: 【HTML】 <!DOCTYPE html> <html lang="zh-CN"> <head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><m…

DBAPI个人版如何升级到企业版

安装好企业版软件&#xff0c;并启动 注意要新建mysql数据库&#xff0c;执行新版本的ddl_mysql.sql脚本 在旧版本系统中分别导出数据源、分组、API&#xff0c;得到3个json文件 注意全选所有的数据导出 在新版本系统中导入数据源 在新版本系统中导入分组 进入分组管理菜单&…

ioDraw AI:思维导图、流程图、序列图、类图、饼图,一应俱全

前言 在信息爆炸的时代&#xff0c;我们每天接收着大量的信息&#xff0c;如何高效地整理和呈现这些信息成为了一项重要的挑战。思维导图作为一种可视化思维工具&#xff0c;能够帮助我们快速构建和整理复杂的信息结构&#xff0c;便于我们理解和记忆。ioDraw AI绘图工具正是基…

跟着LearnOpenGL学习12--光照贴图

文章目录 一、前言二、漫反射贴图三、镜面光贴图3.1、采样镜面光贴图 一、前言 在跟着LearnOpenGL学习11–材质中&#xff0c;我们讨论了让每个物体都拥有自己独特的材质从而对光照做出不同的反应的方法。这样子能够很容易在一个光照的场景中给每个物体一个独特的外观&#xf…

Java学习:多线程编程

一、概念 进程&#xff1a;它是运行中的程序。有的程序启动后可能有多个进程。Java程序的执行时&#xff0c;首先启动一个独立的JVM进程。该进程任务是解析并执行Java字节码。进程各有独立地址空间&#xff0c;JVM进程间不能访问对方所拥有内存空间。 线程&#xff1a;一个进程…