当前位置: 首页 > article >正文

YOLOv8改进 | 模块融合 | C2f融合 ghost + DynamicConv 【两次融合 + 独家改进】

秋招面试专栏推荐 :深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转


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


专栏目录 :《YOLOv8改进有效涨点》专栏介绍 & 专栏目录 | 目前已有100+篇内容,内含各种Head检测头、损失函数Loss、Backbone、Neck、NMS等创新点改进——点击即可跳转


 本文介绍了一种C2f_GhostDynamicConv模块融合GhostModule和DynamicConv,通过在GhostModule结构中使用DynamicConv替换传统卷积,实现了在保持计算效率的同时增强特征表达能力的目的。然后再用GhostModule替换C2f的Bottleneck实现改进。文章在介绍主要的原理后,将手把手教学如何进行模块的代码添加和修改,并将修改后的完整代码放在文章的最后,方便大家一键运行,小白也可轻松上手实践。以帮助您更好地学习深度学习目标检测YOLO系列的挑战。 

专栏地址YOLOv8改进——更新各种有效涨点方法——点击即可跳转  

目录

1. 原理 

2. 将C2f_GhostDynamicConv添加到yolov8网络中

2.1 C2f_GhostDynamicConv 代码实现

2.2 C2f_GhostDynamicConv的神经网络模块代码解析

2.3 更改init.py文件

2.4 添加yaml文件

2.5 注册模块

2.6 执行程序

3. 完整代码分享

4. GFLOPs

5. 进阶

6. 总结


1. 原理 

论文地址:Dynamic Convolution: Attention over Convolution Kernels——点击即可跳转

代码实现: 非官方代码仓库——点击即可跳转

动态卷积是一种旨在提高卷积神经网络 (CNN) 的表示能力而不会显著增加计算成本的技术。动态卷积背后的主要原理如下:

  1. 多个卷积核:动态卷积不是每层使用单个卷积核,而是采用一组 (K) 个并行卷积核。对于每个输入,这些内核使用输入相关的注意权重进行动态聚合。这允许网络根据输入数据调整卷积操作,从而增强其灵活性和表达能力。

  2. 注意机制:每个内核的注意权重\pi_k(x) 都是根据输入 ( x ) 动态计算的。这些权重决定了每个内核在最终聚合的卷积内核中应该有多大的影响。聚合按如下方式执行: W(x) = \sum{k=1}^{K} \pi_k(x) W_k, \quad b(x) = \sum{k=1}^{K} \pi_k(x) b_k , 其中 W_kb_k分别是各个内核权重和偏差。

  3. 效率:尽管使用多个内核会增加模型复杂度,但动态卷积在计算上是高效的。额外的计算主要是由于计算注意力权重和聚合内核,与卷积操作本身相比,这只增加了很小的开销。这使得动态卷积特别适合轻量级 CNN,因为增加深度或宽度(即更多层或通道)的成本太高。

  4. 非线性聚合:通过注意力机制以非线性方式聚合内核,动态卷积可以对数据中更复杂的关系进行建模,而传统的静态卷积则不然。

  5. 训练注意事项:动态卷积网络 (DY-CNN) 更难训练,因为它们需要跨层联合优化所有内核和注意力权重。建议采用两种关键策略来促进训练:

  • 和为一约束:强制约束 \sum_{k=1}^{K} \pi_k(x) = 1 有助于更有效地学习注意力模型。

  • 早期训练中的扁平注意力:在早期训练阶段使用 softmax 函数中的高温可产生更均匀的注意力权重,从而帮助网络更好地学习。

动态卷积已被证明可以显著提高 MobileNetV2 和 MobileNetV3 等模型在 ImageNet 分类等任务上的性能,而计算成本仅略有增加。

2. 将C2f_GhostDynamicConv添加到yolov8网络中

2.1 C2f_GhostDynamicConv 代码实现

关键步骤一将下面代码粘贴到在/ultralytics/ultralytics/nn/modules/block.py中,并在该文件的__all__中添加“C2f_GhostDynamicConv”

import math
from timm.layers import CondConv2d

class DynamicConv_Single(nn.Module):
    """ Dynamic Conv layer
    """
    def __init__(self, in_features, out_features, kernel_size=1, stride=1, padding='', dilation=1,
                 groups=1, bias=False, num_experts=4):
        super().__init__()
        self.routing = nn.Linear(in_features, num_experts)
        self.cond_conv = CondConv2d(in_features, out_features, kernel_size, stride, padding, dilation,
                 groups, bias, num_experts)
        
    def forward(self, x):
        pooled_inputs = F.adaptive_avg_pool2d(x, 1).flatten(1)  # CondConv routing
        routing_weights = torch.sigmoid(self.routing(pooled_inputs))
        x = self.cond_conv(x, routing_weights)
        return x

