YOLOv5改进系列:升级版ResNet的新主干网络DenseNet

news/2024/7/11 0:47:23 标签: YOLO, 人工智能, 深度学习

一、论文理论

论文地址:Densely Connected Convolutional Networks

1.理论思想

DenseNet最大化前后层信息交流,通过建立前面所有层与后面层的密集连接,实现了特征在通道维度上的复用,不但减缓了梯度消失的现象,也使其可以在参数与计算量更少的情况下实现比ResNet更优的性能

2.创新点

操作过程:

  • 每一个Bottleneck输出的特征通道数是相同的,例如这里的K=32。同时可以看到,经过concat操作后的通道数是按K的增长量增加的,因此这个K也被称为GrowthRate。
  • 这里1×1卷积的作用是固定输出通道数,达到降维的作用,1×1卷积输出的通道数通常是GrowthRate的4倍。当几十个Bottleneck相连接时,concat后的通道数会增加到上千,如果不增加1×1的卷积来降维,后续3×3卷积所需的参数量会急剧增加。比如,输入通道数64,增长率K=32,经过15个Bottleneck,通道数输出为64+15*32=544,再经过第16个Bottleneck时,如果不使用1×1卷积,第16个Bottleneck层参数量是3*3*544*32=156672,如果使用1×1卷积,第16个Bottleneck层参数量是1*1*544*128+3*3*128*32=106496,可以看到参数量大大降低。
  • Dense Block采用了激活函数在前、卷积层在后的顺序,即BN-ReLU-Conv的顺序,这种方式也被称为pre-activation。通常的模型relu等激活函数处于卷积conv、批归一化batchnorm之后,即Conv-BN-ReLU,也被称为post-activation。作者证明,如果采用post-activation设计,性能会变差。想要更清晰的了解pre-activition,可以参考我的博客ResNet残差网络及变体详解中的Pre Activation ResNet。

二、代码部署

1.代码

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.utils.checkpoint as cp
from torch.jit.annotations import List

from timm.models.layers import BatchNormAct2d

def autopad(k, p=None):  # kernel, padding
    # Pad to 'same'
    if p is None:
        p = k // 2 if isinstance(k, int) else [x // 2 for x in k]  # auto-pad
    return p


class Conv(nn.Module):
    # Standard convolution iscyy
    def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True):  # ch_in, ch_out, kernel, stride, padding, groups
        super().__init__()
        self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False)
        self.bn = nn.BatchNorm2d(c2)
        self.act = nn.SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity())

    def forward(self, x):
        return self.act(self.bn(self.conv(x)))

    def forward_fuse(self, x):
        return self.act(self.conv(x))

class DenseLayer(nn.Module):
    def __init__(
            self, int_numss, gr, bs, norm_layer=BatchNormAct2d,
            drop_rate=0., memory_efficient=False):
        super(DenseLayer, self).__init__()
        self.add_module('norm1', norm_layer(int_numss)),
        self.add_module('conv1', nn.Conv2d(
            int_numss, bs * gr, kernel_size=1, stride=1, bias=False)),
        self.add_module('norm2', norm_layer(bs * gr)),
        self.add_module('conv2', nn.Conv2d(
            bs * gr, gr, kernel_size=3, stride=1, padding=1, bias=False)),
        self.drop_rate = float(drop_rate)
        self.memory_efficient = memory_efficient

    def bottleneck_fn(self, xs):
        concated_features = torch.cat(xs, 1)
        bottleneck_output = self.conv1(self.norm1(concated_features))  # noqa: T484
        return bottleneck_output

    def any_requires_grad(self, x):
        for tensor in x:
            if tensor.requires_grad:
                return True
        return False

    @torch.jit.unused  # noqa: T484
    def call_checkpoint_bottleneck(self, x):
        def closure(*xs):
            return self.bottleneck_fn(xs)

        return cp.checkpoint(closure, *x)

    @torch.jit._overload_method  # mango noqa: F811
    def forward(self, x):
        pass

    @torch.jit._overload_method  # noqa: F811
    def forward(self, x):
        pass

    def forward(self, x):  # noqa: F811 iscyy/mango
        if isinstance(x, torch.Tensor):
            prev_features = [x]
        else:
            prev_features = x

        if self.memory_efficient and self.any_requires_grad(prev_features):
            if torch.jit.is_scripting():
                raise Exception("Memory Efficient not supported in JIT")
            bottleneck_output = self.call_checkpoint_bottleneck(prev_features)
        else:
            bottleneck_output = self.bottleneck_fn(prev_features)

        new_features = self.conv2(self.norm2(bottleneck_output))
        if self.drop_rate > 0:
            new_features = F.dropout(new_features, p=self.drop_rate, training=self.training)
        return new_features

