改进yolo11-LSCD等200+全套创新点大全:集装箱号码识别系统源码&数据集全套
1.图片效果展示
项目来源 人工智能促进会 2024.10.24
注意:由于项目一直在更新迭代,上面“1.图片效果展示”和“2.视频效果展示”展示的系统图片或者视频可能为老版本,新版本在老版本的基础上升级如下:(实际效果以升级的新版本为准)
(1)适配了YOLOV11的“目标检测”模型和“实例分割”模型,通过加载相应的权重(.pt)文件即可自适应加载模型。
(2)支持“图片识别”、“视频识别”、“摄像头实时识别”三种识别模式。
(3)支持“图片识别”、“视频识别”、“摄像头实时识别”三种识别结果保存导出,解决手动导出(容易卡顿出现爆内存)存在的问题,识别完自动保存结果并导出到tempDir中。
(4)支持Web前端系统中的标题、背景图等自定义修改。
另外本项目提供训练的数据集和训练教程,暂不提供权重文件(best.pt),需要您按照教程进行训练后实现图片演示和Web前端界面演示的效果。
2.视频效果展示
3.背景
研究背景与意义
随着全球贸易的迅猛发展,集装箱运输已成为国际物流的重要组成部分。集装箱的高效管理和监控对于提高运输效率、降低运营成本具有重要意义。在这一背景下,集装箱号码的自动识别技术应运而生,成为提升集装箱管理智能化水平的关键手段。传统的集装箱号码识别方法多依赖人工操作,不仅效率低下,而且容易受到人为因素的影响,导致识别错误率较高。因此,基于计算机视觉和深度学习的自动识别系统逐渐成为研究的热点。
YOLO(You Only Look Once)系列模型因其高效的实时目标检测能力而受到广泛关注。最新的YOLOv11模型在准确性和速度上均有显著提升,适合用于集装箱号码的快速识别。然而,针对特定场景(如集装箱号码识别),YOLOv11的性能仍有待进一步优化。为此,本研究提出了一种基于改进YOLOv11的集装箱号码识别系统,旨在提高识别精度和速度,满足实际应用需求。
本项目所使用的数据集包含952张图像,专注于集装箱号码的识别,分类数量为1,涵盖了水平和垂直两种集装箱号码的样式。这一数据集为模型的训练和测试提供了丰富的样本,能够有效提升模型的泛化能力和识别效果。通过对YOLOv11模型的改进,结合该数据集的特性,研究将探索如何在复杂环境下实现高效、准确的集装箱号码识别,为物流行业的智能化转型提供技术支持和理论依据。最终,期望本研究能够推动集装箱管理的自动化进程,提高全球物流的运作效率,具有重要的学术价值和实际应用意义。
4.数据集信息展示
4.1 本项目数据集详细数据(类别数&类别名)
nc: 2
names: [‘container_number_h’, ‘container_number_v’]
该项目为【目标检测】数据集,请在【训练教程和Web端加载模型教程(第三步)】这一步的时候按照【目标检测】部分的教程来训练
4.2 本项目数据集信息介绍
本项目数据集信息介绍
本项目采用的数据集名为“CNR”,旨在为改进YOLOv11的集装箱号码识别系统提供支持。CNR数据集专注于集装箱号码的检测与识别,涵盖了两种主要类别:水平放置的集装箱号码(container_number_h)和垂直放置的集装箱号码(container_number_v)。这两种类别的设计考虑了实际应用中集装箱号码的多样性和复杂性,确保模型能够在不同的场景下有效识别集装箱信息。
CNR数据集的构建过程经过精心设计,包含了大量真实场景下拍摄的集装箱图像,确保数据的多样性和代表性。数据集中不仅包含了不同角度、不同光照条件下的集装箱号码图像,还涵盖了各种背景和环境因素,以模拟实际操作中可能遇到的各种情况。这种多样性为模型的训练提供了丰富的样本,有助于提高识别的准确性和鲁棒性。
此外,CNR数据集在标注过程中严格遵循了标准化流程,确保每个图像中的集装箱号码都得到了准确的标注。通过对图像进行细致的标注,数据集为YOLOv11模型的训练提供了高质量的输入,帮助模型学习到更为有效的特征表示,从而提升其在集装箱号码识别任务中的性能。
总之,CNR数据集不仅为本项目的目标提供了坚实的数据基础,还为未来在集装箱识别领域的研究与应用奠定了良好的基础。通过利用这一数据集,改进后的YOLOv11模型有望在集装箱号码识别的准确性和效率上实现显著提升,为物流与运输行业的智能化发展贡献力量。
5.全套项目环境部署视频教程(零基础手把手教学)
5.1 所需软件PyCharm和Anaconda安装教程(第一步)
5.2 安装Python虚拟环境创建和依赖库安装视频教程(第二步)
6.改进YOLOv11训练教程和Web_UI前端加载模型教程(零基础手把手教学)
6.1 改进YOLOv11训练教程和Web_UI前端加载模型教程(第三步)
按照上面的训练视频教程链接加载项目提供的数据集,运行train.py即可开始训练
Epoch gpu_mem box obj cls labels img_size
1/200 20.8G 0.01576 0.01955 0.007536 22 1280: 100%|██████████| 849/849 [14:42<00:00, 1.04s/it]
Class Images Labels P R mAP@.5 mAP@.5:.95: 100%|██████████| 213/213 [01:14<00:00, 2.87it/s]
all 3395 17314 0.994 0.957 0.0957 0.0843
Epoch gpu_mem box obj cls labels img_size
2/200 20.8G 0.01578 0.01923 0.007006 22 1280: 100%|██████████| 849/849 [14:44<00:00, 1.04s/it]
Class Images Labels P R mAP@.5 mAP@.5:.95: 100%|██████████| 213/213 [01:12<00:00, 2.95it/s]
all 3395 17314 0.996 0.956 0.0957 0.0845
Epoch gpu_mem box obj cls labels img_size
3/200 20.8G 0.01561 0.0191 0.006895 27 1280: 100%|██████████| 849/849 [10:56<00:00, 1.29it/s]
Class Images Labels P R mAP@.5 mAP@.5:.95: 100%|███████ | 187/213 [00:52<00:00, 4.04it/s]
all 3395 17314 0.996 0.957 0.0957 0.0845
项目数据集下载链接
7.原始YOLOv11算法讲解
其实到了YOLOV11 基本创新点就不太多了,主要就是大家互相排列组合复用不同的网络模块、损失函数和样本匹配策略,需要注意YOLO V5、V8 V11
都是1个公司的,其余的个人建议看看V8的,剩下的了解就好。
V11支持多种视觉任务:物体检测、实例分割、图像分类、姿态估计和定向物体检测(OBB)。
YOLOv11
基本和YOLOV8同源,甚至git目前都是1个,部分代码注释还是YOLOV8的,所以建议先看我写的YOLOV8相关博客,对比YOLOV8主要涉及到:
*backbone 中的使用C2f模块 变为 c3k2 模块。
*backbone 中的最后一层(sppf层)后增加了C2PSA模块。
*head 解耦头中的分类检测头两个Conv 变为 DWConv。
整体技术而言:
*backbone 使用了C2K2模块+最后SPPF模块级联C2PSA模块;
*neck 使用PAN结构,并且里面也使用C3K2模块;
*head使用了anchor-free + Decoupled-head,其中回归头使用正常的卷积,分类头使用DWConv;
*损失函数使用了分类BCE、回归CIOU + VFL的组合;
*框匹配策略由静态匹配改为了Task-Aligned Assigner匹配方式;
*训练策略没有提及,其中YOLOV8可以参考如下最后 10 个 epoch 关闭 Mosaic 的操作、训练总 epoch 数从 300 提升到了 500。
主要思路
配置文件:ultralytics/ultralytics/cfg/models/11/yolo11.yaml at main ·
ultralytics/ultralytics ·
GitHub
解析函数:ultralytics/ultralytics/nn/tasks.py at main · ultralytics/ultralytics ·
GitHub
具体细节
input
输入要求以及预处理,可选项比较多,可以参考这个配置文件:ultralytics/ultralytics/cfg/default.yaml at main
· ultralytics/ultralytics ·
GitHub 的Hyperparameters 部分。
基础输入仍然为640*640。预处理就是熟悉的letterbox(根据参数配置可以为不同的缩放填充模式,主要用于resize到640)+
转换rgb、chw、int8(0-255)->float(0-1),注意没有归一化操作。需要注意的是作者实现的mosaic和网上看到的不同,对比如下图(左边网上版本,右边是YOLO的实现)。并且作者添加了在最后10轮关闭mosaic增强(YOLOV8开始支持,具体原因个人的经验如我的这篇文章:yolov5
mosaic相关,关闭参数在 Train settings 部分的close_mosaic 选项)
backbone
主干网络以及改进
这里不去特意强调对比YOLOv5、V8等等的改进,因为各个系列都在疯狂演进,个人认为没必要花费时间看差异,着重看看一些比较重要的模块即可。源代码:
大多数模块:ultralytics/ultralytics/nn/modules/block.py at main ·
ultralytics/ultralytics ·
GitHub
head 部分:ultralytics/ultralytics/nn/modules/head.py at main ·
ultralytics/ultralytics ·
GitHub
串联模块构造网络:ultralytics/ultralytics/nn/tasks.py at main ·
ultralytics/ultralytics ·
GitHub
1)CBS 模块(后面叫做Conv)
就是pytorch 自带的conv + BN +SiLU,这里对应上面的配置文件的Conv 的 args 比如[64, 3, 2] 就是 conv2d
的c2=64、k=3、 s =2、c1 自动为上一层参数、p 为自动计算,真实需要计算scales 里面的with 和 max_channels 缩放系数。
这里连续使用两个3*3卷积stride为2的CBS模块直接横竖各降低了4倍分辨率(整体变为原来1/16)。这个还是比较猛的,敢在如此小的感受野下连续两次仅仅用一层卷积就下采样,当然作为代价它的特征图还是比较厚的分别为16、32。
class Conv(nn.Module):
"""Standard convolution with args(ch_in, ch_out, kernel, stride, padding, groups, dilation, activation)."""
default_act = nn.SiLU() # default activation
def __init__(self, c1, c2, k=1, s=1, p=None, g=1, d=1, act=True):
"""Initialize Conv layer with given arguments including activation."""
super().__init__()
self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p, d), groups=g, dilation=d, bias=False)
self.bn = nn.BatchNorm2d(c2)
self.act = self.default_act if act is True else act if isinstance(act, nn.Module) else nn.Identity()
def forward(self, x):
"""Apply convolution, batch normalization and activation to input tensor."""
return self.act(self.bn(self.conv(x)))
def forward_fuse(self, x):
"""Perform transposed convolution of 2D data."""
return self.act(self.conv(x))
2)c3k2 模块
Bottleneck
有两种结构,需要参数shortcut和两个conv的宽度是否相同来控制。
C3 & C3K
都是CSP bottleneck module with 3 convolutions, C3 代表3个卷积层,
K代表其中bottleneck中的卷积核为支持自定义,其实这里c3k作者使用的默认的33卷积核也就等同于使用c3(c3是33卷积核)。
c2f & c3k2
其实也就是仿照YOLOv7 的ELAN
结构,通过更多的分支夸层链接,丰富了模型的梯度流。C3K2模块其实就是C2F模块转变出来的,它代码中有一个设置,就是当c3k这个参数为FALSE的时候,C3K2模块就是C2F模块,也就是说它的Bottleneck是普通的Bottleneck;反之当它为true的时候,将Bottleneck模块替换成C3K模块。模块中存在
Split 等操作对特定硬件部署没有之前那么友好了。需要针对自己的硬件进行测试看对最终推理速度的影响。
可视化关系如下,这里需要注意配置文件中的参数,比如21行[-1, 2, C3k2, [512, False, 0.25]]
512代表宽度、false代表是否使用shortcut、0.25代表c2f的宽度缩放。也就是第一个Conv的输出宽度。
源代码如下:
class Bottleneck(nn.Module):
"""Standard bottleneck."""
def __init__(self, c1, c2, shortcut=True, g=1, k=(3, 3), e=0.5):
"""Initializes a standard bottleneck module with optional shortcut connection and configurable parameters."""
super().__init__()
c_ = int(c2 * e) # hidden channels
self.cv1 = Conv(c1, c_, k[0], 1)
self.cv2 = Conv(c_, c2, k[1], 1, g=g)
self.add = shortcut and c1 == c2
def forward(self, x):
"""Applies the YOLO FPN to input data."""
return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))
class C3(nn.Module):
"""CSP Bottleneck with 3 convolutions."""
def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):
"""Initialize the CSP Bottleneck with given channels, number, shortcut, groups, and expansion values."""
super().__init__()
c_ = int(c2 * e) # hidden channels
self.cv1 = Conv(c1, c_, 1, 1)
self.cv2 = Conv(c1, c_, 1, 1)
self.cv3 = Conv(2 * c_, c2, 1) # optional act=FReLU(c2)
self.m = nn.Sequential(*(Bottleneck(c_, c_, shortcut, g, k=((1, 1), (3, 3)), e=1.0) for _ in range(n)))
def forward(self, x):
"""Forward pass through the CSP bottleneck with 2 convolutions."""
return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), 1))
class C3k(C3):
"""C3k is a CSP bottleneck module with customizable kernel sizes for feature extraction in neural networks."""
def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5, k=3):
"""Initializes the C3k module with specified channels, number of layers, and configurations."""
super().__init__(c1, c2, n, shortcut, g, e)
c_ = int(c2 * e) # hidden channels
# self.m = nn.Sequential(*(RepBottleneck(c_, c_, shortcut, g, k=(k, k), e=1.0) for _ in range(n)))
self.m = nn.Sequential(*(Bottleneck(c_, c_, shortcut, g, k=(k, k), e=1.0) for _ in range(n)))
class C2f(nn.Module):
"""Faster Implementation of CSP Bottleneck with 2 convolutions."""
def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5):
"""Initializes a CSP bottleneck with 2 convolutions and n Bottleneck blocks for faster processing."""
super().__init__()
self.c = int(c2 * e) # hidden channels
self.cv1 = Conv(c1, 2 * self.c, 1, 1)
self.cv2 = Conv((2 + n) * self.c, c2, 1) # optional act=FReLU(c2)
self.m = nn.ModuleList(Bottleneck(self.c, self.c, shortcut, g, k=((3, 3), (3, 3)), e=1.0) for _ in range(n))
def forward(self, x):
"""Forward pass through C2f layer."""
y = list(self.cv1(x).chunk(2, 1))
y.extend(m(y[-1]) for m in self.m)
return self.cv2(torch.cat(y, 1))
def forward_split(self, x):
"""Forward pass using split() instead of chunk()."""
y = list(self.cv1(x).split((self.c, self.c), 1))
y.extend(m(y[-1]) for m in self.m)
return self.cv2(torch.cat(y, 1))
class C3k2(C2f):
"""Faster Implementation of CSP Bottleneck with 2 convolutions."""
def __init__(self, c1, c2, n=1, c3k=False, e=0.5, g=1, shortcut=True):
"""Initializes the C3k2 module, a faster CSP Bottleneck with 2 convolutions and optional C3k blocks."""
super().__init__(c1, c2, n, shortcut, g, e)
self.m = nn.ModuleList(
C3k(self.c, self.c, 2, shortcut, g) if c3k else Bottleneck(self.c, self.c, shortcut, g) for _ in range(n)
)
3)sppf 模块
对比spp,将简单的并行max pooling 改为串行+并行的方式。对比如下(左边是SPP,右边是SPPF):
class SPPF(nn.Module):
# Spatial Pyramid Pooling - Fast (SPPF) layer for YOLOv5 by Glenn Jocher
def __init__(self, c1, c2, k=5): # equivalent to SPP(k=(5, 9, 13))
super().__init__()
c_ = c1 // 2 # hidden channels
self.cv1 = Conv(c1, c_, 1, 1)
self.cv2 = Conv(c_ * 4, c2, 1, 1)
self.m = nn.MaxPool2d(kernel_size=k, stride=1, padding=k // 2)
def forward(self, x):
x = self.cv1(x)
with warnings.catch_warnings():
warnings.simplefilter('ignore') # suppress torch 1.9.0 max_pool2d() warning
y1 = self.m(x)
y2 = self.m(y1)
return self.cv2(torch.cat((x, y1, y2, self.m(y2)), 1))
4)C2PSA 模块
C2PSA它结合了PSA(Pointwise Spatial
Attention)块,用于增强特征提取和注意力机制。下面的图建议从左到右看,这样才能更有条理的理解,其实PSA个人感觉就是仿着VIT
的Attention来做的,是把输入C2PSA的特征图的hw 看做VIT 的path数(也可以理解为NLP中token 个数),特征图的channel
数看做VIT特征维度(CNN的宽度,或者理解为NLP中token
编码后的特征维度),然后计算出QKV(这里需要注意第四幅图的QKV是值,不是操作,所以标注成了圆角矩形,这里是为了大家好理解),这里的Attention其实是在hw维度计算空间Attention,个人感觉是强制给了全局感受野,并且并联了一个33的深度可分离卷积的单空间部分,就是仅在每一个特征图上进行33卷积,具体实现是通过pytorch
conv2d 的
group参数设置为特征图的通道数。特别的关于Conv的参数分别为:输入通道数、输出通道数、卷积核尺寸、pad尺寸、group数、是否有激活函数(默认silu)。图中的最后一幅省略了一些细节,可以参考源码。
注意区别C2fPSA,C2fPSA才是对 C2f 模块的扩展,通过在标准 C2f 模块中引入 PSA
块,C2fPSA实现了更强大的注意力机制,从而提高了模型对重要特征的捕捉能力。作者实现了该模块但最终没有使用。
涉及的源码:
class Attention(nn.Module):
"""
Attention module that performs self-attention on the input tensor.
Args:
dim (int): The input tensor dimension.
num_heads (int): The number of attention heads.
attn_ratio (float): The ratio of the attention key dimension to the head dimension.
Attributes:
num_heads (int): The number of attention heads.
head_dim (int): The dimension of each attention head.
key_dim (int): The dimension of the attention key.
scale (float): The scaling factor for the attention scores.
qkv (Conv): Convolutional layer for computing the query, key, and value.
proj (Conv): Convolutional layer for projecting the attended values.
pe (Conv): Convolutional layer for positional encoding.
"""
def __init__(self, dim, num_heads=8, attn_ratio=0.5):
"""Initializes multi-head attention module with query, key, and value convolutions and positional encoding."""
super().__init__()
self.num_heads = num_heads
self.head_dim = dim // num_heads
self.key_dim = int(self.head_dim * attn_ratio)
self.scale = self.key_dim**-0.5
nh_kd = self.key_dim * num_heads
h = dim + nh_kd * 2
self.qkv = Conv(dim, h, 1, act=False)
self.proj = Conv(dim, dim, 1, act=False)
self.pe = Conv(dim, dim, 3, 1, g=dim, act=False)
def forward(self, x):
"""
Forward pass of the Attention module.
Args:
x (torch.Tensor): The input tensor.
Returns:
(torch.Tensor): The output tensor after self-attention.
"""
B, C, H, W = x.shape
N = H * W
qkv = self.qkv(x)
q, k, v = qkv.view(B, self.num_heads, self.key_dim * 2 + self.head_dim, N).split(
[self.key_dim, self.key_dim, self.head_dim], dim=2
)
attn = (q.transpose(-2, -1) @ k) * self.scale
attn = attn.softmax(dim=-1)
x = (v @ attn.transpose(-2, -1)).view(B, C, H, W) + self.pe(v.reshape(B, C, H, W))
x = self.proj(x)
return x
class PSABlock(nn.Module):
"""
PSABlock class implementing a Position-Sensitive Attention block for neural networks.
This class encapsulates the functionality for applying multi-head attention and feed-forward neural network layers
with optional shortcut connections.
Attributes:
attn (Attention): Multi-head attention module.
ffn (nn.Sequential): Feed-forward neural network module.
add (bool): Flag indicating whether to add shortcut connections.
Methods:
forward: Performs a forward pass through the PSABlock, applying attention and feed-forward layers.
Examples:
Create a PSABlock and perform a forward pass
>>> psablock = PSABlock(c=128, attn_ratio=0.5, num_heads=4, shortcut=True)
>>> input_tensor = torch.randn(1, 128, 32, 32)
>>> output_tensor = psablock(input_tensor)
"""
def __init__(self, c, attn_ratio=0.5, num_heads=4, shortcut=True) -> None:
"""Initializes the PSABlock with attention and feed-forward layers for enhanced feature extraction."""
super().__init__()
self.attn = Attention(c, attn_ratio=attn_ratio, num_heads=num_heads)
self.ffn = nn.Sequential(Conv(c, c * 2, 1), Conv(c * 2, c, 1, act=False))
self.add = shortcut
def forward(self, x):
"""Executes a forward pass through PSABlock, applying attention and feed-forward layers to the input tensor."""
x = x + self.attn(x) if self.add else self.attn(x)
x = x + self.ffn(x) if self.add else self.ffn(x)
return x
class C2PSA(nn.Module):
"""
C2PSA module with attention mechanism for enhanced feature extraction and processing.
This module implements a convolutional block with attention mechanisms to enhance feature extraction and processing
capabilities. It includes a series of PSABlock modules for self-attention and feed-forward operations.
Attributes:
c (int): Number of hidden channels.
cv1 (Conv): 1x1 convolution layer to reduce the number of input channels to 2*c.
cv2 (Conv): 1x1 convolution layer to reduce the number of output channels to c.
m (nn.Sequential): Sequential container of PSABlock modules for attention and feed-forward operations.
Methods:
forward: Performs a forward pass through the C2PSA module, applying attention and feed-forward operations.
Notes:
This module essentially is the same as PSA module, but refactored to allow stacking more PSABlock modules.
Examples:
>>> c2psa = C2PSA(c1=256, c2=256, n=3, e=0.5)
>>> input_tensor = torch.randn(1, 256, 64, 64)
>>> output_tensor = c2psa(input_tensor)
"""
def __init__(self, c1, c2, n=1, e=0.5):
"""Initializes the C2PSA module with specified input/output channels, number of layers, and expansion ratio."""
super().__init__()
assert c1 == c2
self.c = int(c1 * e)
self.cv1 = Conv(c1, 2 * self.c, 1, 1)
self.cv2 = Conv(2 * self.c, c1, 1)
self.m = nn.Sequential(*(PSABlock(self.c, attn_ratio=0.5, num_heads=self.c // 64) for _ in range(n)))
def forward(self, x):
"""Processes the input tensor 'x' through a series of PSA blocks and returns the transformed tensor."""
a, b = self.cv1(x).split((self.c, self.c), dim=1)
b = self.m(b)
return self.cv2(torch.cat((a, b), 1))
3、neck & head
1)检测头
YOLOV11 Head 部分和YOLOV8是近似的,所以简单对比YOLOV5、V8、V11。
如上面图,上边是YOLOV5 的结构,中是YOLOv8 的结构,下面是YOLOV11 结构
Yolov5: 检测和分类共用一个卷积(coupled head)并且是anchor based ,其 卷积输出为(5+N class)*3,其中
5为bbox 四个值(具体代表什么不同版本略有不同,官方git有说明,历史版本见 目标检测算法——YOLOV5 )+ 一个obj 值
(是否有目标,这个是从YOLO V1 传承下来的,个人感觉有点绕和不合理,并且后面取消),N class 为类别数,3为anchor 的数量,默认是3个。
YOLOv8:检测和分类的卷积是解耦的(decoupled),如中图,上面一条卷积支路是回归框,框的特征图channel为4*regmax,关于这个regmax
后面我们详细的解释,并不是anchor;分类的channel 为类别数。
YOLOV11:检测和分类的卷积是解耦的(decoupled),如右图,上面一条卷积支路是回归框,框的特征图channel为4*regmax,关于这个regmax
后面我们详细的解释,并不是anchor;分类的channel 为类别数,分类使用深度可分离卷积替代常规卷积降低计算量。
源码部分如下
class Detect(nn.Module):
"""YOLO Detect head for detection models."""
dynamic = False # force grid reconstruction
export = False # export mode
end2end = False # end2end
max_det = 300 # max_det
shape = None
anchors = torch.empty(0) # init
strides = torch.empty(0) # init
def __init__(self, nc=80, ch=()):
"""Initializes the YOLO detection layer with specified number of classes and channels."""
super().__init__()
self.nc = nc # number of classes
self.nl = len(ch) # number of detection layers
self.reg_max = 16 # DFL channels (ch[0] // 16 to scale 4/8/12/16/20 for n/s/m/l/x)
self.no = nc + self.reg_max * 4 # number of outputs per anchor
self.stride = torch.zeros(self.nl) # strides computed during build
c2, c3 = max((16, ch[0] // 4, self.reg_max * 4)), max(ch[0], min(self.nc, 100)) # channels
self.cv2 = nn.ModuleList(
nn.Sequential(Conv(x, c2, 3), Conv(c2, c2, 3), nn.Conv2d(c2, 4 * self.reg_max, 1)) for x in ch
)
self.cv3 = nn.ModuleList(
nn.Sequential(
nn.Sequential(DWConv(x, x, 3), Conv(x, c3, 1)),
nn.Sequential(DWConv(c3, c3, 3), Conv(c3, c3, 1)),
nn.Conv2d(c3, self.nc, 1),
)
for x in ch
)
self.dfl = DFL(self.reg_max) if self.reg_max > 1 else nn.Identity()
if self.end2end:
self.one2one_cv2 = copy.deepcopy(self.cv2)
self.one2one_cv3 = copy.deepcopy(self.cv3)
def forward(self, x):
"""Concatenates and returns predicted bounding boxes and class probabilities."""
if self.end2end:
return self.forward_end2end(x)
for i in range(self.nl):
x[i] = torch.cat((self.cv2[i](x[i]), self.cv3[i](x[i])), 1)
if self.training: # Training path
return x
y = self._inference(x)
return y if self.export else (y, x)
因此主要的变化可以认为有三个:(1)coupled head -> decoupled head ;(2)obj 分支消失;(3)anchor
based——> anchor free ; 4) 深度可分离卷积。
(1)coupled head -> decoupled head
这个解耦操作,看YOLO x 的论文,约有1% 的提升。逻辑和实现都比较直观易懂,不再赘述。
(2)obj 分支消失;
这个其实我自己再看YOLO V1 的时候就有疑问,它存在的意义。后来人们发现,其实obj
的在训练和推理过程中存在逻辑不一致性。具体而言(摘自“https://zhuanlan.zhihu.com/p/147691786”)
A。用法不一致。训练的时候,分类和质量估计各自训练自个儿的,但测试的时候却又是乘在一起作为NMS score排序的依据,这个操作显然没有end-to-
end,必然存在一定的gap。(个人认为还好,就是两个监督信号)
B。对象不一致。借助Focal
Loss的力量,分类分支能够使得少量的正样本和大量的负样本一起成功训练,但是质量估计通常就只针对正样本训练。那么,对于one-
stage的检测器而言,在做NMS
score排序的时候,所有的样本都会将分类score和质量预测score相乘用于排序,那么必然会存在一部分分数较低的“负样本”的质量预测是没有在训练过程中有监督信号的,对于大量可能的负样本,他们的质量预测是一个未定义行为。这就很有可能引发这么一个情况:一个分类score相对低的真正的负样本,由于预测了一个不可信的极高的质量score,而导致它可能排到一个真正的正样本(分类score不够高且质量score相对低)的前面。问题一如图所示:
(3)anchor based——> anchor free
这里主要涉及怎么定义回归内容以及如何匹配GT框的问题。也就是如下:
2)匹配策略
A。回归的内容当前版本就是回归的lftp四个值(这四个值是距离匹配到的anchor 点的距离值!不是图片的绝对位置)。后面推理阶段通过
dist2bbox函数转换为需要的格式:
def dist2bbox(distance, anchor_points, xywh=True, dim=-1):
"""Transform distance(ltrb) to box(xywh or xyxy)."""
lt, rb = torch.split(distance, 2, dim)
x1y1 = anchor_points - lt
x2y2 = anchor_points + rb
if xywh:
c_xy = (x1y1 + x2y2) / 2
wh = x2y2 - x1y1
return torch.cat((c_xy, wh), dim) # xywh bbox
return torch.cat((x1y1, x2y2), dim) # xyxy bbox
B.匹配策略
YOLOv5 采用静态的匹配策略,V8采用了动态的TaskAlignedAssigner,其余常见的动态匹配还有: YOLOX 的 simOTA、TOOD
的 TaskAlignedAssigner 和 RTMDet 的 DynamicSoftLabelAssigner。
TaskAligned使用分类得分和IoU的高阶组合来衡量Task-Alignment的程度。使用上面公式来对每个实例计算Anchor-level
的对齐程度:s 和 u 分别为分类得分和 IoU 值,α 和 β 为权重超参。t 可以同时控制分类得分和IoU 的优化来实现 Task-
Alignment,可以引导网络动态的关注于高质量的Anchor。采用一种简单的分配规则选择训练样本:对每个实例,选择m个具有最大t值的Anchor作为正样本,选择其余的Anchor作为负样本。然后,通过损失函数(针对分类与定位的对齐而设计的损失函数)进行训练。
默认参数如下(当前版本这些超参没有提供修改的接口,如需修改需要在源码上进行修改):
4、loss function
损失函数设计
Loss 计算包括 2 个分支: 分类和回归分支,没有了之前的 objectness 分支。
分类分支依然采用 BCE Loss。回归分支使用了 Distribution Focal Loss(DFL Reg_max默认为16)+ CIoU
Loss。3 个 Loss
采用一定权重比例加权即可(默认如下:https://github.com/ultralytics/ultralytics/blob/main/ultralytics/yolo/configs/default.yaml#L83)。
这里重点介绍一下DFL损失。目前被广泛使用的bbox表示可以看作是对bbox方框坐标建模了单一的狄拉克分布。但是在复杂场景中,一些检测对象的边界并非十分明确。如下图左面所示,对于滑板左侧被水花模糊,引起对左边界的预测分布是任意而扁平的,对右边界的预测分布是明确而尖锐的。对于这个问题,有学者提出直接回归一个任意分布来建模边界框,使用softmax实现离散的回归,将狄拉克分布的积分形式推导到一般形式的积分形式来表示边界框。
狄拉克分布可以认为在一个点概率密度为无穷大,其他点概率密度为0,这是一种极端地认为离散的标签时绝对正确的。
因为标签是一个离散的点,如果把标签认为是绝对正确的目标,那么学习出的就是狄拉克分布,概率密度是一条尖锐的竖线。然而真实场景,物体边界并非是十分明确的,因此学习一个宽范围的分布更为合理。我们需要获得的分布虽然不再像狄拉克分布那么极端(只存在标签值),但也应该在标签值附近。因此学者提出Distribution
Focal
Loss损失函数,目的让网络快速聚焦到标签附近的数值,是标签处的概率密度尽量大。思想是使用交叉熵函数,来优化标签y附近左右两个位置的概率,是网络分布聚焦到标签值附近。如下公式。Si
是网络的sigmod 输出(因为真是是多分类,所以是softmax),yi 和 yi+1 是上图的区间顺序,y是label
值。
具体而言,针对我们将DFL的超参数Reg_max 设置为16的情况下:
A。训练阶段:我们以回归left为例:目标的label 转换为ltrb后,y = ( left - 匹配到的anchor 中心点 x 坐标)/
当前的下采样倍数,假设求得3.2。那么i 就应该为3,yi = 3 ,yi+1 = 4。
B。推理阶段:因为没有label,直接将16个格子进行积分(离散变量为求和,也就是期望)结果就是最终的坐标偏移量(再乘以下采样倍数+
匹配到的anchor的对应坐标)
DFL的实现方式其实就是一个卷积:ultralytics/ultralytics/nn/modules.py at
cc3c774bde86ffce694d202b7383da6cc1721c1b · ultralytics/ultralytics ·
GitHub
NOTE:作者代码中的超参数Reg_max是写死的——16,并且代码内部做了强制截断到16,如果要修改需要修改源码,如果你的输入是640,最大下采样到2020,那么16是够用的,如果输入没有resize或者超过了640一定要自己设置这个Reg_max参数,否则如果目标尺寸还大,将无法拟合到这个偏移量。
比如12801280的图片,目标1280*960,最大下采样32倍,1280/32/2=20 > 16(除以2是因为是一半的偏移量),超过了dfl
滑板右侧那个图的范围。至于为什么叫focal
loss的变体,有兴趣看一下这个https://zhuanlan.zhihu.com/p/357415257和https://zhuanlan.zhihu.com/p/147691786就可以,这里不再赘述是因为,如果先看这些,很容易犯晕,反而抓不住DFL
我认为的重点(离散的分布形式)
class DFL(nn.Module):
# Integral module of Distribution Focal Loss (DFL) proposed in Generalized Focal Loss https://ieeexplore.ieee.org/document/9792391
def __init__(self, c1=16):
super().__init__()
self.conv = nn.Conv2d(c1, 1, 1, bias=False).requires_grad_(False)
x = torch.arange(c1, dtype=torch.float)
self.conv.weight.data[:] = nn.Parameter(x.view(1, c1, 1, 1))
self.c1 = c1
def forward(self, x):
b, c, a = x.shape # batch, channels, anchors
return self.conv(x.view(b, 4, self.c1, a).transpose(2, 1).softmax(1)).view(b, 4, a)
# return self.conv(x.view(b, self.c1, 4, a).softmax(1)).view(b, 4, a)
8.200+种全套改进YOLOV11创新点原理讲解
8.1 200+种全套改进YOLOV11创新点原理讲解大全
由于篇幅限制,每个创新点的具体原理讲解就不全部展开,具体见下列网址中的改进模块对应项目的技术原理博客网址【Blog】(创新点均为模块化搭建,原理适配YOLOv5~YOLOv11等各种版本)
8.2 精选部分改进YOLOV11创新点原理讲解
这里节选部分改进创新点展开原理讲解(完整的改进原理见上图和改进模块技术原理博客链接【如果此小节的图加载失败可以通过CSDN或者Github搜索该博客的标题访问原始博客,原始博客图片显示正常】
### FasterNet简介
神经网络在图像分类、检测和分割等各种计算机视觉任务中经历了快速发展。尽管其令人印象深刻的性能为许多应用程序提供了动力,但一个巨大的趋势是追求具有低延迟和高吞吐量的快速神经网络,以获得良好的用户体验、即时响应和安全原因等。
如何快速?研究人员和从业者不需要更昂贵的计算设备,而是倾向于设计具有成本效益的快速神经网络,降低计算复杂度,主要以浮点运算(FLOPs)的数量来衡量。
MobileNet、ShuffleNet和GhostNet等利用深度卷积(DWConv)和/或组卷积(GConv)来提取空间特征。然而,在减少FLOPs的过程中,算子经常会受到内存访问增加的副作用的影响。MicroNet进一步分解和稀疏网络,将其FLOPs推至极低水平。尽管这种方法在FLOPs方面有所改进,但其碎片计算效率很低。此外,上述网络通常伴随着额外的数据操作,如级联、Shuffle和池化,这些操作的运行时间对于小型模型来说往往很重要。
除了上述纯卷积神经网络(CNNs)之外,人们对使视觉Transformer(ViTs)和多层感知器(MLP)架构更小更快也越来越感兴趣。例如,MobileViT和MobileFormer通过将DWConv与改进的注意力机制相结合,降低了计算复杂性。然而,它们仍然受到DWConv的上述问题的困扰,并且还需要修改的注意力机制的专用硬件支持。使用先进但耗时的标准化和激活层也可能限制其在设备上的速度。
所有这些问题一起导致了以下问题:这些“快速”的神经网络真的很快吗?为了回答这个问题,作者检查了延迟和FLOPs之间的关系,这由
其中FLOPS是每秒浮点运算的缩写,作为有效计算速度的度量。虽然有许多减少FLOPs的尝试,但都很少考虑同时优化FLOPs以实现真正的低延迟。为了更好地理解这种情况,作者比较了Intel CPU上典型神经网络的FLOPS。
图中的结果表明,许多现有神经网络的FLOPS较低,其FLOPS通常低于流行的ResNet50。由于FLOPS如此之低,这些“快速”的神经网络实际上不够快。它们的FLOPs减少不能转化为延迟的确切减少量。在某些情况下,没有任何改善,甚至会导致更糟的延迟。例如,CycleMLP-B1具有ResNet50的一半FLOPs,但运行速度较慢(即CycleMLPB1与ResNet50:111.9ms与69.4ms)。
请注意,FLOPs与延迟之间的差异在之前的工作中也已被注意到,但由于它们采用了DWConv/GConv和具有低FLOPs的各种数据处理,因此部分问题仍未解决。人们认为没有更好的选择。
该博客提供的方案通过开发一种简单、快速、有效的运算符来消除这种差异,该运算符可以在减少FLOPs的情况下保持高FLOPS。
具体来说,作者重新审视了现有的操作符,特别是DWConv的计算速度——FLOPS。作者发现导致低FLOPS问题的主要原因是频繁的内存访问。然后,作者提出了PConv作为一种竞争性替代方案,它减少了计算冗余以及内存访问的数量。
图1说明了PConv的设计。它利用了特征图中的冗余,并系统地仅在一部分输入通道上应用规则卷积(Conv),而不影响其余通道。本质上,PConv的FLOPs低于常规Conv,而FLOPs高于DWConv/GConv。换句话说,PConv更好地利用了设备上的计算能力。PConv在提取空间特征方面也很有效,这在本文后面的实验中得到了验证。
作者进一步引入PConv设计了FasterNet作为一个在各种设备上运行速度非常快的新网络家族。特别是,FasterNet在分类、检测和分割任务方面实现了最先进的性能,同时具有更低的延迟和更高的吞吐量。例如,在GPU、CPU和ARM处理器上,小模型FasterNet-T0分别比MobileVitXXS快3.1倍、3.1倍和2.5倍,而在ImageNet-1k上的准确率高2.9%。大模型FasterNet-L实现了83.5%的Top-1精度,与Swin-B不相上下,同时在GPU上提供了49%的高吞吐量,在CPU上节省了42%的计算时间。
总之,贡献如下:
指出了实现更高FLOPS的重要性,而不仅仅是为了更快的神经网络而减少FLOPs。
引入了一种简单但快速且有效的卷积PConv,它很有可能取代现有的选择DWConv。
推出FasterNet,它在GPU、CPU和ARM处理器等多种设备上运行良好且普遍快速。
对各种任务进行了广泛的实验,并验证了PConv和FasterNet的高速性和有效性。
Conv和FasterNet的设计
原理
DWConv是Conv的一种流行变体,已被广泛用作许多神经网络的关键构建块。对于输入,DWConv应用个滤波器来计算输出。如图(b)所示,每个滤波器在一个输入通道上进行空间滑动,并对一个输出通道做出贡献。
与具有的FLOPs常规Conv相比,这种深度计算使得DWConv仅仅具有的FLOPs。虽然在减少FLOPs方面有效,但DWConv(通常后跟逐点卷积或PWConv)不能简单地用于替换常规Conv,因为它会导致严重的精度下降。因此,在实践中,DWConv的通道数(或网络宽度)增加到>以补偿精度下降,例如,倒置残差块中的DWConv宽度扩展了6倍。然而,这会导致更高的内存访问,这会造成不可忽略的延迟,并降低总体计算速度,尤其是对于I/O绑定设备。特别是,内存访问的数量现在上升到
它比一个常规的Conv的值要高,即,
注意,内存访问花费在I/O操作上,这被认为已经是最小的成本,很难进一步优化。
PConv作为一个基本的算子
在下面演示了通过利用特征图的冗余度可以进一步优化成本。如图所示,特征图在不同通道之间具有高度相似性。许多其他著作也涵盖了这种冗余,但很少有人以简单而有效的方式充分利用它。
具体而言,作者提出了一种简单的PConv,以同时减少计算冗余和内存访问。图4中的左下角说明了PConv的工作原理。它只需在输入通道的一部分上应用常规Conv进行空间特征提取,并保持其余通道不变。对于连续或规则的内存访问,将第一个或最后一个连续的通道视为整个特征图的代表进行计算。在不丧失一般性的情况下认为输入和输出特征图具有相同数量的通道。因此,PConv的FLOPs仅
对于典型的r=1/4 ,PConv的FLOPs只有常规Conv的1/16。此外,PConv的内存访问量较小,即:
对于r=1/4,其仅为常规Conv的1/4。
由于只有通道用于空间特征提取,人们可能会问是否可以简单地移除剩余的(c−)通道?如果是这样,PConv将退化为具有较少通道的常规Conv,这偏离了减少冗余的目标。
请注意,保持其余通道不变,而不是从特征图中删除它们。这是因为它们对后续PWConv层有用,PWConv允许特征信息流经所有通道。
PConv之后是PWConv
为了充分有效地利用来自所有通道的信息,进一步将逐点卷积(PWConv)附加到PConv。它们在输入特征图上的有效感受野看起来像一个T形Conv,与均匀处理补丁的常规Conv相比,它更专注于中心位置,如图5所示。为了证明这个T形感受野的合理性,首先通过计算位置的Frobenius范数来评估每个位置的重要性。
假设,如果一个职位比其他职位具有更大的Frobenius范数,则该职位往往更重要。对于正则Conv滤波器,位置处的Frobenius范数由计算,其中。
一个显著位置是具有最大Frobenius范数的位置。然后,在预训练的ResNet18中集体检查每个过滤器,找出它们的显著位置,并绘制显著位置的直方图。图6中的结果表明,中心位置是过滤器中最常见的突出位置。换句话说,中心位置的权重比周围的更重。这与集中于中心位置的T形计算一致。
虽然T形卷积可以直接用于高效计算,但作者表明,将T形卷积分解为PConv和PWConv更好,因为该分解利用了滤波器间冗余并进一步节省了FLOPs。对于相同的输入和输出,T形Conv的FLOPs可以计算为:
它高于PConv和PWConv的流量,即:
其中和(例如,当时)。此外,可以很容易地利用常规Conv进行两步实现。
FasterNet作为Backbone
鉴于新型PConv和现成的PWConv作为主要的算子,进一步提出FasterNet,这是一个新的神经网络家族,运行速度非常快,对许多视觉任务非常有效。目标是使体系结构尽可能简单,使其总体上对硬件友好。
在图中展示了整体架构。它有4个层次级,每个层次级前面都有一个嵌入层(步长为4的常规4×4卷积)或一个合并层(步长为2的常规2×2卷积),用于空间下采样和通道数量扩展。每个阶段都有一堆FasterNet块。作者观察到,最后两个阶段中的块消耗更少的内存访问,并且倾向于具有更高的FLOPS,如表1中的经验验证。因此,放置了更多FasterNet块,并相应地将更多计算分配给最后两个阶段。每个FasterNet块有一个PConv层,后跟2个PWConv(或Conv 1×1)层。它们一起显示为倒置残差块,其中中间层具有扩展的通道数量,并且放置了Shorcut以重用输入特征。
除了上述算子,标准化和激活层对于高性能神经网络也是不可或缺的。然而,许多先前的工作在整个网络中过度使用这些层,这可能会限制特征多样性,从而损害性能。它还可以降低整体计算速度。相比之下,只将它们放在每个中间PWConv之后,以保持特征多样性并实现较低的延迟。
此外,使用批次归一化(BN)代替其他替代方法。BN的优点是,它可以合并到其相邻的Conv层中,以便更快地进行推断,同时与其他层一样有效。对于激活层,根据经验选择了GELU用于较小的FasterNet变体,而ReLU用于较大的FasterNet变体,同时考虑了运行时间和有效性。最后三个层,即全局平均池化、卷积1×1和全连接层,一起用于特征转换和分类。
为了在不同的计算预算下提供广泛的应用,提供FasterNet的Tiny模型、Small模型、Medium模型和Big模型变体,分别称为FasterNetT0/1/2、FasterNet-S、FasterNet-M和FasterNet-L。它们具有相似的结构,但深度和宽度不同。
架构规范如下:
9.系统功能展示
图9.1.系统支持检测结果表格显示
图9.2.系统支持置信度和IOU阈值手动调节
图9.3.系统支持自定义加载权重文件best.pt(需要你通过步骤5中训练获得)
图9.4.系统支持摄像头实时识别
图9.5.系统支持图片识别
图9.6.系统支持视频识别
图9.7.系统支持识别结果文件自动保存
图9.8.系统支持Excel导出检测结果数据
10. YOLOv11核心改进源码讲解
10.1 CTrans.py
以下是对代码中最核心部分的提炼和详细中文注释:
import torch
import torch.nn as nn
import numpy as np
from torch.nn import Dropout, Softmax, Conv2d, LayerNorm
class Channel_Embeddings(nn.Module):
"""从图像的patch和位置嵌入构建通道嵌入"""
def __init__(self, patchsize, img_size, in_channels):
super().__init__()
img_size = (img_size, img_size) # 将图像大小转换为元组
patch_size = (patchsize, patchsize) # 将patch大小转换为元组
n_patches = (img_size[0] // patch_size[0]) * (img_size[1] // patch_size[1]) # 计算patch的数量
# 使用最大池化和卷积层构建patch嵌入
self.patch_embeddings = nn.Sequential(
nn.MaxPool2d(kernel_size=5, stride=5),
Conv2d(in_channels=in_channels,
out_channels=in_channels,
kernel_size=patchsize // 5,
stride=patchsize // 5)
)
# 位置嵌入参数
self.position_embeddings = nn.Parameter(torch.zeros(1, n_patches, in_channels))
self.dropout = Dropout(0.1) # dropout层以防止过拟合
def forward(self, x):
"""前向传播"""
if x is None:
return None
x = self.patch_embeddings(x) # 通过patch嵌入
x = x.flatten(2) # 展平为(B, n_patches, hidden)
x = x.transpose(-1, -2) # 转置为(B, n_patches, hidden)
embeddings = x + self.position_embeddings # 添加位置嵌入
embeddings = self.dropout(embeddings) # 应用dropout
return embeddings
class Attention_org(nn.Module):
"""多头注意力机制"""
def __init__(self, vis, channel_num):
super(Attention_org, self).__init__()
self.vis = vis # 可视化标志
self.KV_size = sum(channel_num) # 键值对的大小
self.channel_num = channel_num # 通道数量
self.num_attention_heads = 4 # 注意力头的数量
# 初始化查询、键、值的线性层
self.query = nn.ModuleList([nn.Linear(c, c, bias=False) for c in channel_num])
self.key = nn.Linear(self.KV_size, self.KV_size, bias=False)
self.value = nn.Linear(self.KV_size, self.KV_size, bias=False)
self.softmax = Softmax(dim=3) # softmax层
self.attn_dropout = Dropout(0.1) # 注意力dropout
self.proj_dropout = Dropout(0.1) # 投影dropout
def forward(self, emb_list, emb_all):
"""前向传播"""
multi_head_Q_list = [query(emb) for query, emb in zip(self.query, emb_list)]
K = self.key(emb_all) # 计算键
V = self.value(emb_all) # 计算值
# 计算注意力分数
attention_scores = [torch.matmul(Q, K) / np.sqrt(self.KV_size) for Q in multi_head_Q_list]
attention_probs = [self.softmax(score) for score in attention_scores]
# 应用dropout
attention_probs = [self.attn_dropout(prob) for prob in attention_probs]
# 计算上下文层
context_layers = [torch.matmul(prob, V) for prob in attention_probs]
# 通过线性层输出
outputs = [query_layer for query_layer in context_layers]
return outputs
class Block_ViT(nn.Module):
"""ViT块,包含注意力和前馈网络"""
def __init__(self, vis, channel_num):
super(Block_ViT, self).__init__()
self.attn_norm = LayerNorm(sum(channel_num), eps=1e-6) # 归一化层
self.channel_attn = Attention_org(vis, channel_num) # 注意力模块
self.ffn = nn.Sequential(
nn.Linear(channel_num[0], channel_num[0] * 4), # 前馈网络
nn.GELU(),
nn.Linear(channel_num[0] * 4, channel_num[0])
)
def forward(self, emb_list):
"""前向传播"""
emb_all = torch.cat(emb_list, dim=2) # 连接所有嵌入
emb_all = self.attn_norm(emb_all) # 归一化
attn_outputs = self.channel_attn(emb_list, emb_all) # 计算注意力输出
outputs = [org + attn for org, attn in zip(emb_list, attn_outputs)] # 残差连接
return outputs
class Encoder(nn.Module):
"""编码器,包含多个ViT块"""
def __init__(self, vis, channel_num):
super(Encoder, self).__init__()
self.layer = nn.ModuleList([Block_ViT(vis, channel_num) for _ in range(1)]) # 多个ViT块
def forward(self, emb_list):
"""前向传播"""
for layer_block in self.layer:
emb_list = layer_block(emb_list) # 逐层传递嵌入
return emb_list
class ChannelTransformer(nn.Module):
"""通道变换器模型"""
def __init__(self, channel_num=[64, 128, 256, 512], img_size=640, vis=False, patchSize=[40, 20, 10, 5]):
super().__init__()
self.embeddings = nn.ModuleList([Channel_Embeddings(patch, img_size // (2 ** i), c)
for i, (patch, c) in enumerate(zip(patchSize, channel_num))])
self.encoder = Encoder(vis, channel_num) # 编码器
self.reconstruct = nn.ModuleList([Reconstruct(c, c, kernel_size=1, scale_factor=(patch, patch))
for patch, c in zip(patchSize, channel_num)])
def forward(self, en):
"""前向传播"""
emb_list = [emb(en[i]) for i, emb in enumerate(self.embeddings) if en[i] is not None]
encoded = self.encoder(emb_list) # 编码
outputs = [recon(enc) + en[i] for i, (recon, enc) in enumerate(zip(self.reconstruct, encoded)) if en[i] is not None]
return outputs
代码说明:
- Channel_Embeddings:构建图像的通道嵌入,包括patch嵌入和位置嵌入。
- Attention_org:实现多头注意力机制,计算查询、键、值并生成注意力输出。
- Block_ViT:包含注意力层和前馈网络的ViT块,使用残差连接。
- Encoder:由多个ViT块组成的编码器,逐层处理嵌入。
- ChannelTransformer:主模型,负责输入图像的嵌入、编码和重构输出。
通过这些核心组件,模型能够有效地处理图像数据并提取特征。
这个文件定义了一个名为 CTrans.py
的深度学习模型,主要用于图像处理任务,特别是图像的特征提取和重建。模型的核心结构是一个通道变换器(Channel Transformer),它通过多个模块对输入的图像进行处理。以下是对文件中各个部分的详细讲解。
首先,文件导入了一些必要的库,包括 torch
和 torch.nn
,这些是构建神经网络的基础库。接着,定义了几个类,分别实现不同的功能。
Channel_Embeddings
类用于构建输入图像的嵌入。它通过最大池化和卷积操作将输入图像划分为多个小块(patch),并为每个小块生成位置嵌入。这个类的 forward
方法接收输入图像,经过处理后返回嵌入结果。
Reconstruct
类则负责将经过编码的特征重新构建成图像。它使用卷积层和上采样操作来实现特征的恢复,确保输出的形状与输入的形状相匹配。
Attention_org
类实现了多头注意力机制。它通过线性变换生成查询(Query)、键(Key)和值(Value),并计算注意力分数。注意力机制允许模型关注输入的不同部分,从而提高特征提取的能力。该类的 forward
方法处理多个输入嵌入,并计算注意力权重。
Mlp
类实现了一个简单的多层感知机(MLP),用于特征的非线性变换。它包含两个全连接层和一个激活函数(GELU),并在每一层后应用了 dropout,以防止过拟合。
Block_ViT
类则将注意力机制和多层感知机结合在一起,形成一个完整的变换器块。它首先对输入进行层归一化,然后通过注意力机制处理特征,最后通过 MLP 进行进一步的处理。这个类的 forward
方法将输入嵌入进行处理,并返回更新后的嵌入。
Encoder
类是一个编码器,包含多个 Block_ViT
模块。它将输入的嵌入传递给这些模块,并返回经过处理的嵌入。
ChannelTransformer
类是整个模型的核心,负责将输入图像分为多个通道并进行处理。它首先通过 Channel_Embeddings
生成嵌入,然后通过 Encoder
进行编码,最后通过 Reconstruct
进行重建。这个类的 forward
方法整合了所有步骤,输出重建后的图像。
最后,GetIndexOutput
类用于从模型的输出中提取特定的索引,方便后续处理。
总体而言,这个文件实现了一个复杂的图像处理模型,结合了卷积、注意力机制和多层感知机等多种深度学习技术,旨在提高图像特征提取和重建的效果。
10.2 efficientViT.py
以下是简化后的代码,保留了核心部分,并添加了详细的中文注释:
import torch
import torch.nn as nn
import torch.nn.functional as F
import itertools
# 定义卷积层和批归一化的组合
class Conv2d_BN(torch.nn.Sequential):
def __init__(self, in_channels, out_channels, kernel_size=1, stride=1, padding=0):
super().__init__()
# 添加卷积层
self.add_module('conv', torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding, bias=False))
# 添加批归一化层
self.add_module('bn', torch.nn.BatchNorm2d(out_channels))
@torch.no_grad()
def switch_to_deploy(self):
# 将训练模式下的BN层转换为推理模式
conv, bn = self._modules.values()
w = bn.weight / (bn.running_var + bn.eps)**0.5
w = conv.weight * w[:, None, None, None]
b = bn.bias - bn.running_mean * bn.weight / (bn.running_var + bn.eps)**0.5
# 返回新的卷积层
return torch.nn.Conv2d(w.size(1) * conv.groups, w.size(0), w.shape[2:], stride=conv.stride, padding=conv.padding, groups=conv.groups)
# 定义EfficientViT的基本模块
class EfficientViTBlock(torch.nn.Module):
def __init__(self, embed_dim, key_dim, num_heads=8):
super().__init__()
# 第一部分:卷积和残差连接
self.dw0 = nn.Sequential(
Conv2d_BN(embed_dim, embed_dim, kernel_size=3, stride=1, padding=1, groups=embed_dim),
nn.ReLU()
)
# 第二部分:前馈网络
self.ffn0 = nn.Sequential(
Conv2d_BN(embed_dim, embed_dim * 2, kernel_size=1),
nn.ReLU(),
Conv2d_BN(embed_dim * 2, embed_dim, kernel_size=1)
)
# 注意力机制
self.mixer = LocalWindowAttention(embed_dim, key_dim, num_heads)
def forward(self, x):
# 前向传播
return self.ffn0(self.dw0(x)) + self.mixer(x)
# 定义局部窗口注意力机制
class LocalWindowAttention(torch.nn.Module):
def __init__(self, dim, key_dim, num_heads=8):
super().__init__()
self.attn = CascadedGroupAttention(dim, key_dim, num_heads)
def forward(self, x):
# 前向传播
return self.attn(x)
# 定义高效的视觉变换器
class EfficientViT(torch.nn.Module):
def __init__(self, img_size=224, embed_dim=[64, 128, 192], depth=[1, 2, 3], num_heads=[4, 4, 4]):
super().__init__()
self.patch_embed = nn.Sequential(
Conv2d_BN(3, embed_dim[0] // 8, kernel_size=3, stride=2, padding=1),
nn.ReLU(),
Conv2d_BN(embed_dim[0] // 8, embed_dim[0] // 4, kernel_size=3, stride=2, padding=1),
nn.ReLU()
)
# 构建多个EfficientViTBlock
self.blocks = nn.ModuleList()
for i in range(len(depth)):
for _ in range(depth[i]):
self.blocks.append(EfficientViTBlock(embed_dim[i], key_dim=16, num_heads=num_heads[i]))
def forward(self, x):
x = self.patch_embed(x)
for block in self.blocks:
x = block(x)
return x
# 实例化模型
if __name__ == '__main__':
model = EfficientViT()
inputs = torch.randn((1, 3, 224, 224)) # 输入一个224x224的RGB图像
res = model(inputs) # 前向传播
print(res.size()) # 输出结果的尺寸
代码说明:
- Conv2d_BN: 这个类结合了卷积层和批归一化层,并提供了一个方法用于将训练模式下的批归一化转换为推理模式。
- EfficientViTBlock: 这是EfficientViT的基本构建块,包含卷积层、前馈网络和局部窗口注意力机制。
- LocalWindowAttention: 实现了局部窗口注意力机制,用于增强特征表示。
- EfficientViT: 整个模型的构建,包括图像嵌入和多个EfficientViTBlock的堆叠。
- 主程序: 实例化模型并进行一次前向传播,输出结果的尺寸。
这个程序文件定义了一个名为 EfficientViT
的高效视觉变换器模型,主要用于下游任务的图像处理。文件的开头部分包含了一些版权信息和作者信息,接着导入了必要的库,包括 PyTorch 和一些自定义的模块。
程序中定义了多个类和函数,首先是 Conv2d_BN
类,它是一个包含卷积层和批归一化层的序列模型。该类的构造函数初始化了卷积层和批归一化层,并对批归一化的权重进行了初始化。switch_to_deploy
方法用于在推理阶段将批归一化层融合到卷积层中,以提高推理速度。
接下来是 replace_batchnorm
函数,它遍历模型的所有子模块,将批归一化层替换为恒等映射,以便在推理时加速。
PatchMerging
类实现了一个用于合并图像块的模块,包含多个卷积层和一个 Squeeze-and-Excitation(SE)模块。它的 forward
方法对输入进行一系列卷积和激活操作。
Residual
类实现了残差连接,允许在训练期间随机丢弃一些输入,以提高模型的鲁棒性。
FFN
类定义了一个前馈神经网络模块,包含两个卷积层和一个 ReLU 激活函数。
CascadedGroupAttention
和 LocalWindowAttention
类实现了不同类型的注意力机制,用于在模型中提取特征。CascadedGroupAttention
使用分组卷积来计算注意力,而 LocalWindowAttention
则在局部窗口内进行注意力计算。
EfficientViTBlock
类是一个基本的 EfficientViT 构建块,结合了卷积、前馈网络和注意力机制。
EfficientViT
类是整个模型的核心部分,它将所有的构建块组合在一起,并通过多个阶段进行特征提取。构造函数中定义了输入图像的大小、补丁大小、嵌入维度、深度、注意力头数等超参数。模型的前向传播方法 forward
将输入图像通过各个模块,输出多个特征图。
在文件的最后部分,定义了一些不同配置的 EfficientViT 模型(如 EfficientViT_m0
到 EfficientViT_m5
),每个配置都有不同的超参数设置。接着定义了一些函数用于创建这些模型,并加载预训练权重。
最后的 if __name__ == '__main__':
部分是程序的入口,创建了一个 EfficientViT_M0
模型实例,并对一个随机生成的输入进行前向传播,打印输出的特征图的尺寸。这部分代码可以用于测试模型的基本功能。
10.3 hcfnet.py
以下是经过简化和注释的核心代码部分,保留了重要的模块和功能,并对每个部分进行了详细的中文注释:
import math
import torch
import torch.nn as nn
import torch.nn.functional as F
# 定义空间注意力模块
class SpatialAttentionModule(nn.Module):
def __init__(self):
super(SpatialAttentionModule, self).__init__()
# 使用卷积层将输入通道数从2降到1,kernel_size为7,padding为3
self.conv2d = nn.Conv2d(in_channels=2, out_channels=1, kernel_size=7, stride=1, padding=3)
self.sigmoid = nn.Sigmoid() # Sigmoid激活函数
def forward(self, x):
# 计算输入的平均值和最大值
avgout = torch.mean(x, dim=1, keepdim=True) # 在通道维度上取平均
maxout, _ = torch.max(x, dim=1, keepdim=True) # 在通道维度上取最大值
out = torch.cat([avgout, maxout], dim=1) # 将平均值和最大值拼接
out = self.sigmoid(self.conv2d(out)) # 通过卷积和Sigmoid激活
return out * x # 将注意力权重应用于输入
# 定义局部-全局注意力模块
class LocalGlobalAttention(nn.Module):
def __init__(self, output_dim, patch_size):
super().__init__()
self.output_dim = output_dim
self.patch_size = patch_size
self.mlp1 = nn.Linear(patch_size * patch_size, output_dim // 2) # MLP层1
self.norm = nn.LayerNorm(output_dim // 2) # 层归一化
self.mlp2 = nn.Linear(output_dim // 2, output_dim) # MLP层2
self.conv = nn.Conv2d(output_dim, output_dim, kernel_size=1) # 1x1卷积
self.prompt = torch.nn.parameter.Parameter(torch.randn(output_dim, requires_grad=True)) # 可学习的参数
self.top_down_transform = torch.nn.parameter.Parameter(torch.eye(output_dim), requires_grad=True) # 顶层变换
def forward(self, x):
x = x.permute(0, 2, 3, 1) # 调整维度顺序
B, H, W, C = x.shape # 获取批量大小、高度、宽度和通道数
P = self.patch_size
# 局部分支
local_patches = x.unfold(1, P, P).unfold(2, P, P) # 获取局部补丁
local_patches = local_patches.reshape(B, -1, P * P, C) # 重塑为适合MLP的形状
local_patches = local_patches.mean(dim=-1) # 在通道维度上取平均
local_patches = self.mlp1(local_patches) # 第一个MLP
local_patches = self.norm(local_patches) # 归一化
local_patches = self.mlp2(local_patches) # 第二个MLP
local_attention = F.softmax(local_patches, dim=-1) # 计算局部注意力
local_out = local_patches * local_attention # 应用注意力
# 计算余弦相似度并应用掩码
cos_sim = F.normalize(local_out, dim=-1) @ F.normalize(self.prompt[None, ..., None], dim=1)
mask = cos_sim.clamp(0, 1)
local_out = local_out * mask
local_out = local_out @ self.top_down_transform # 应用顶层变换
# 恢复形状并进行上采样
local_out = local_out.reshape(B, H // P, W // P, self.output_dim)
local_out = local_out.permute(0, 3, 1, 2) # 调整维度顺序
local_out = F.interpolate(local_out, size=(H, W), mode='bilinear', align_corners=False) # 上采样
output = self.conv(local_out) # 通过卷积层
return output # 返回输出
# 定义PPA模块
class PPA(nn.Module):
def __init__(self, in_features, filters) -> None:
super().__init__()
self.skip = nn.Conv2d(in_features, filters, kernel_size=1) # 跳跃连接
self.c1 = nn.Conv2d(filters, filters, kernel_size=3, padding=1) # 卷积层1
self.c2 = nn.Conv2d(filters, filters, kernel_size=3, padding=1) # 卷积层2
self.c3 = nn.Conv2d(filters, filters, kernel_size=3, padding=1) # 卷积层3
self.sa = SpatialAttentionModule() # 空间注意力模块
self.lga2 = LocalGlobalAttention(filters, 2) # 局部-全局注意力模块
self.lga4 = LocalGlobalAttention(filters, 4) # 局部-全局注意力模块
self.drop = nn.Dropout2d(0.1) # Dropout层
self.bn1 = nn.BatchNorm2d(filters) # 批归一化
self.silu = nn.SiLU() # SiLU激活函数
def forward(self, x):
x_skip = self.skip(x) # 跳跃连接
x_lga2 = self.lga2(x_skip) # 局部-全局注意力2
x_lga4 = self.lga4(x_skip) # 局部-全局注意力4
x1 = self.c1(x) # 卷积1
x2 = self.c2(x1) # 卷积2
x3 = self.c3(x2) # 卷积3
x = x1 + x2 + x3 + x_skip + x_lga2 + x_lga4 # 融合多个特征
x = self.bn1(x) # 批归一化
x = self.sa(x) # 应用空间注意力
x = self.drop(x) # Dropout
x = self.silu(x) # SiLU激活
return x # 返回输出
代码核心部分说明:
- SpatialAttentionModule: 该模块通过计算输入特征图的平均值和最大值来生成空间注意力权重,并将其应用于输入特征图。
- LocalGlobalAttention: 该模块通过局部补丁的方式计算局部特征,并结合全局特征生成最终的输出特征图。
- PPA: 该模块结合了卷积、跳跃连接、局部-全局注意力和空间注意力,生成丰富的特征表示。
这些模块可以用于构建更复杂的神经网络架构,特别是在计算机视觉任务中。
这个程序文件 hcfnet.py
定义了一些用于深度学习的模块,主要是构建一个网络模型,可能用于图像处理或计算机视觉任务。文件中包含多个类,每个类实现了特定的功能。
首先,SpatialAttentionModule
类实现了空间注意力机制。它通过计算输入特征图的平均值和最大值,生成一个注意力图,然后通过卷积层处理这个注意力图,最后将其与输入特征图相乘,以增强特征图中重要区域的表示。
接下来,LocalGlobalAttention
类实现了局部和全局注意力机制。它首先将输入特征图划分为多个局部块,并对这些块进行处理以提取局部特征。通过两个多层感知机(MLP)进行特征变换,并使用层归一化来稳定训练过程。局部特征经过处理后,结合全局特征生成最终的输出。
ECA
类实现了有效通道注意力机制(ECA),它通过自适应平均池化和一维卷积来计算通道注意力,强调重要通道的特征表示。其核心思想是根据输入通道的数量动态选择卷积核的大小。
PPA
类是一个组合模块,整合了前面提到的空间注意力、有效通道注意力和局部全局注意力。它使用多个卷积层来提取特征,并通过跳跃连接将不同层的特征结合起来,以增强模型的表达能力。最终的输出经过批归一化和激活函数处理。
Bag
类是一个简单的加权融合模块,接收三个输入,通过计算边缘注意力来加权组合输入特征。
最后,DASI
类是一个更复杂的模块,结合了多个输入特征图,使用跳跃连接和卷积层进行特征融合。它通过 Bag
类对不同尺度的特征进行加权融合,并最终生成输出特征图。这个类的设计允许处理不同分辨率的特征图,以提高模型的性能。
整体来看,这个文件实现了一种复杂的网络结构,利用注意力机制和多尺度特征融合来增强特征表示,适用于图像处理等任务。
10.4 mamba_vss.py
以下是经过简化和注释的核心代码部分:
import torch
import torch.nn as nn
import torch.nn.functional as F
class SS2D(nn.Module):
def __init__(self, d_model, d_state=16, d_conv=3, expand=2, dropout=0.):
super().__init__()
self.d_model = d_model # 输入特征维度
self.d_state = d_state # 状态维度
self.d_conv = d_conv # 卷积核大小
self.expand = expand # 扩展因子
self.d_inner = int(self.expand * self.d_model) # 内部特征维度
# 输入线性变换
self.in_proj = nn.Linear(self.d_model, self.d_inner * 2)
# 卷积层
self.conv2d = nn.Conv2d(in_channels=self.d_inner, out_channels=self.d_inner, groups=self.d_inner, kernel_size=d_conv, padding=(d_conv - 1) // 2)
self.act = nn.SiLU() # 激活函数
# 输出线性变换
self.out_proj = nn.Linear(self.d_inner, self.d_model)
self.dropout = nn.Dropout(dropout) if dropout > 0. else None # Dropout层
def forward(self, x: torch.Tensor):
# 前向传播
B, H, W, C = x.shape # 获取输入的维度
xz = self.in_proj(x) # 输入线性变换
x, z = xz.chunk(2, dim=-1) # 分割为x和z
x = x.permute(0, 3, 1, 2).contiguous() # 调整维度以适应卷积
x = self.act(self.conv2d(x)) # 卷积和激活
y = self.forward_core(x) # 核心前向传播
y = y * F.silu(z) # 结合z
out = self.out_proj(y) # 输出线性变换
if self.dropout is not None:
out = self.dropout(out) # 应用Dropout
return out
def forward_core(self, x: torch.Tensor):
# 核心前向传播逻辑
# 这里省略了具体实现,主要是对输入进行处理和计算
# 返回处理后的结果
return x # 这里仅为示例,实际应为处理后的输出
class VSSBlock(nn.Module):
def __init__(self, hidden_dim: int = 0, drop_path: float = 0.2):
super().__init__()
self.ln_1 = nn.LayerNorm(hidden_dim) # 归一化层
self.self_attention = SS2D(d_model=hidden_dim) # 自注意力层
self.drop_path = nn.Dropout(drop_path) # DropPath层
def forward(self, input: torch.Tensor):
input = input.permute((0, 2, 3, 1)) # 调整输入维度
x = input + self.drop_path(self.self_attention(self.ln_1(input))) # 残差连接
return x.permute((0, 3, 1, 2)) # 调整输出维度
# 示例代码,展示如何使用VSSBlock
if __name__ == '__main__':
inputs = torch.randn((1, 64, 32, 32)).cuda() # 随机输入
model = VSSBlock(64).cuda() # 创建模型
pred = model(inputs) # 前向传播
print(pred.size()) # 输出结果的尺寸
代码注释说明:
- SS2D类:实现了一个自注意力机制的模块,包含输入线性变换、卷积层、激活函数和输出线性变换。
forward
方法负责处理输入并返回输出。 - VSSBlock类:是一个包含归一化、自注意力和DropPath的模块,使用残差连接来增强模型的表达能力。
- 前向传播:在
forward
方法中,输入数据的维度被调整以适应后续的计算,最后返回经过处理的输出。
该代码实现了自注意力机制的基本结构,并且提供了使用示例。
这个程序文件 mamba_vss.py
定义了几个神经网络模块,主要是基于 PyTorch 框架实现的。文件中包含了一个主要的类 SS2D
,以及两个继承自 VSSBlock
的类 VSSBlock
和 Mamba2Block
。这些模块主要用于构建深度学习模型,特别是在处理时序数据或图像数据时。
首先,SS2D
类是一个复杂的神经网络模块,包含多个参数和层。它的构造函数接收许多超参数,如 d_model
(模型的维度)、d_state
(状态的维度)、d_conv
(卷积核的大小)、expand
(扩展因子)等。该类内部定义了多个层,包括线性层、卷积层和激活函数等。
在 SS2D
的 forward
方法中,输入数据首先通过一个线性投影层,然后经过卷积层和激活函数处理。接着,数据会进入一个核心的前向计算过程 forward_corev0
,该过程涉及到对输入的多种变换和处理,包括选择性扫描(selective_scan
),这是一种用于处理时序数据的技术。最终,经过一系列的处理后,输出结果会通过层归一化和线性投影得到最终的输出。
接下来,VSSBlock
类继承自 nn.Module
,它实现了一个自注意力机制的模块。该模块在构造函数中初始化了层归一化和 SS2D
自注意力层,并且使用了 DropPath 技术来进行正则化。在 forward
方法中,输入数据经过层归一化后,传递给自注意力层,并与原始输入相加,形成残差连接。
最后,Mamba2Block
类是 VSSBlock
的一个变体,它将自注意力层替换为 Mamba2Simple
模块,后者是一个可能更为复杂的自注意力实现。这个类的 forward
方法与 VSSBlock
类似,主要是进行输入的变换和处理。
在文件的最后部分,有一个简单的测试代码段,用于验证 VSSBlock
和 Mamba2Block
的功能。它生成随机输入并将其传递给模型,打印输出的尺寸,以确保模型的前向传播能够正常工作。
整体来看,这个文件实现了一个基于自注意力机制的深度学习模块,适用于处理图像或时序数据,具有较高的灵活性和可扩展性。
注意:由于此博客编辑较早,上面“10.YOLOv11核心改进源码讲解”中部分代码可能会优化升级,仅供参考学习,以“11.完整训练+Web前端界面+200+种全套创新点源码、数据集获取(由于版权原因,本博客仅提供【原始博客的链接】,原始博客提供下载链接)”的内容为准。