class DynamicConv(nn.Module):
    default_act = nn.SiLU()  # default activation
    def __init__(self, c1, c2, k=1, s=1, p=None, g=1, d=1, act=True, num_experts=4):
        super().__init__()
        self.conv = nn.Sequential(
            DynamicConv_Single(c1, c2, kernel_size=k, stride=s, padding=autopad(k, p, d), dilation=d, groups=g, num_experts=num_experts),
            nn.BatchNorm2d(c2),
            self.default_act if act is True else act if isinstance(act, nn.Module) else nn.Identity()
        )
    
    def forward(self, x):
        return self.conv(x)

class GhostModule(nn.Module):
    def __init__(self, inp, oup, kernel_size=1, ratio=2, dw_size=3, stride=1, act_layer=nn.SiLU, num_experts=4):
        super(GhostModule, self).__init__()
        self.oup = oup
        init_channels = math.ceil(oup / ratio)
        new_channels = init_channels * (ratio - 1)

        self.primary_conv = DynamicConv(inp, init_channels, kernel_size, stride, num_experts=num_experts)

        self.cheap_operation = DynamicConv(init_channels, new_channels, dw_size, 1, g=init_channels, num_experts=num_experts)

    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 Bottleneck_DynamicConv(Bottleneck):
    def __init__(self, c1, c2, shortcut=True, g=1, k=(3, 3), e=0.5):
        super().__init__(c1, c2, shortcut, g, k, e)
        c_ = int(c2 * e)  # hidden channels
        self.cv2 = DynamicConv(c2, c2, 3)

class C3_DynamicConv(C3):
    def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5):
        super().__init__(c1, c2, n, shortcut, g, e)
        c_ = int(c2 * e)  # hidden channels
        self.m = nn.Sequential(*(Bottleneck_DynamicConv(c_, c_, shortcut, g, k=(1, 3), e=1.0) for _ in range(n)))

class C2f_DynamicConv(C2f):
    def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5):
        super().__init__(c1, c2, n, shortcut, g, e)
        self.m = nn.ModuleList(Bottleneck_DynamicConv(self.c, self.c, shortcut, g, k=(3, 3), e=1.0) for _ in range(n))

class C3_GhostDynamicConv(C3):
    def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5):
        super().__init__(c1, c2, n, shortcut, g, e)
        c_ = int(c2 * e)  # hidden channels
        self.m = nn.Sequential(*(GhostModule(c_, c_) for _ in range(n)))

class C2f_GhostDynamicConv(C2f):
    def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5):
        super().__init__(c1, c2, n, shortcut, g, e)
        self.m = nn.ModuleList(GhostModule(self.c, self.c) for _ in range(n))

2.2 C2f_GhostDynamicConv的神经网络模块代码解析

C2f_GhostDynamicConv 是一个基于 C2f 模块的自定义神经网络模块,结合了 GhostModule 和 Dynamic Convolution 的特性。让我们逐步讲解它的组成和功能:

1. 继承 C2f 模块

  • C2f_GhostDynamicConv 继承自 C2f 类,因此它保留了 C2f 的结构和功能。这意味着它是一个层级结构,通常用于构建包含多个子层的模块。

2. GhostModule 的引入

  • C2f_GhostDynamicConv 通过 nn.ModuleList 创建了多个 GhostModule 实例,这些实例被存储在模块列表中 self.m。这里的 GhostModule 是一个轻量级的卷积模块,它使用了 Dynamic Convolution 来增强表示能力。

3. GhostModule 的细节

  • primary_conv:这是第一个卷积操作,使用 Dynamic Convolution 在输入通道和 init_channels 之间进行卷积。Dynamic Convolution 是一种基于输入数据动态聚合多个卷积核的技术,它使卷积操作更加灵活和自适应。

  • cheap_operation:这是第二个卷积操作,同样使用了 Dynamic Convolution,但在通道数增加的情况下,它通过更小的深度卷积(dw_size)来减少计算成本。

  • 输出GhostModule 的输出是通过 torch.catprimary_convcheap_operation 的结果在通道维度上拼接,并裁剪到所需的输出通道数。

