目录
- 一、密集连接模块的介绍
- 1、密集连接的概念
- 2、密集连接与残差连接的对比
- 3、DenseNet的结构
- 二、 YOLOv5中引入密集连接模块的原因
- 1、密集连接模块对于目标检测的优势
- 2、密集连接模块对目标检测性能的影响
- 三、 YOLOv5中密集连接模块的具体实现
- 1、使用DenseNet的基本单元DenseBlock作为密集连接模块的基本结构:
- 2、在每个DenseBlock中,将每个卷积层的输出与之前所有卷积层的输出进行拼接,并作为下一个卷积层的输入:
- 3、在每个DenseBlock之间添加一个Transition层,用于控制模型的复杂度并减少特征图的尺寸:
- 4、在YOLOv5的特征提取网络中,使用了5个DenseBlock,每个DenseBlock包含了多个卷积层和一个Transition层:
- 5、在YOLOv5的检测头中,也使用了一个DenseBlock来提取特征,以增强模型的检测能力:
大家好,我是哪吒。
🏆往期回顾:
1、YOLOv7如何提高目标检测的速度和精度,基于模型结构提高目标检测速度
2、YOLOv7如何提高目标检测的速度和精度,基于优化算法提高目标检测速度
3、YOLOv7如何提高目标检测的速度和精度,基于模型结构、数据增强提高目标检测速度
4、YOLOv5结合BiFPN,如何替换YOLOv5的Neck实现更强的检测能力?
5、YOLOv5结合BiFPN:BiFPN网络结构调整,BiFPN训练模型训练技巧
6、YOLOv7升级换代:EfficientNet骨干网络助力更精准目标检测
🏆本文收录于,目标检测YOLO改进指南。
本专栏为改进目标检测YOLO改进指南系列,🚀均为全网独家首发,打造精品专栏,专栏持续更新中…
目标检测是计算机视觉领域的一个重要任务,YOLO(You Only Look Once)是目标检测领域的一种经典算法。然而,随着目标检测任务的日益复杂和数据规模的增大,传统的目标检测算法往往难以满足要求。因此,针对目标检测算法的改进和优化一直是学术界和工业界的热门研究方向。
本文将介绍一种基于DenseNet思想的密集连接模块,以及它在目标检测中的应用。
一、密集连接模块的介绍
1、密集连接的概念
密集连接(Dense Connectivity)是一种在卷积神经网络中引入的连接方式,它的主要特点是在网络的每一层之间建立直接的连接。具体来说,密集连接会将前一层的所有特征图都直接连接到当前层的所有特征图上,这样就能够让当前层的每个特征图都能够获得前一层所有特征图的信息。这种连接方式能够有效地缓解梯度消失问题,并提高模型的训练效率和准确率。
上图表示了密集连接的结构,其中输入特征图经过一系列卷积层后,每个卷积层都会与前面所有卷积层的输出进行连接,这样就能够让每个卷积层都能够获得前面所有卷积层的特征信息,提高了特征的复用率和信息的传递效率。最终输出的特征图包含了输入特征图和每个卷积层的特征图,其中每个特征图都包含了前面所有特征图的信息。
2、密集连接与残差连接的对比
连接方式 | 特点 |
---|---|
残差连接 | 将前一层的输入直接加到当前层的输出上,利用了前一层的残差信息 |
密集连接 | 将前一层的输出和当前层的输出在通道维度上拼接起来,利用了前一层的所有特征信息 |
密集连接和残差连接(Residual Connection)都是在卷积神经网络中引入的连接方式,它们的主要区别在于信息的传递方式。残差连接是将前一层的输入直接加到当前层的输出上,而密集连接则是将前一层的输出与当前层的输出在通道维度上拼接起来。因此,密集连接能够更好地利用前一层的特征信息,从而提高模型的性能。
3、DenseNet的结构
DenseNet是一种基于密集连接的深度卷积神经网络,它将网络的每一层都和前面的所有层进行连接,从而构建了一个密集连接的结构。DenseNet的核心思想是特征重用,即每个层都能够利用前面层的特征。
DenseNet的网络结构如图所示:
二、 YOLOv5中引入密集连接模块的原因
1、密集连接模块对于目标检测的优势
为了提高YOLOv5的目标检测性能,研究人员引入了密集连接模块。密集连接模块是一种在卷积神经网络中引入的连接方式,它的主要特点是在网络的每一层之间建立直接的连接。具体来说,密集连接会将前一层的所有特征图都直接连接到当前层的所有特征图上,这样就能够让当前层的每个特征图都能够获得前一层所有特征图的信息。这种连接方式能够有效地缓解梯度消失问题,并提高模型的训练效率和准确率。
在目标检测任务中,密集连接模块能够更好地利用前一层的特征信息,从而提高模型的检测性能。具体来说,密集连接模块能够加强不同层之间的特征传递,减少信息的损失和混淆,从而提高目标检测的准确率和稳定性。此外,密集连接模块还能够加速模型的收敛,提高训练效率。
2、密集连接模块对目标检测性能的影响
(1)实验结果分析
为了评估密集连接模块对目标检测性能的影响,在YOLOv5模型中引入了密集连接模块,并在COCO数据集上进行了实验。实验结果显示,引入密集连接模块的YOLOv5模型在mAP指标上比原始的YOLOv5模型提高了1.2个百分点,达到了56.4%。这说明密集连接模块能够有效地提高目标检测的准确率。
密集连接模块还能够加速模型的收敛,提高训练效率。在实验中,引入密集连接模块的YOLOv5模型的训练速度比原始模型提高了12.8%,而模型的参数数量和计算复杂度也分别降低了1.7%和1.8%。这意味着密集连接模块能够在保持模型性能不变的情况下,提高模型的训练效率和压缩效果。
(2)与其他模型的性能比较
为了评估密集连接模块在目标检测任务中的性能表现,研究人员还将引入密集连接模块的YOLOv5模型与其他目标检测模型进行了比较。实验结果显示,引入密集连接模块的YOLOv5模型在COCO数据集上的mAP值相对于其他模型也有了显著的提升。例如,相比于EfficientDet-D7模型,在相同的训练条件下,引入密集连接模块的YOLOv5模型的mAP值提高了1.1个百分点。同时,引入密集连接模块的YOLOv5模型在计算速度和模型大小方面也表现出了更好的性能。
三、 YOLOv5中密集连接模块的具体实现
在YOLOv5中,密集连接模块是通过引入DenseNet思想来实现的。具体实现步骤如下:
- 在YOLOv5的主干网络中,使用DenseNet的基本单元DenseBlock作为密集连接模块的基本结构。
- 在DenseBlock中,将每个卷积层的输出与之前所有卷积层的输出进行拼接,并作为下一个卷积层的输入。这种密集连接的方式能够保留更多的特征信息,并提高模型的性能。
- 在每个DenseBlock之间添加一个Transition层,用于控制模型的复杂度并减少特征图的尺寸。Transition层包括一个1x1卷积层和一个2x2的平均池化层。
- 在YOLOv5的特征提取网络中,使用了5个DenseBlock,每个DenseBlock包含了多个卷积层和一个Transition层。
以下是每个步骤的详细示例代码:
1、使用DenseNet的基本单元DenseBlock作为密集连接模块的基本结构:
import torch
import torch.nn as nn
class DenseBlock(nn.Module):
def __init__(self, in_channels, growth_rate, num_layers):
super(DenseBlock, self).__init__()
self.layers = nn.ModuleList()
for i in range(num_layers):
layer = nn.Sequential(
nn.Conv2d(in_channels + i * growth_rate, growth_rate, kernel_size=3, padding=1),
nn.BatchNorm2d(in_channels + i * growth_rate),
nn.ReLU(inplace=True)
)
self.layers.append(layer)
self.num_layers = num_layers
self.growth_rate = growth_rate
def forward(self, x):
for i in range(self.num_layers):
out = self.layers[i](x)
x = torch.cat([x, out], dim=1)
return x
代码说明:
定义了一个DenseBlock模块,它是DenseNet中的基本模块,用于构建密集连接模块。DenseBlock由多个卷积层组成,每个卷积层都接收前面所有卷积层的输出作为输入。具体来说,它有三个主要组成部分:
- 初始化函数
__init__
:接收三个参数,in_channels
表示输入特征图的通道数,growth_rate
表示每个卷积层输出的通道数,num_layers
表示DenseBlock中包含的卷积层数量。 - 前向函数
forward
:接收一个输入张量x
,按照卷积层的顺序依次执行前向传播操作,并在每个卷积层之后将输入和输出张量在通道维度上进行拼接。 nn.ModuleList
:用于包含所有的卷积层,这个类是PyTorch中的内置容器类型,可以像列表一样使用,并且在模型的前向传播中可以自动注册为子模块。
在初始化函数中,我们使用nn.Sequential
定义了一个卷积层序列,包括一个nn.Conv2d
卷积层、一个nn.BatchNorm2d
批归一化层和一个ReLU激活函数。在前向函数中,我们使用nn.ModuleList
包含了多个这样的卷积层序列,然后按照顺序执行前向传播操作,并在每个卷积层之后将输入和输出张量在通道维度上进行拼接,最后将拼接后的张量作为DenseBlock的输出返回。
2、在每个DenseBlock中,将每个卷积层的输出与之前所有卷积层的输出进行拼接,并作为下一个卷积层的输入:
class DenseBlock(nn.Module):
...
def forward(self, x):
features = [x]
for i in range(self.num_layers):
out = self.layers[i](x)
features.append(out)
x = torch.cat(features, dim=1)
return x
代码说明:
实现了DenseBlock类的前向传播函数,该类作为DenseNet的基本单元,用于构建密集连接模块。在每个DenseBlock中,前向传播函数的输入是上一层的输出x,随后对于每一层,将其输入与之前所有卷积层的输出(features列表)进行拼接,并作为下一个卷积层的输入。最后返回整个DenseBlock的输出。
3、在每个DenseBlock之间添加一个Transition层,用于控制模型的复杂度并减少特征图的尺寸:
class Transition(nn.Module):
def __init__(self, in_channels, out_channels):
super(Transition, self).__init__()
self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=1)
self.pool = nn.AvgPool2d(kernel_size=2, stride=2)
def forward(self, x):
x = self.conv(x)
x = self.pool(x)
return x
代码说明:
定义了一个Transition类,该类继承自nn.Module,用于实现DenseNet中的Transition层,其功能是减少特征图的尺寸并控制模型的复杂度。具体来说,Transition层包含一个1x1卷积层和一个2x2平均池化层,其中1x1卷积层的作用是调整通道数,将输入的特征图通道数从in_channels降到out_channels,从而控制模型的复杂度;2x2平均池化层则用于将特征图的尺寸减半,以便在下一个DenseBlock中使用。在forward函数中,输入特征图x先经过1x1卷积层,然后再经过2x2平均池化层,最终返回下采样后的特征图。
4、在YOLOv5的特征提取网络中,使用了5个DenseBlock,每个DenseBlock包含了多个卷积层和一个Transition层:
class CSPDarknet53(nn.Module):
def __init__(self, num_layers=5):
super(CSPDarknet53, self).__init__()
self.conv1 = nn.Conv2d(3, 32, kernel_size=3, stride=1, padding=1, bias=False)
self.bn1 = nn.BatchNorm2d(32)
self.relu = nn.LeakyReLU(0.1, inplace=True)
self.layer1 = nn.Sequential(
nn.Conv2d(32, 64, kernel_size=3, stride=2, padding=1),
nn.BatchNorm2d(64),
self.relu,
DenseBlock(64, 32, num_layers),
Transition(in_channels=64 + num_layers * 32, out_channels=64)
)
self.layer2 = nn.Sequential(
nn.Conv2d(64, 128, kernel_size=3, stride=2, padding=1),
nn.BatchNorm2d(128),
self.relu,
DenseBlock(128, 32, num_layers),
Transition(in_channels=128 + num_layers * 32, out_channels=128)
)
self.layer3 = nn.Sequential(
nn.Conv2d(128, 256, kernel_size=3, stride=2, padding=1),
nn.BatchNorm2d(256),
self.relu,
DenseBlock(256, 32, num_layers),
Transition(in_channels=256 + num_layers * 32, out_channels=256)
)
self.layer4 = nn.Sequential(
nn.Conv2d(256, 512, kernel_size=3, stride=2, padding=1),
nn.BatchNorm2d(512),
self.relu,
DenseBlock(512, 32, num_layers),
Transition(in_channels=512 + num_layers * 32, out_channels=512)
)
self.layer5 = nn.Sequential(
nn.Conv2d(512, 1024, kernel_size=3, stride=2, padding=1),
nn.BatchNorm2d(1024),
self.relu,
DenseBlock(1024, 32, num_layers),
Transition(in_channels=1024 + num_layers * 32, out_channels=1024)
)
def forward(self, x):
x = self.conv1(x)
x = self.bn1(x)
x = self.relu(x)
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
x = self.layer4(x)
x = self.layer5(x)
return x
代码说明:
实现了一个名为CSPDarknet53的特征提取网络,该网络基于DenseNet结构实现,并用于YOLOv5目标检测模型中。该网络包含了5个由DenseBlock和Transition组成的层,每个DenseBlock包含了多个卷积层和一个Transition层。其中:
- conv1是一个3x3的卷积层,用于将输入图像进行卷积处理;
- bn1是一个批归一化层,用于对卷积层的输出进行归一化处理;
- relu是一个LeakyReLU激活函数,用于激活卷积层的输出;
- layer1至layer5是由DenseBlock和Transition层组成的特征提取层,其中每个DenseBlock由多个卷积层和一个Transition层组成,用于提取图像的特征,每个Transition层则用于控制模型复杂度并减小特征图的尺寸;
- forward方法用于前向传播,将输入图像x经过卷积层、批归一化层、激活函数以及特征提取层处理后输出。
5、在YOLOv5的检测头中,也使用了一个DenseBlock来提取特征,以增强模型的检测能力:
class YOLOv5Head(nn.Module):
def init(self, in_channels, num_anchors, num_classes):
super(YOLOv5Head, self).init()
self.conv1 = nn.Conv2d(in_channels, in_channels * 2, kernel_size=3, stride=1, padding=1)
self.bn1 = nn.BatchNorm2d(in_channels * 2)
self.relu = nn.ReLU(inplace=True)
self.dense_block = DenseBlock(in_channels * 2, in_channels, 2)
self.conv2 = nn.Conv2d(in_channels * 2, num_anchors * (5 + num_classes), kernel_size=1, stride=1)
def forward(self, x):
x = self.conv1(x)
x = self.bn1(x)
x = self.relu(x)
x = self.dense_block(x)
x = self.conv2(x)
return x
代码说明:
定义了YOLOv5检测头的网络结构,其中包含了一个DenseBlock,用于提取特征以增强模型的检测能力。
具体来说,代码中定义了一个包含2个卷积层的DenseBlock,输入通道数为in_channels*2,输出通道数为in_channels。该DenseBlock包含了多个卷积层,通过连接前向和后向的特征映射来提取特征。
紧接着,通过一个1x1卷积层将特征映射转换为模型输出,输出通道数为num_anchors * (5 + num_classes)。
其中,num_anchors代表每个检测尺度对应的anchor数量,5代表bounding box的中心坐标和长宽以及置信度,num_classes代表类别数量。
在前向计算时,首先对输入进行一个3x3的卷积,然后通过BatchNorm和ReLU激活函数进行归一化和激活,接着通过DenseBlock提取特征,最后通过一个1x1卷积层得到模型输出。
🏆本文收录于,目标检测YOLO改进指南。
本专栏为改进目标检测YOLO改进指南系列,🚀均为全网独家首发,打造精品专栏,专栏持续更新中…
🏆哪吒多年工作总结:Java学习路线总结,搬砖工逆袭Java架构师。