class DenseBlock(nn.ModuleDict):
    _version = 2
    def __init__(
            self, int_numss, gr, num_layers, bs=4, norm_layer=nn.ReLU,
            drop_rate=0., memory_efficient=False):
        super(DenseBlock, self).__init__()
        for i in range(num_layers):
            layer = DenseLayer(
                int_numss + i * gr,
                gr=gr,
                bs=bs,
                norm_layer=norm_layer,
                drop_rate=drop_rate,
                memory_efficient=memory_efficient,
            )
            self.add_module('denselayer%d' % (i + 1), layer)

    def forward(self, init_features):
        features = [init_features]
        for name, layer in self.items():
            new_features = layer(features)
            features.append(new_features)
        
        return torch.cat(features, 1)


class DenseTrans(nn.Sequential):
    def __init__(self, int_numss, out_numss, kernel_size, norm_layer=nn.BatchNorm2d, aa_layer=None,  act=True):
        super(DenseTrans, self).__init__()
        self.conv = nn.Conv2d(
            int_numss, out_numss, kernel_size=kernel_size, stride=1)
        self.bn = nn.BatchNorm2d(out_numss)
        self.act = self.act = nn.SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity())
        
    def forward(self, x):
        return self.act(self.bn(self.conv(x)))
    def forward_fuse(self, x):
        return self.act(self.conv(x))

class DenseB(nn.Module):
    def __init__(self, c1, c2, gr, num_layers=6):
        super().__init__()
        self.dense = DenseBlock(c1, gr, num_layers)
        self.con = DenseTrans(c1 + gr * num_layers, c2, 1 ,1)

    def forward(self, x):
        x = self.con(self.dense(x))
        return x

class DenseC(nn.Module):
    def __init__(self, c1, c2, gr, num_layers=6):
        super().__init__()
        self.dense = DenseBlock(c1, gr, num_layers)
        self.con = DenseTrans(c1 + gr * num_layers, c2, 1 ,1)

        self.dense2 = DenseBlock(c1, gr, num_layers)
        self.con2 = DenseTrans(c1 + gr * num_layers, c2, 1 ,1)

    def forward(self, x):
        x = self.con(self.dense(x))
        x = self.con2(self.dense2(x))
        return x

class DenseOne(nn.Module):
    def __init__(self, c1, c2, n=1, gr=32, e=0.5):
        super().__init__()
        c_ = int(c2 * e)
        self.cv1 = Conv(c1, c_, 1, 1)
        self.cv2 = Conv(c1, c_, 1, 1)
        self.cv3 = Conv(2 * c_, c2, 1)
        self.m = nn.Sequential(*(DenseB(c_, c_, gr=gr, num_layers=6) for _ in range(n)))

    def forward(self, x):
        return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), dim=1))

class DenseOneC(nn.Module):
    def __init__(self, c1, c2, n=1, gr=32, e=0.5):
        super().__init__()
        c_ = int(c2 * e)
        self.cv1 = Conv(c1, c_, 1, 1)
        self.cv2 = Conv(c1, c_, 1, 1)
        self.cv3 = Conv(2 * c_, c2, 1)
        self.m = nn.Sequential(*(DenseC(c_, c_, gr=gr, num_layers=6) for _ in range(n)))

    def forward(self, x):
        return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), dim=1))

2.配置教程

(1)在models/cmmon.py中添加上述代码,将与初始代码中重复类删除