4. 模块的调用

  • C2f_GhostDynamicConv 被调用时,它将依次执行 self.m 中所有 GhostModule 实例的 forward 函数,并根据 C2f 的定义将这些模块的输出聚合。

C2f_GhostDynamicConv 通过整合 Dynamic Convolution 和 GhostModule 的特性,在保持计算高效的前提下,增强了模型的灵活性和表达能力,非常适合用于资源受限的深度学习应用。

2.3 更改init.py文件

关键步骤二:修改modules文件夹下的__init__.py文件,先导入函数   

然后在下面的__all__中声明函数 

2.4 添加yaml文件

关键步骤三:在/ultralytics/ultralytics/cfg/models/v8下面新建文件yolov8_C2f_GhostDynamicConv.yaml文件,粘贴下面的内容

  • OD【目标检测】
# Ultralytics YOLO 🚀, AGPL-3.0 license
# YOLOv8 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=yolov8n.yaml' will call yolov8.yaml with scale 'n'
  # [depth, width, max_channels]
  n: [0.33, 0.25, 1024]  # YOLOv8n summary: 225 layers,  3157200 parameters,  3157184 gradients,   8.9 GFLOPs
  s: [0.33, 0.50, 1024]  # YOLOv8s summary: 225 layers, 11166560 parameters, 11166544 gradients,  28.8 GFLOPs
  m: [0.67, 0.75, 768]   # YOLOv8m summary: 295 layers, 25902640 parameters, 25902624 gradients,  79.3 GFLOPs
  l: [1.00, 1.00, 512]   # YOLOv8l summary: 365 layers, 43691520 parameters, 43691504 gradients, 165.7 GFLOPs
  x: [1.00, 1.25, 512]   # YOLOv8x summary: 365 layers, 68229648 parameters, 68229632 gradients, 258.5 GFLOPs

# YOLOv8.0n backbone
backbone:
  # [from, repeats, module, args]
  - [-1, 1, Conv, [64, 3, 2]]  # 0-P1/2
  - [-1, 1, Conv, [128, 3, 2]]  # 1-P2/4
  - [-1, 3, C2f_GhostDynamicConv, [128, True]]
  - [-1, 1, Conv, [256, 3, 2]]  # 3-P3/8
  - [-1, 6, C2f_GhostDynamicConv, [256, True]]
  - [-1, 1, Conv, [512, 3, 2]]  # 5-P4/16
  - [-1, 6, C2f_GhostDynamicConv, [512, True]]
  - [-1, 1, Conv, [1024, 3, 2]]  # 7-P5/32
  - [-1, 3, C2f_GhostDynamicConv, [1024, True]]
  - [-1, 1, SPPF, [1024, 5]]  # 9

# YOLOv8.0n head
head:
  - [-1, 1, nn.Upsample, [None, 2, 'nearest']]
  - [[-1, 6], 1, Concat, [1]]  # cat backbone P4
  - [-1, 3, C2f_GhostDynamicConv, [512]]  # 12

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

  - [-1, 1, Conv, [256, 3, 2]]
  - [[-1, 12], 1, Concat, [1]]  # cat head P4
  - [-1, 3, C2f_GhostDynamicConv, [512]]  # 18 (P4/16-medium)

  - [-1, 1, Conv, [512, 3, 2]]
  - [[-1, 9], 1, Concat, [1]]  # cat head P5
  - [-1, 3, C2f_GhostDynamicConv, [1024]]  # 21 (P5/32-large)

  - [[15, 18, 21], 1, Detect, [nc]]  # Detect(P3, P4, P5)
  • Seg【语义分割】
# Ultralytics YOLO 🚀, AGPL-3.0 license
# YOLOv8 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=yolov8n.yaml' will call yolov8.yaml with scale 'n'
  # [depth, width, max_channels]
  n: [0.33, 0.25, 1024]  # YOLOv8n summary: 225 layers,  3157200 parameters,  3157184 gradients,   8.9 GFLOPs
  s: [0.33, 0.50, 1024]  # YOLOv8s summary: 225 layers, 11166560 parameters, 11166544 gradients,  28.8 GFLOPs
  m: [0.67, 0.75, 768]   # YOLOv8m summary: 295 layers, 25902640 parameters, 25902624 gradients,  79.3 GFLOPs
  l: [1.00, 1.00, 512]   # YOLOv8l summary: 365 layers, 43691520 parameters, 43691504 gradients, 165.7 GFLOPs
  x: [1.00, 1.25, 512]   # YOLOv8x summary: 365 layers, 68229648 parameters, 68229632 gradients, 258.5 GFLOPs

