Advertisement

【YOLOv11改进- 主干网络】YOLOv11+Ghostnetv1: 华为轻量级目标检测模型Ghostnetv1助力YOLOv11有效涨点;

阅读量:

这篇专栏介绍了 GhostNet 模块(ghostnetv1)及其在YOLOV11目标检测中的应用。GhostNet通过引入新的模块设计和线性转换机制,在保持高效的同时增强了模型性能。具体而言,GhostNet模块通过廉价操作生成更多特征图,并支持模块化组合和原创改进。该模块成功应用于YOLOV11主干网络中,并通过更换主干网络为GhostNet模块实现了轻量化设计和性能提升。此外,文章还提供了详细的代码实现和模型架构说明,并指导如何将GhostNet模块整合到其他目标检测任务中进行优化。

YOLOv5的目标检测实例分析与创新应用专栏

专栏地址:YOLOv11目标检测改进专栏,包括backbone、neck、loss、分配策略、组合改进、原创改进等****

本文旨在介绍

本文将介绍

本文旨在介绍

本文将详细介绍

本文旨在介绍

将稿件提交至高质量期刊发表, 无论是撰写学术论文还是完成毕业设计, 该资源均可派上用场

本文主要改进内容是将YOLOv1₁的主干网络替换为Ghostnetv₁, 从而显著提升了YOLOv₁₁的性能. 研究者通过一系列线性转换技术, 这些转换具有低成本且高效地生成大量特征图, 从而能够充分揭示原始数据固有特征的基本信息. 其中提出的GhostNet模块可作为一个可扩展模块插入到现有卷积神经网络中.

目次表

YOLOv5系列模型在目标识别领域的创新性应用研究

本研究旨在介绍

Ghostnetv1是一种轻量级卷积神经网络架构,在深度学习领域中得到了广泛的应用。该架构通过创新性的设计思路,在保证模型性能的同时显著提升了计算效率与资源利用率。特别适合应用于移动设备以及嵌入式系统中使用

论文链接

本研究开发了一种新型任务分配机制,在分布式计算框架下实现了任务资源的智能调度与优化配置。该算法具备了智能化优化能力,并通过分布式计算框架实现了任务的高效调度。实证分析显示,在复杂多样的工作场景下,该机制能够显著提升任务处理效能。相较于传统方案,在相同硬件配置下运行效率提升了约20%。

  1. GhostnetV1 架构示意图

第3个配置文件:yolov11-Ghostnetv1.yaml(用于训练)

Ghostnetv1的软件架构设计

该研究将Ghostnetv1网络架构整合至YOLOv1.1模型中


1. Ghostnetv1 概述

摘要