(2)在./models/yolo.py文件下里的parse_model函数,将类名加入进去

           for i, (f, n, m, args) in enumerate(d['backbone'] + d['head']):内部

        elif m in [DenseOne, DenseOneC]:
            c1, c2 = ch[f], args[0]
            if c2 != no:  # if not output
                c2 = make_divisible(c2 * gw, 8)

            args = [c1, c2, *args[1:]]
            if m in [DenseOne, DenseOneC]:
                args.insert(2, n)  # number of repeats
                n = 1

3.yaml文件

 YOLOv5 🚀 by Ultralytics, GPL-3.0 license

# Parameters
nc: 2  # number of classes
depth_multiple: 0.33  # model depth multiple
width_multiple: 0.5  # 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  by mango
backbone:
  # [from, number, module, args]
  [[-1, 1, Conv, [64, 6, 2, 2]],  # 0-P1/2
   [-1, 1, Conv, [128, 3, 2]],  # 1-P2/4
   [-1, 3, C3, [128]],
   [-1, 1, Conv, [256, 3, 2]],  # 3-P3/8
   [-1, 6, C3, [256]],
   [-1, 1, Conv, [512, 3, 2]],  # 5-P4/16
   [-1, 9, C3, [512]],
   [-1, 1, Conv, [1024, 3, 2]],  # 7-P5/32
   [-1, 3, DenseOne, [1024, 32]],
   [-1, 1, SPPF, [1024, 5]],  # 9
  ]

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

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

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

   [-1, 1, Conv, [512, 3, 2]],
   [[-1, 10], 1, Concat, [1]],  # cat head P5
   [-1, 3, DenseOne, [1024]],  # 23 (P5/32-large)

   [[17, 20, 23], 1, Detect, [nc, anchors]],  # Detect(P3, P4, P5)
  ]

三、总结

本文主要工作包括DenseNet介绍及改进代码策略,该模块为即插即用模块,部署位置可根据实际针对任务需求,自行调整

本专栏持续更新中,订阅本栏,关注更新~


http://www.niftyadmin.cn/n/5459985.html

相关文章

kubernetes(K8S)学习(六):K8S之Dashboard图形界面

K8S之Dashboard图形界面 一、Dashboard简介二、k8s安装Dashboard(1)下载Dashboard镜像(可选)(2)根据yaml文件创建资源(3)查看资源(4)生成登录需要的token(5)使用火狐 / 搜狗浏览器访问(个人用的搜狗) 一、Dashboard简介 官网&…

【LeetCode: 面试题 16.05. 阶乘尾数 + 阶乘】

🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,…

企微获客助手到底有哪些价值?

获客助手作为企业微信官方提供的获客工具,在私域布局中确实展现了其强大的引流效率和便利性。这一工具通过简化传统引流过程中的复杂步骤,使得企业能够更高效地吸引和转化潜在客户。此外,获客助手还能实现不同渠道的无缝链接,进一…

【LeetCode热题100】155. 最小栈(栈)

一.题目要求 设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。 实现 MinStack 类: MinStack() 初始化堆栈对象。void push(int val) 将元素val推入堆栈。void pop() 删除堆栈顶部的元素。int top() 获取堆栈顶部的…

C语言共用体和枚举

1.共用体概述: 在c语言中,不同数据类型可以使用共同的存储区域,这种数据构造类型称为共用体,有称联合体。与结构体相识,本质不同是在使用内存的方式上。 语法: union 共用体名 { 共用体成员列表 }&#x…

Redis常见阻塞原因总结

大key string 类型的 value 超过 1MB复合类型(列表、哈希、集合、有序集合等)的 value 包含的元素超过 5000 个(对于复合类型的 value 来说,不一定包含的元素越多,占用的内存就越多)。 大 key 造成的阻塞…

【Gitlab】服务器性能调优

安装之后操作Gitlab网页端时候会有些卡顿,可以修改配置来改善性能: 提前说明:我的机器配置如下 两个40线程的处理器 160G内存 # //处理器 CPU(s): 40On-line CPU(s) list: 0-39 Vendor ID: GenuineIntelMod…

特殊数据类型

目录 记录类型 定义一个记录类型 myrecord_type,用于存储 emp 数据表中的员工姓名和职务 %TYPE 类型 定义一个变量,存储数据表 emp 中编号为 7369 的员工姓名,并且显示出结果 %ROWTYPE 类型 声明一个用于存储 emp 数据表中每行记录的变…