# YOLOv8.0n backbone
backbone:
  # [from, repeats, module, args]
  - [-1, 1, Conv, [64, 3, 2]]  # 0-P1/2
  - [-1, 1, Conv, [128, 3, 2]]  # 1-P2/4
  - [-1, 3, C2f_GhostDynamicConv, [128, True]]
  - [-1, 1, Conv, [256, 3, 2]]  # 3-P3/8
  - [-1, 6, C2f_GhostDynamicConv, [256, True]]
  - [-1, 1, Conv, [512, 3, 2]]  # 5-P4/16
  - [-1, 6, C2f_GhostDynamicConv, [512, True]]
  - [-1, 1, Conv, [1024, 3, 2]]  # 7-P5/32
  - [-1, 3, C2f_GhostDynamicConv, [1024, True]]
  - [-1, 1, SPPF, [1024, 5]]  # 9

# YOLOv8.0n head
head:
  - [-1, 1, nn.Upsample, [None, 2, 'nearest']]
  - [[-1, 6], 1, Concat, [1]]  # cat backbone P4
  - [-1, 3, C2f_GhostDynamicConv, [512]]  # 12

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

  - [-1, 1, Conv, [256, 3, 2]]
  - [[-1, 12], 1, Concat, [1]]  # cat head P4
  - [-1, 3, C2f_GhostDynamicConv, [512]]  # 18 (P4/16-medium)

  - [-1, 1, Conv, [512, 3, 2]]
  - [[-1, 9], 1, Concat, [1]]  # cat head P5
  - [-1, 3, C2f_GhostDynamicConv, [1024]]  # 21 (P5/32-large)

  - [[15, 18, 21], 1, Segment, [nc, 32, 256]] # Segment(P3, P4, P5)

温馨提示:因为本文只是对yolov8基础上添加模块,如果要对yolov8n/l/m/x进行添加则只需要指定对应的depth_multiple 和 width_multiple。不明白的同学可以看这篇文章: yolov8yaml文件解读——点击即可跳转  


# YOLOv8n
depth_multiple: 0.33  # model depth multiple
width_multiple: 0.25  # layer channel multiple
max_channels: 1024 # max_channels
 
# YOLOv8s
depth_multiple: 0.33  # model depth multiple
width_multiple: 0.50  # layer channel multiple
max_channels: 1024 # max_channels
 
# YOLOv8l 
depth_multiple: 1.0  # model depth multiple
width_multiple: 1.0  # layer channel multiple
max_channels: 512 # max_channels
 
# YOLOv8m
depth_multiple: 0.67  # model depth multiple
width_multiple: 0.75  # layer channel multiple
max_channels: 768 # max_channels
 
# YOLOv8x
depth_multiple: 1.33  # model depth multiple
width_multiple: 1.25  # layer channel multiple
max_channels: 512 # max_channels

2.5 注册模块

关键步骤四:在task.py的parse_model函数中注册

2.6 执行程序

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

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

from ultralytics import YOLO
import warnings
warnings.filterwarnings('ignore')
from pathlib import Path
 