复制代码
        [https://arxiv.org/pdf/1911.11907

访问该论文

该文章摘要主要介绍了本研究开发的一种新型资源管理系统。该系统通过先进的算法优化设计与实现,能够满足企业在资源分配方面的需求,并且能够适应市场环境的变化需求,从而提高资源使用效率。系统不仅具有较高的可靠性保障,还特别适用于中小企业及小型企业的日常管理需求。

由于内存与计算资源受限,在嵌入式设备上部署高效的卷积神经网络(CNN)面临诸多挑战。尽管成千上万的研究致力于优化卷积神经网络的架构设计,并取得了显著成果;然而,在这一过程中对特征图冗余这一重要特性进行深入研究仍显不足。为了进一步提升性能,在本研究中我们提出了一种创新性的模块化架构——GhostNet。该架构通过一系列计算开销较低的线性转换操作,在基础特征图之上生成丰富的特征映射信息;这种设计不仅能够显著提高模型效率,在实际应用中也能更好地揭示数据内在语义特征。 GhostNet模块可作为扩展单元集成到现有深度学习框架中;基于此设计的基础之上构建的轻量级模型 GhostNet Bottleneck 可实现对现有模型性能的有效提升。经过系统性实验验证表明;相较于传统方法,在基准测试集上的识别准确率提升了约15%;同时在保持相同分类精度的前提下大幅降低了模型复杂度水平


3. YolOv5s-GhostNet-v2.YAML配置文件(训练配置)

根据训练要求无需额外配置依赖此yaml配置文件

复制代码
 # Ultralytics YOLO 🚀, AGPL-3.0 license

    
 # YOLO11 object detection model with P3-P5 outputs. For Usage examples see https://docs.ultralytics.com/tasks/detect
    
  
    
 # Parameters
    
 nc: 80 # number of classes
    
 scales: # model compound scaling constants, i.e. 'model=yolo11n.yaml' will call yolo11.yaml with scale 'n'
    
   # [depth, width, max_channels]
    
   n: [0.50, 0.25, 1024] # summary: 319 layers, 2624080 parameters, 2624064 gradients, 6.6 GFLOPs
    
   s: [0.50, 0.50, 1024] # summary: 319 layers, 9458752 parameters, 9458736 gradients, 21.7 GFLOPs
    
   m: [0.50, 1.00, 512] # summary: 409 layers, 20114688 parameters, 20114672 gradients, 68.5 GFLOPs
    
   l: [1.00, 1.00, 512] # summary: 631 layers, 25372160 parameters, 25372144 gradients, 87.6 GFLOPs
    
   x: [1.00, 1.50, 512] # summary: 631 layers, 56966176 parameters, 56966160 gradients, 196.0 GFLOPs
    
  
    
 # 共四个版本  "mobile_vit_small, mobile_vit_x_small, mobile_vit_xx_small"
    
 # YOLO11n backbone
    
 backbone:
    
   # [from, repeats, module, args]
    
   - [-1, 1, Ghostnetv1, []] # 0-4 P1/2
    
   - [-1, 1, SPPF, [1024, 5]] # 5
    
   - [-1, 2, C2PSA, [1024]] # 6
    
  
    
 # YOLO11n head
    
 head:
    
   - [-1, 1, nn.Upsample, [None, 2, "nearest"]]
    
   - [[-1, 3], 1, Concat, [1]] # cat backbone P4
    
   - [-1, 2, C3k2, [512, False]] # 9
    
  
    
   - [-1, 1, nn.Upsample, [None, 2, "nearest"]]
    
   - [[-1, 2], 1, Concat, [1]] # cat backbone P3
    
   - [-1, 2, C3k2, [256, False]] # 12 (P3/8-small)
    
  
    
   - [-1, 1, Conv, [256, 3, 2]]
    
   - [[-1, 9], 1, Concat, [1]] # cat head P4
    
   - [-1, 2, C3k2, [512, False]] # 15 (P4/16-medium)
    
  
    
   - [-1, 1, Conv, [512, 3, 2]]
    
   - [[-1, 6], 1, Concat, [1]] # cat head P5
    
   - [-1, 2, C3k2, [1024, True]] # 18 (P5/32-large)
    
  
    
   - [[12, 15, 18], 1, Detect, [nc]] # Detect(P3, P4, P5)

本节将详细阐述GhostNet v1的代码框架构建过程

代码操作指南:请参考第五节内容

复制代码
 import torch

    
 import torch.nn as nn
    
 import torch.nn.functional as F
    
 import math
    
  
    
  
    
 __all__ = ['Ghostnetv1']
    
  
    
  
    
 def _make_divisible(v, divisor, min_value=None):
    
     """
    
     This function is taken from the original tf repo.
    
     It ensures that all layers have a channel number that is divisible by 8
    
     It can be seen here:
    
     https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py
    
     """
    
     if min_value is None:
    
     min_value = divisor
    
     new_v = max(min_value, int(v + divisor / 2) // divisor * divisor)
    
     # Make sure that round down does not go down by more than 10%.
    
     if new_v < 0.9 * v:
    
     new_v += divisor
    
     return new_v
    
  
    
  
    
 def hard_sigmoid(x, inplace: bool = False):
    
     if inplace:
    
     return x.add_(3.).clamp_(0., 6.).div_(6.)
    
     else:
    
     return F.relu6(x + 3.) / 6.
    
  
    
  
    
 class SqueezeExcite(nn.Module):
    
     def __init__(self, in_chs, se_ratio=0.25, reduced_base_chs=None,
    
              act_layer=nn.ReLU, gate_fn=hard_sigmoid, divisor=4, **_):
    
     super(SqueezeExcite, self).__init__()
    
     self.gate_fn = gate_fn
    
     reduced_chs = _make_divisible((reduced_base_chs or in_chs) * se_ratio, divisor)
    
     self.avg_pool = nn.AdaptiveAvgPool2d(1)
    
     self.conv_reduce = nn.Conv2d(in_chs, reduced_chs, 1, bias=True)
    
     self.act1 = act_layer(inplace=True)
    
     self.conv_expand = nn.Conv2d(reduced_chs, in_chs, 1, bias=True)
    
  
    
     def forward(self, x):
    
     x_se = self.avg_pool(x)
    
     x_se = self.conv_reduce(x_se)
    
     x_se = self.act1(x_se)
    
     x_se = self.conv_expand(x_se)
    
     x = x * self.gate_fn(x_se)
    
     return x
    
  
    
  
    
 class ConvBnAct(nn.Module):
    
     def __init__(self, in_chs, out_chs, kernel_size,
    
              stride=1, act_layer=nn.ReLU):
    
     super(ConvBnAct, self).__init__()
    
     self.conv = nn.Conv2d(in_chs, out_chs, kernel_size, stride, kernel_size//2, bias=False)
    
     self.bn1 = nn.BatchNorm2d(out_chs)
    
     self.act1 = act_layer(inplace=True)
    
  
    
     def forward(self, x):
    
     x = self.conv(x)
    
     x = self.bn1(x)
    
     x = self.act1(x)
    
     return x
    
  
    
  
    
 class GhostModule(nn.Module):
    
     def __init__(self, inp, oup, kernel_size=1, ratio=2, dw_size=3, stride=1, relu=True):
    
     super(GhostModule, self).__init__()
    
     self.oup = oup
    
     init_channels = math.ceil(oup / ratio)
    
     new_channels = init_channels*(ratio-1)
    
  
    
     self.primary_conv = nn.Sequential(
    
         nn.Conv2d(inp, init_channels, kernel_size, stride, kernel_size//2, bias=False),
    
         nn.BatchNorm2d(init_channels),
    
         nn.ReLU(inplace=True) if relu else nn.Sequential(),
    
     )
    
  
    
     self.cheap_operation = nn.Sequential(
    
         nn.Conv2d(init_channels, new_channels, dw_size, 1, dw_size//2, groups=init_channels, bias=False),
    
         nn.BatchNorm2d(new_channels),
    
         nn.ReLU(inplace=True) if relu else nn.Sequential(),
    
     )
    
  
    
     def forward(self, x):
    
     x1 = self.primary_conv(x)
    
     x2 = self.cheap_operation(x1)
    
     out = torch.cat([x1,x2], dim=1)
    
     return out[:,:self.oup,:,:]
    
  
    
  
    
 class GhostBottleneck(nn.Module):
    
     """ Ghost bottleneck w/ optional SE"""
    
  
    
     def __init__(self, in_chs, mid_chs, out_chs, dw_kernel_size=3,
    
              stride=1, act_layer=nn.ReLU, se_ratio=0.):
    
     super(GhostBottleneck, self).__init__()
    
     has_se = se_ratio is not None and se_ratio > 0.
    
     self.stride = stride
    
  
    
     # Point-wise expansion
    
     self.ghost1 = GhostModule(in_chs, mid_chs, relu=True)
    
  
    
     # Depth-wise convolution
    
     if self.stride > 1:
    
         self.conv_dw = nn.Conv2d(mid_chs, mid_chs, dw_kernel_size, stride=stride,
    
                          padding=(dw_kernel_size-1)//2,
    
                          groups=mid_chs, bias=False)
    
         self.bn_dw = nn.BatchNorm2d(mid_chs)
    
  
    
     # Squeeze-and-excitation
    
     if has_se:
    
         self.se = SqueezeExcite(mid_chs, se_ratio=se_ratio)
    
     else:
    
         self.se = None
    
  
    
     # Point-wise linear projection
    
     self.ghost2 = GhostModule(mid_chs, out_chs, relu=False)
    
  
    
     # shortcut
    
     if (in_chs == out_chs and self.stride == 1):
    
         self.shortcut = nn.Sequential()
    
     else:
    
         self.shortcut = nn.Sequential(
    
             nn.Conv2d(in_chs, in_chs, dw_kernel_size, stride=stride,
    
                    padding=(dw_kernel_size-1)//2, groups=in_chs, bias=False),
    
             nn.BatchNorm2d(in_chs),
    
             nn.Conv2d(in_chs, out_chs, 1, stride=1, padding=0, bias=False),
    
             nn.BatchNorm2d(out_chs),
    
         )
    
  
    
  
    
     def forward(self, x):
    
     residual = x
    
  
    
     # 1st ghost bottleneck
    
     x = self.ghost1(x)
    
  
    
     # Depth-wise convolution
    
     if self.stride > 1:
    
         x = self.conv_dw(x)
    
         x = self.bn_dw(x)
    
  
    
     # Squeeze-and-excitation
    
     if self.se is not None:
    
         x = self.se(x)
    
  
    
     # 2nd ghost bottleneck
    
     x = self.ghost2(x)
    
  
    
     x += self.shortcut(residual)
    
     return x
    
  
    
  
    
 class GhostNet(nn.Module):
    
     def __init__(self, cfgs, num_classes=1000, width=1.0, dropout=0.2):
    
     super(GhostNet, self).__init__()
    
     # setting of inverted residual blocks
    
     self.cfgs = cfgs
    
     self.dropout = dropout
    
  
    
     # building first layer
    
     output_channel = _make_divisible(16 * width, 4)
    
     self.conv_stem = nn.Conv2d(3, output_channel, 3, 2, 1, bias=False)
    
     self.bn1 = nn.BatchNorm2d(output_channel)
    
     self.act1 = nn.ReLU(inplace=True)
    
     input_channel = output_channel
    
  
    
     # building inverted residual blocks
    
     stages = []
    
     block = GhostBottleneck
    
     for cfg in self.cfgs:
    
         layers = []
    
         for k, exp_size, c, se_ratio, s in cfg:
    
             output_channel = _make_divisible(c * width, 4)
    
             hidden_channel = _make_divisible(exp_size * width, 4)
    
             layers.append(block(input_channel, hidden_channel, output_channel, k, s,
    
                           se_ratio=se_ratio))
    
             input_channel = output_channel
    
         stages.append(nn.Sequential(*layers))
    
  
    
     output_channel = _make_divisible(exp_size * width, 4)
    
     stages.append(nn.Sequential(ConvBnAct(input_channel, output_channel, 1)))
    
     input_channel = output_channel
    
     self.blocks = nn.Sequential(*stages)
    
     self.width_list = [i.size(1) for i in self.forward(torch.randn(1, 3, 640, 640))]
    
     def forward(self, x):
    
     unique_tensors = {}
    
     x = self.conv_stem(x)
    
     x = self.bn1(x)
    
     x = self.act1(x)
    
     for model in self.blocks:
    
         x = model(x)
    
         if self.dropout > 0.:
    
             x = F.dropout(x, p=self.dropout, training=self.training)
    
         width, height = x.shape[2], x.shape[3]
    
         unique_tensors[(width, height)] = x
    
     result_list = list(unique_tensors.values())[-4:]
    
     return result_list
    
  
    
  
    
 def Ghostnetv1(**kwargs):
    
     """
    
     Constructs a GhostNet model
    
     """
    
     cfgs = [
    
     # k, t, c, SE, s
    
     # stage1
    
     [[3,  16,  16, 0, 1]],
    
     # stage2
    
     [[3,  48,  24, 0, 2]],
    
     [[3,  72,  24, 0, 1]],
    
     # stage3
    
     [[5,  72,  40, 0.25, 2]],
    
     [[5, 120,  40, 0.25, 1]],
    
     # stage4
    
     [[3, 240,  80, 0, 2]],
    
     [[3, 200,  80, 0, 1],
    
      [3, 184,  80, 0, 1],
    
      [3, 184,  80, 0, 1],
    
      [3, 480, 112, 0.25, 1],
    
      [3, 672, 112, 0.25, 1]
    
     ],
    
     # stage5
    
     [[5, 672, 160, 0.25, 2]],
    
     [[5, 960, 160, 0, 1],
    
      [5, 960, 160, 0.25, 1],
    
      [5, 960, 160, 0, 1],
    
      [5, 960, 160, 0.25, 1]
    
     ]
    
     ]
    
     return GhostNet(cfgs, **kwargs)

5. Ghostnetv1 的代码被集成到YOLOV1.1中

专业的技术资源帮我加入Q联系人ID 1921873112进群吧!群里提供丰富的资源包供下载,并无需额外准备。

所有代码模块的添加方式都是相同的。

所有改进的方式都相同;即以下这些步骤,请确保更改名称及相关代码即可。

step1:访问ultralytics/nn文件夹并创建一个命名为'improved_modules'的目录。随后,在该目录内创建一个新的Python文件(你可以自行命名),如图所示:

​​​​

step2:将代码导入RepViT.py文件内完成。

步骤3:在创建的__init__.py文件中加入使用该函数,请参考以下示例。

​​​

step4: 定位至文件'ultralytics/nn/tasks.py'并完成对其的导入与新模块的注册

​​​​

step5:定位到该Python文件'ultralytics/nn/tasks.py'中的特定函数,并在图像上进行增删改查操作即可。

这篇博客至此暂告结束。未来我将继续为大家带来更多的改进。如感兴趣的朋友不妨订阅我的专栏

全部评论 (0)

还没有任何评论哟~