if __name__ == '__main__':
 
 
    # 加载模型
    model = YOLO("ultralytics/cfg/v8/yolov8.yaml")  # 你要选择的模型yaml文件地址
    # Use the model
    results = model.train(data=r"你的数据集的yaml文件地址",
                          epochs=100, batch=16, imgsz=640, workers=4, name=Path(model.cfg).stem)  # 训练模型

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

                   from  n    params  module                                       arguments
  0                  -1  1       464  ultralytics.nn.modules.conv.Conv             [3, 16, 3, 2]
  1                  -1  1      4672  ultralytics.nn.modules.conv.Conv             [16, 32, 3, 2]
  2                  -1  1      3624  ultralytics.nn.modules.block.C2f_GhostDynamicConv[32, 32, 1, True]
  3                  -1  1     18560  ultralytics.nn.modules.conv.Conv             [32, 64, 3, 2]
  4                  -1  2     18320  ultralytics.nn.modules.block.C2f_GhostDynamicConv[64, 64, 2, True]
  5                  -1  1     73984  ultralytics.nn.modules.conv.Conv             [64, 128, 3, 2]
  6                  -1  2     69392  ultralytics.nn.modules.block.C2f_GhostDynamicConv[128, 128, 2, True]
  7                  -1  1    295424  ultralytics.nn.modules.conv.Conv             [128, 256, 3, 2]
  8                  -1  1    200968  ultralytics.nn.modules.block.C2f_GhostDynamicConv[256, 256, 1, True]
  9                  -1  1    164608  ultralytics.nn.modules.block.SPPF            [256, 256, 5]
 10                  -1  1         0  torch.nn.modules.upsampling.Upsample         [None, 2, 'nearest']
 11             [-1, 6]  1         0  ultralytics.nn.modules.conv.Concat           [1]
 12                  -1  1     84104  ultralytics.nn.modules.block.C2f_GhostDynamicConv[384, 128, 1]
 13                  -1  1         0  torch.nn.modules.upsampling.Upsample         [None, 2, 'nearest']
 14             [-1, 4]  1         0  ultralytics.nn.modules.conv.Concat           [1]
 15                  -1  1     21576  ultralytics.nn.modules.block.C2f_GhostDynamicConv[192, 64, 1]
 16                  -1  1     36992  ultralytics.nn.modules.conv.Conv             [64, 64, 3, 2]
 17            [-1, 12]  1         0  ultralytics.nn.modules.conv.Concat           [1]
 18                  -1  1     59528  ultralytics.nn.modules.block.C2f_GhostDynamicConv[192, 128, 1]
 19                  -1  1    147712  ultralytics.nn.modules.conv.Conv             [128, 128, 3, 2]
 20             [-1, 9]  1         0  ultralytics.nn.modules.conv.Concat           [1]
 21                  -1  1    233736  ultralytics.nn.modules.block.C2f_GhostDynamicConv[384, 256, 1]
 22        [15, 18, 21]  1    897664  ultralytics.nn.modules.head.Detect           [80, [64, 128, 256]]
YOLOv8_C2f_GhostDynamicConv summary: 286 layers, 2331328 parameters, 2331312 gradients, 6.5 GFLOPs

3. 完整代码分享

https://pan.baidu.com/s/12d7gkE043OmxAWL24H-FsA?pwd=9cpg

提取码: 9cpg 

4. GFLOPs

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

未改进的YOLOv8nGFLOPs

img

改进后的GFLOPs

5. 进阶

可以与其他的注意力机制或者损失函数等结合,进一步提升检测效果

6. 总结

Dynamic Convolution通过在每一层使用多组并行卷积核,并根据输入数据动态地聚合这些卷积核来增强卷积神经网络的表示能力。通过引入基于输入数据的注意力机制,网络能够自适应地调整每个卷积核的影响力,实现对输入的非线性响应。这种方法在保持网络深度和宽度不变的情况下,提高了模型的复杂度和表达能力。尽管模型中增加了多个卷积核,计算成本的增加主要集中在注意力权重的计算和卷积核的聚合上,相对于卷积操作本身,这些额外的计算量可以忽略不计。因此,Dynamic Convolution特别适用于轻量级神经网络。在训练过程中,使用动态卷积的网络需要同时优化所有卷积核和跨层的注意力权重,这使得训练更加困难。为此,采用了两个关键策略来改善训练效果:一是对注意力输出施加求和为1的约束,以简化注意力模型的学习;二是在训练初期使用高温度的softmax函数,使注意力权重趋于均匀,从而促进卷积核的学习。总体而言,Dynamic Convolution在不显著增加计算量的情况下,显著提升了模型在检测等任务中的表现。


http://www.kler.cn/a/280546.html

相关文章:

  • SQL中NULL值导致NOT IN操作符异常查询的问题
  • 趋动科技联合云轴科技推出GPU云原生超融合解决方案
  • Spring不是引入了三级缓存,解决了循环依赖的问题吗?
  • UE5.4内容示例(5)UI_CommonUI - 学习笔记
  • 如何满足业主多元需求?开发物业APP,打造智能社区生活
  • 大数据技术之Flume事务及内部原理(3)
  • 【JVM】剖析字符串与数组的底层实现(一)
  • 清理linux的buff/cache缓存
  • 搭建面向切面编程项目
  • mysql事务不加锁一致性读
  • Adobe Photoshop 2024 25.5安装与下载教程(直接用)
  • hive客户端
  • Datawhale AI夏令营第五期学习!
  • ArcGIS Pro基础:如何将数据和引用地图样式一起打包分享
  • elasticsearch -- RestClient操作文档
  • 二十三设计模式速记
  • 攻防演练之-最有价值安全工具大巡礼
  • Linux下TCP编程
  • 基于vue框架的便利店收银管理系统im2gw(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。
  • 3个方法快速打开rar压缩包文件