改进yolo11-MLCA等200+全套创新点大全:箱子检测系统源码&数据集全套
1.图片效果展示
项目来源 人工智能促进会 2024.11.01
注意:由于项目一直在更新迭代,上面“1.图片效果展示”和“2.视频效果展示”展示的系统图片或者视频可能为老版本,新版本在老版本的基础上升级如下:(实际效果以升级的新版本为准)
(1)适配了YOLOV11的“目标检测”模型和“实例分割”模型,通过加载相应的权重(.pt)文件即可自适应加载模型。
(2)支持“图片识别”、“视频识别”、“摄像头实时识别”三种识别模式。
(3)支持“图片识别”、“视频识别”、“摄像头实时识别”三种识别结果保存导出,解决手动导出(容易卡顿出现爆内存)存在的问题,识别完自动保存结果并导出到tempDir中。
(4)支持Web前端系统中的标题、背景图等自定义修改。
另外本项目提供训练的数据集和训练教程,暂不提供权重文件(best.pt),需要您按照教程进行训练后实现图片演示和Web前端界面演示的效果。
2.视频效果展示
2.1 视频效果展示
3.背景
研究背景与意义
随着电子商务和物流行业的迅猛发展,货物的高效管理和监控变得愈发重要。在这一背景下,自动化的箱子检测系统应运而生,成为提升仓储和运输效率的关键技术之一。传统的人工检测方法不仅耗时耗力,而且容易出现误差,难以满足现代物流对实时性和准确性的高要求。因此,基于深度学习的目标检测技术,尤其是YOLO(You Only Look Once)系列模型,因其在速度和精度上的优势,逐渐成为研究的热点。
本研究聚焦于改进YOLOv11模型在箱子检测中的应用,利用NEW_PALLET数据集进行训练和测试。该数据集包含812张图像,涵盖了2个类别,其中包括“Box-mDiy”这一特定类别。这一数据集的构建为模型的训练提供了丰富的样本,有助于提高检测的准确性和鲁棒性。通过对YOLOv11模型的改进,旨在进一步提升其在复杂环境下的检测能力,尤其是在不同光照、角度和背景下的箱子识别。
改进后的YOLOv11模型不仅能够实现快速的实时检测,还能在多种场景下保持高准确率,这对于物流行业的智能化转型具有重要意义。通过有效识别和定位箱子,系统能够为仓库管理、库存监控及货物追踪提供可靠的数据支持,进而提升整体运营效率。此外,该研究还将为相关领域的学术研究提供新的思路和方法,推动目标检测技术在实际应用中的发展。因此,基于改进YOLOv11的箱子检测系统的研究不仅具有理论价值,更具备广泛的实际应用前景。
4.数据集信息展示
4.1 本项目数据集详细数据(类别数&类别名)
nc: 2
names: [‘0’, ‘Box-mDiy’]
该项目为【目标检测】数据集,请在【训练教程和Web端加载模型教程(第三步)】这一步的时候按照【目标检测】部分的教程来训练
4.2 本项目数据集信息介绍
本项目数据集信息介绍
本项目所使用的数据集名为“NEW_PALLET”,旨在为改进YOLOv11的箱子检测系统提供强有力的支持。该数据集专注于特定的物体检测任务,包含两类目标,分别为“0”和“Box-mDiy”。其中,“0”代表一种基础的背景类别,而“Box-mDiy”则指代特定的箱子类型,具有多样的形状和尺寸,适合于不同的应用场景。通过对这两类目标的标注和分类,数据集为模型的训练提供了丰富的样本,确保了检测系统在实际应用中的准确性和鲁棒性。
“NEW_PALLET”数据集的构建经过精心设计,旨在涵盖各种可能的场景和条件,以提高YOLOv11在不同环境下的适应能力。数据集中包含了多种光照条件、视角和背景设置,使得模型能够学习到更加全面的特征,从而提升其在实际应用中的表现。此外,数据集的样本量经过严格筛选,确保每一类目标的代表性和多样性,进而为模型的泛化能力提供保障。
在数据集的标注过程中,采用了高精度的标注工具,确保每个目标的边界框准确无误。这一过程不仅提高了数据集的质量,也为后续的模型训练奠定了坚实的基础。通过使用“NEW_PALLET”数据集,改进后的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函数转换为需要的格式:
https://github.com/ultralytics/ultralytics/blob/cc3c774bde86ffce694d202b7383da6cc1721c1b/ultralytics/nn/modules.py#L378
https://github.com/ultralytics/ultralytics/blob/cc3c774bde86ffce694d202b7383da6cc1721c1b/ultralytics/yolo/utils/tal.py#L196。
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作为负样本。然后,通过损失函数(针对分类与定位的对齐而设计的损失函数)进行训练。
代码地址:ultralytics/ultralytics/yolo/utils/tal.py at
c0c0c138c12699807ff9446f942cb3bd325d670b · ultralytics/ultralytics ·
GitHub
默认参数如下(当前版本这些超参没有提供修改的接口,如需修改需要在源码上进行修改):
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等各种版本)
改进模块技术原理博客【Blog】网址链接
8.2 精选部分改进YOLOV11创新点原理讲解
这里节选部分改进创新点展开原理讲解(完整的改进原理见上图和改进模块技术原理博客链接【如果此小节的图加载失败可以通过CSDN或者Github搜索该博客的标题访问原始博客,原始博客图片显示正常】
视觉transformer(ViT)简介
视觉transformer(ViT)最近在各种计算机视觉任务中证明了巨大的成功,并受到了相当多的关注。与卷积神经网络(CNNs)相比,ViT具有更强的全局信息捕获能力和远程交互能力,表现出优于CNNs的准确性,特别是在扩大训练数据大小和模型大小时[An image is worth 16x16 words: Transformers for image recognition at scale,Coatnet]。
尽管ViT在低分辨率和高计算领域取得了巨大成功,但在高分辨率和低计算场景下,ViT仍不如cnn。例如,下图(左)比较了COCO数据集上当前基于cnn和基于vit的一级检测器。基于vit的检测器(160G mac)和基于cnn的检测器(6G mac)之间的效率差距超过一个数量级。这阻碍了在边缘设备的实时高分辨率视觉应用程序上部署ViT。
左图:现有的基于vit的一级检测器在实时目标检测方面仍然不如当前基于cnn的一级检测器,需要的计算量多出一个数量级。本文引入了第一个基于vit的实时对象检测器来弥补这一差距。在COCO上,efficientvit的AP比efficientdet高3.8,而mac较低。与YoloX相比,efficient ViT节省67.2%的计算成本,同时提供更高的AP。
中:随着输入分辨率的增加,计算成本呈二次增长,无法有效处理高分辨率的视觉应用。
右图:高分辨率对图像分割很重要。当输入分辨率从1024x2048降低到512x1024时,MobileNetV2的mIoU减少12% (8.5 mIoU)。在不提高分辨率的情况下,只提高模型尺寸是无法缩小性能差距的。
ViT的根本计算瓶颈是softmax注意模块,其计算成本随输入分辨率的增加呈二次增长。例如,如上图(中)所示,随着输入分辨率的增加,vit- small[Pytorch image models. https://github.com/rwightman/ pytorch-image-models]的计算成本迅速显著大于ResNet-152的计算成本。
解决这个问题的一个直接方法是降低输入分辨率。然而,高分辨率的视觉识别在许多现实世界的计算机视觉应用中是必不可少的,如自动驾驶,医疗图像处理等。当输入分辨率降低时,图像中的小物体和精细细节会消失,导致目标检测和语义分割性能显著下降。
上图(右)显示了在cityscape数据集上不同输入分辨率和宽度乘法器下MobileNetV2的性能。例如,将输入分辨率从1024x2048降低到512x1024会使cityscape的性能降低12% (8.5 mIoU)。即使是3.6倍高的mac,只放大模型尺寸而不增加分辨率也无法弥补这一性能损失。
除了降低分辨率外,另一种代表性的方法是限制softmax注意,方法是将其范围限制在固定大小的局部窗口内[Swin transformer,Swin transformer v2]或降低键/值张量的维数[Pyramid vision transformer,Segformer]。然而,它损害了ViT的非局部注意能力,降低了全局接受域(ViT最重要的优点),使得ViT与大内核cnn的区别更小[A convnet for the 2020s,Scaling up your kernels to 31x31: Revisiting large kernel design in cnns,Lite pose: Efficient architecture design for 2d human pose estimation]。
本文介绍了一个有效的ViT体系结构,以解决这些挑战。发现没有必要坚持softmax注意力。本文建议用线性注意[Transformers are rnns: Fast autoregressive transformers with linear attention]代替softmax注意。
线性注意的关键好处是,它保持了完整的n 2 n^2n 2
注意映射,就像softmax注意。同时,它利用矩阵乘法的联想特性,避免显式计算完整的注意映射,同时保持相同的功能。因此,它保持了softmax注意力的全局特征提取能力,且计算复杂度仅为线性。线性注意的另一个关键优点是它避免了softmax,这使得它在移动设备上更有效(下图左)。
左图:线性注意比类似mac下的softmax注意快3.3-4.5倍,这是因为去掉了硬件效率不高的softmax功能。延迟是在Qualcomm Snapdragon 855 CPU和TensorFlow-Lite上测量的。本文增加线性注意的头部数量,以确保它具有与softmax注意相似的mac。
中:然而,如果没有softmax注意中使用的非线性注意评分归一化,线性注意无法有效集中其注意分布,削弱了其局部特征提取能力。后文提供了可视化。
右图:本文用深度卷积增强线性注意,以解决线性注意的局限性。深度卷积可以有效地捕捉局部特征,而线性注意可以专注于捕捉全局信息。增强的线性注意在保持线性注意的效率和简单性的同时,表现出在各种视觉任务上的强大表现(图4)。
然而,直接应用线性注意也有缺点。以往的研究表明线性注意和softmax注意之间存在显著的性能差距(下图中间)。
左:高通骁龙855上的精度和延迟权衡。效率vit比效率网快3倍,精度更高。中:ImageNet上softmax注意与线性注意的比较。在相同的计算条件下,本文观察到softmax注意与线性注意之间存在显著的精度差距。而深度卷积增强模型后,线性注意的精度有明显提高。
相比之下,softmax注意的精度变化不大。在相同MAC约束下,增强线性注意比增强软最大注意提高了0.3%的精度。右图:与增强的softmax注意相比,增强的线性注意硬件效率更高,随着分辨率的增加,延迟增长更慢。
深入研究线性注意和softmax注意的详细公式,一个关键的区别是线性注意缺乏非线性注意评分归一化方案。这使得线性注意无法有效地将注意力分布集中在局部模式产生的高注意分数上,从而削弱了其局部特征提取能力。
本文认为这是线性注意的主要限制,使其性能不如softmax注意。本文提出了一个简单而有效的解决方案来解决这一限制,同时保持线性注意在低复杂度和低硬件延迟方面的优势。具体来说,本文建议通过在每个FFN层中插入额外的深度卷积来增强线性注意。因此,本文不需要依赖线性注意进行局部特征提取,避免了线性注意在捕捉局部特征方面的不足,并利用了线性注意在捕捉全局特征方面的优势。
本文广泛评估了efficient vit在低计算预算下对各种视觉任务的有效性,包括COCO对象检测、城市景观语义分割和ImageNet分类。本文想要突出高效的主干设计,所以没有包括任何正交的附加技术(例如,知识蒸馏,神经架构搜索)。尽管如此,在COCO val2017上,efficientvit的AP比efficientdet - d1高2.4倍,同时节省27.9%的计算成本。在cityscape上,efficientvit提供了比SegFormer高2.5个mIoU,同时降低了69.6%的计算成本。在ImageNet上,efficientvit在584M mac上实现了79.7%的top1精度,优于efficientnet - b1的精度,同时节省了16.6%的计算成本。
与现有的以减少参数大小或mac为目标的移动ViT模型[Mobile-former,Mobilevit,NASVit]不同,本文的目标是减少移动设备上的延迟。本文的模型不涉及复杂的依赖或硬件低效操作。因此,本文减少的计算成本可以很容易地转化为移动设备上的延迟减少。
在高通骁龙855 CPU上,efficient vit运行速度比efficientnet快3倍,同时提供更高的ImageNet精度。本文的代码和预训练的模型将在出版后向公众发布。
Efficient Vision Transformer.
提高ViT的效率对于在资源受限的边缘平台上部署ViT至关重要,如手机、物联网设备等。尽管ViT在高计算区域提供了令人印象深刻的性能,但在针对低计算区域时,它通常不如以前高效的cnn[Efficientnet, mobilenetv3,Once for all: Train one network and specialize it for efficient deployment]。为了缩小差距,MobileViT建议结合CNN和ViT的长处,使用transformer将卷积中的局部处理替换为全局处理。MobileFormer提出了在MobileNet和Transformer之间建立双向桥以实现特征融合的并行化。NASViT提出利用神经架构搜索来搜索高效的ViT架构。
这些模型在ImageNet上提供了极具竞争力的准确性和效率的权衡。然而,它们并不适合高分辨率的视觉任务,因为它们仍然依赖于softmax注意力。
在本节中,本文首先回顾了自然语言处理中的线性注意,并讨论了它的优缺点。接下来,本文介绍了一个简单而有效的解决方案来克服线性注意的局限性。最后,给出了efficient vit的详细架构。
为可学习投影矩阵。Oi表示矩阵O的第i行。Sim(·,·)为相似度函数。
虽然softmax注意力在视觉和NLP方面非常成功,但它并不是唯一的选择。例如,线性注意提出了如下相似度函数:
其中,φ(·)为核函数。在本工作中,本文选择了ReLU作为内核函数,因为它对硬件来说是友好的。当Sim(Q, K) = φ(Q)φ(K)T时,式(1)可改写为:
线性注意的一个关键优点是,它允许利用矩阵乘法的结合律,在不改变功能的情况下,将计算复杂度从二次型降低到线性型:
除了线性复杂度之外,线性注意的另一个关键优点是它不涉及注意模块中的softmax。Softmax在硬件上效率非常低。避免它可以显著减少延迟。例如,下图(左)显示了softmax注意和线性注意之间的延迟比较。在类似的mac上,线性注意力比移动设备上的softmax注意力要快得多。
EfficientViT
Enhancing Linear Attention with Depthwise Convolution
虽然线性注意在计算复杂度和硬件延迟方面优于softmax注意,但线性注意也有局限性。以往的研究[Luna: Linear unified nested attention,Random feature attention,Combiner: Full attention transformer with sparse computation cost,cosformer: Rethinking softmax in attention]表明,在NLP中线性注意和softmax注意之间通常存在显著的性能差距。对于视觉任务,之前的研究[Visual correspondence hallucination,Quadtree attention for vision transformers]也表明线性注意不如softmax注意。在本文的实验中,本文也有类似的观察结果(图中)。
本文对这一假设提出了质疑,认为线性注意的低劣性能主要是由于局部特征提取能力的丧失。如果没有在softmax注意中使用的非线性评分归一化,线性注意很难像softmax注意那样集中其注意分布。下图(中间)提供了这种差异的示例。
在相同的原始注意力得分下,使用softmax比不使用softmax更能集中注意力。因此,线性注意不能有效地聚焦于局部模式产生的高注意分数(下图),削弱了其局部特征提取能力。
注意图的可视化显示了线性注意的局限性。通过非线性注意归一化,softmax注意可以产生清晰的注意分布,如中间行所示。相比之下,线性注意的分布相对平滑,使得线性注意在捕捉局部细节方面的能力较弱,造成了显著的精度损失。本文通过深度卷积增强线性注意来解决这一限制,并有效提高了准确性。
介绍了一个简单而有效的解决方案来解决这个限制。本文的想法是用卷积增强线性注意,这在局部特征提取中是非常有效的。这样,本文就不需要依赖于线性注意来捕捉局部特征,而可以专注于全局特征提取。具体来说,为了保持线性注意的效率和简单性,本文建议在每个FFN层中插入一个深度卷积,这样计算开销很小,同时极大地提高了线性注意的局部特征提取能力。
Building Block
下图(右)展示了增强线性注意的详细架构,它包括一个线性注意层和一个FFN层,在FFN的中间插入深度卷积。
与之前的方法[Swin transformer,Coatnet]不同,本文在efficientvit中没有使用相对位置偏差。相对位置偏差虽然可以改善模型的性能,但它使模型容易受到分辨率变化[Segformer]的影响。多分辨率训练或新分辨率下的测试在检测和分割中很常见。去除相对位置偏差使高效率vit对输入分辨率更加灵活。
与之前低计算CNNs[Mobilenetv2,mobilenetv3]的设计不同,本文为下采样块添加了额外的下采样快捷方式。每个下采样快捷方式由一个平均池和一个1x1卷积组成。在本文的实验中,这些额外的下采样快捷方式可以稳定训练效率,提高性能。
Macro Architecture
下图说明了efficientvit的宏观体系结构。它由输入 stem 和4级组成。最近的研究[Coatnet,Levit,Early convolutions help transformers see better]表明在早期阶段使用卷积对ViT更好。本文遵循这个设计,在第三阶段开始使用增强的线性注意。
EfficientViT宏观架构。本文从第三阶段开始使用增强的线性注意。P2、P3和P4形成了一个金字塔特征图,用于检测和分割。P4用于分类。
为了突出高效的主干本身,本文对MBConv和FFN使用相同的扩展比e (e = 4)保持超参数简单,对所有深度卷积使用相同的内核大小k(除了输入stem),对所有层使用相同的激活函数(hard swish)。
P2、P3和P4表示阶段2、3和4的输出,形成了特征图的金字塔。本文按照惯例将P2、P3和P4送至检测头。本文使用Yolov8进行检测。为了分割,本文融合了P2和P4。融合特征被馈送到一个轻量级头,包括几个卷积层,遵循Fast-SCNN。为了分类,本文将P4输入到轻量级头部,与MobileNetV3相同。
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 CSwomTramsformer.py
以下是经过简化和注释的核心代码部分,保留了CSWin Transformer的主要结构和功能:
import torch
import torch.nn as nn
import numpy as np
class Mlp(nn.Module):
"""多层感知机(MLP)模块"""
def __init__(self, in_features, hidden_features=None, out_features=None, act_layer=nn.GELU, drop=0.):
super().__init__()
out_features = out_features or in_features # 输出特征数
hidden_features = hidden_features or in_features # 隐藏层特征数
self.fc1 = nn.Linear(in_features, hidden_features) # 第一层线性变换
self.act = act_layer() # 激活函数
self.fc2 = nn.Linear(hidden_features, out_features) # 第二层线性变换
self.drop = nn.Dropout(drop) # Dropout层
def forward(self, x):
"""前向传播"""
x = self.fc1(x)
x = self.act(x)
x = self.drop(x)
x = self.fc2(x)
x = self.drop(x)
return x
class LePEAttention(nn.Module):
"""局部增强位置编码(LePE)注意力模块"""
def __init__(self, dim, resolution, num_heads, split_size=7, attn_drop=0.):
super().__init__()
self.dim = dim # 输入特征维度
self.resolution = resolution # 输入分辨率
self.split_size = split_size # 切分窗口大小
self.num_heads = num_heads # 注意力头数
self.scale = (dim // num_heads) ** -0.5 # 缩放因子
self.get_v = nn.Conv2d(dim, dim, kernel_size=3, stride=1, padding=1, groups=dim) # 用于获取v的卷积层
self.attn_drop = nn.Dropout(attn_drop) # 注意力Dropout层
def im2cswin(self, x):
"""将输入张量转换为窗口格式"""
B, N, C = x.shape
H = W = int(np.sqrt(N)) # 计算高度和宽度
x = x.transpose(-2, -1).contiguous().view(B, C, H, W) # 转换维度
# 切分为窗口
x = img2windows(x, self.split_size, self.split_size)
return x
def forward(self, qkv):
"""前向传播"""
q, k, v = qkv # 拆分q, k, v
q = self.im2cswin(q) # 将q转换为窗口格式
k = self.im2cswin(k) # 将k转换为窗口格式
v = self.get_v(v) # 通过卷积获取v
attn = (q @ k.transpose(-2, -1)) * self.scale # 计算注意力分数
attn = nn.functional.softmax(attn, dim=-1) # 归一化
attn = self.attn_drop(attn) # 应用Dropout
x = attn @ v # 加权求和
return x
class CSWinBlock(nn.Module):
"""CSWin Transformer的基本块"""
def __init__(self, dim, num_heads, split_size=7):
super().__init__()
self.qkv = nn.Linear(dim, dim * 3) # 线性变换以获取q, k, v
self.attn = LePEAttention(dim, resolution=split_size, num_heads=num_heads) # 注意力模块
self.mlp = Mlp(in_features=dim, hidden_features=dim * 4) # MLP模块
def forward(self, x):
"""前向传播"""
qkv = self.qkv(x).reshape(x.shape[0], -1, 3, x.shape[2]).permute(2, 0, 1, 3) # 计算qkv
x = self.attn(qkv) # 通过注意力模块
x = x + self.mlp(x) # 添加MLP的输出
return x
class CSWinTransformer(nn.Module):
"""CSWin Transformer模型"""
def __init__(self, img_size=640, in_chans=3, num_classes=1000):
super().__init__()
self.stage1_conv_embed = nn.Conv2d(in_chans, 96, kernel_size=7, stride=4, padding=3) # 输入卷积层
self.stage1 = nn.ModuleList([CSWinBlock(dim=96, num_heads=12) for _ in range(2)]) # 第一阶段的CSWin块
def forward(self, x):
"""前向传播"""
x = self.stage1_conv_embed(x) # 通过输入卷积层
for blk in self.stage1:
x = blk(x) # 通过每个CSWin块
return x
# 模型实例化与测试
if __name__ == '__main__':
inputs = torch.randn((1, 3, 640, 640)) # 随机输入
model = CSWinTransformer() # 实例化模型
res = model(inputs) # 前向传播
print(res.size()) # 输出结果的尺寸
代码说明:
- Mlp类:实现了一个简单的多层感知机,包含两个线性层和一个激活函数,适用于特征的非线性变换。
- LePEAttention类:实现了局部增强位置编码的注意力机制,主要负责计算注意力分数并对输入进行加权。
- CSWinBlock类:定义了CSWin Transformer的基本块,包含注意力层和MLP层。
- CSWinTransformer类:构建了整个CSWin Transformer模型,包含输入卷积层和多个CSWin块。
该代码是CSWin Transformer的核心部分,保留了模型的主要结构和功能,去除了不必要的细节和冗余部分。
这个文件实现了一个名为CSWin Transformer的深度学习模型,主要用于计算机视觉任务。该模型由微软公司开发,采用了基于变换器(Transformer)的架构,具有多种不同规模的变体(如CSWin_tiny、CSWin_small、CSWin_base和CSWin_large)。文件中包含了多个类和函数,下面对其进行逐一分析。
首先,文件导入了必要的库,包括PyTorch、timm库(用于处理图像和模型)、以及一些用于模型构建的工具函数。文件的开头部分定义了一些常量和导出模型的名称。
接下来,定义了一个名为Mlp的类,它是一个简单的多层感知机(MLP),包含两个线性层和一个激活函数(默认为GELU)。该类的构造函数允许用户指定输入特征、隐藏特征和输出特征的数量,并支持 dropout 操作。
然后,定义了LePEAttention类,这是模型中的一种注意力机制。该类通过将输入图像转换为窗口(即小块)来计算注意力。它的构造函数中定义了输入的维度、分辨率、头数等参数,并实现了将图像转换为窗口的函数im2cswin和计算局部增强位置编码的函数get_lepe。forward方法实现了注意力的计算过程。
CSWinBlock类则是模型的基本构建块,包含了多个注意力层和MLP层。构造函数中定义了输入维度、头数、分辨率等参数,并根据这些参数创建了相应的注意力层和MLP层。forward方法实现了块的前向传播,包括对输入进行归一化、计算注意力、合并结果以及通过MLP进行处理。
img2windows和windows2img是两个辅助函数,用于将图像转换为窗口格式以及将窗口格式的图像转换回原始格式。
Merge_Block类用于在不同阶段之间合并特征图,它通过卷积操作将特征图的维度减半,并进行归一化处理。
CSWinTransformer类是整个模型的核心,包含了多个阶段,每个阶段由多个CSWinBlock和Merge_Block组成。构造函数中定义了输入图像的大小、通道数、类别数、嵌入维度、深度等参数,并根据这些参数构建了模型的各个部分。forward_features方法负责特征提取,返回不同阶段的特征图。
文件中还定义了一些辅助函数,如_conv_filter用于转换权重,update_weight用于更新模型权重,以及不同规模的模型构造函数(如CSWin_tiny、CSWin_small等),这些函数允许用户根据需要创建不同规模的模型,并可选择加载预训练权重。
最后,在主程序部分,创建了不同规模的模型实例,并对随机输入进行前向传播,输出每个模型的特征图大小。这部分代码用于测试模型的构建和前向传播是否正常。
总体来说,这个文件实现了一个灵活且强大的视觉变换器模型,适用于多种计算机视觉任务,具有良好的可扩展性和可配置性。
10.2 kernel_warehouse.py
以下是经过简化并添加详细中文注释的核心代码部分:
import torch
import torch.nn as nn
import torch.nn.functional as F
class Attention(nn.Module):
def __init__(self, in_planes, reduction, num_static_cell, num_local_mixture, norm_layer=nn.BatchNorm1d):
"""
初始化Attention模块
参数:
in_planes: 输入通道数
reduction: 隐藏层的缩减比例
num_static_cell: 静态单元的数量
num_local_mixture: 本地混合的数量
norm_layer: 归一化层类型
"""
super(Attention, self).__init__()
hidden_planes = max(int(in_planes * reduction), 16) # 计算隐藏层通道数
self.kw_planes_per_mixture = num_static_cell + 1 # 每个混合的关键字平面数
self.num_local_mixture = num_local_mixture # 本地混合数量
self.kw_planes = self.kw_planes_per_mixture * num_local_mixture # 总关键字平面数
# 定义网络层
self.avgpool = nn.AdaptiveAvgPool1d(1) # 自适应平均池化
self.fc1 = nn.Linear(in_planes, hidden_planes) # 全连接层1
self.norm1 = norm_layer(hidden_planes) # 归一化层
self.act1 = nn.ReLU(inplace=True) # 激活函数
# 第二个全连接层
self.fc2 = nn.Linear(hidden_planes, self.kw_planes) # 全连接层2
self.temp_bias = torch.zeros([self.kw_planes], requires_grad=False) # 温度偏置
self.temp_value = 0 # 温度值
self._initialize_weights() # 初始化权重
def _initialize_weights(self):
"""初始化网络层的权重"""
for m in self.modules():
if isinstance(m, nn.Linear):
nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') # Kaiming初始化
if m.bias is not None:
nn.init.constant_(m.bias, 0) # 偏置初始化为0
if isinstance(m, nn.BatchNorm1d):
nn.init.constant_(m.weight, 1) # 归一化层权重初始化为1
nn.init.constant_(m.bias, 0) # 偏置初始化为0
def forward(self, x):
"""前向传播"""
x = self.avgpool(x.reshape(*x.shape[:2], -1)).squeeze(dim=-1) # 平均池化
x = self.act1(self.norm1(self.fc1(x))) # 经过全连接层、归一化和激活函数
x = self.fc2(x) # 经过第二个全连接层
x = x / (torch.sum(torch.abs(x), dim=1).view(-1, 1) + 1e-3) # 归一化
x = (1.0 - self.temp_value) * x + self.temp_value * self.temp_bias.to(x.device).view(1, -1) # 温度调整
return x # 返回结果
class KWConv1d(nn.Module):
"""一维卷积层"""
def __init__(self, in_planes, out_planes, kernel_size, stride=1, padding=0, dilation=1, groups=1):
super(KWConv1d, self).__init__()
self.conv = nn.Conv1d(in_planes, out_planes, kernel_size, stride=stride, padding=padding, dilation=dilation, groups=groups)
def forward(self, x):
"""前向传播"""
return self.conv(x) # 直接调用卷积层
class Warehouse_Manager(nn.Module):
"""仓库管理器类,用于管理卷积层的权重"""
def __init__(self):
super(Warehouse_Manager, self).__init__()
self.warehouse_list = {} # 存储仓库信息
def reserve(self, in_planes, out_planes, kernel_size=1, stride=1, padding=0, dilation=1, groups=1):
"""创建一个动态卷积层并记录其信息"""
weight_shape = [out_planes, in_planes, kernel_size] # 权重形状
warehouse_name = 'default' # 默认仓库名称
if warehouse_name not in self.warehouse_list.keys():
self.warehouse_list[warehouse_name] = []
self.warehouse_list[warehouse_name].append(weight_shape) # 记录权重形状
return KWConv1d(in_planes, out_planes, kernel_size, stride, padding, dilation, groups) # 返回卷积层
def store(self):
"""存储权重到仓库"""
for warehouse_name in self.warehouse_list.keys():
warehouse = self.warehouse_list[warehouse_name]
# 这里可以添加权重存储的逻辑
# 示例使用
if __name__ == "__main__":
# 创建一个仓库管理器
wm = Warehouse_Manager()
# 预留一个卷积层
conv_layer = wm.reserve(16, 32, kernel_size=3)
# 生成输入数据
input_data = torch.randn(1, 16, 10) # batch_size=1, channels=16, length=10
# 前向传播
output = conv_layer(input_data)
print(output.shape) # 输出形状
代码说明:
- Attention类:实现了一个注意力机制,包含初始化、权重初始化和前向传播方法。
- KWConv1d类:实现了一维卷积层,封装了PyTorch的
Conv1d
。 - Warehouse_Manager类:管理卷积层的权重,提供了预留和存储权重的方法。
- 示例使用:展示了如何创建仓库管理器并预留一个卷积层,最后进行前向传播。
这个简化版本保留了核心功能并增加了详细的中文注释,以便更好地理解代码的逻辑和用途。
这个程序文件 kernel_warehouse.py
实现了一个基于深度学习的卷积神经网络中的“内核仓库”管理系统。它的主要目的是通过动态管理卷积层的权重来提高模型的效率和灵活性。文件中定义了多个类和函数,以下是对这些内容的详细说明。
首先,文件导入了必要的 PyTorch 库,包括神经网络模块和一些数学函数。接着,定义了一个 parse
函数,用于处理输入参数,确保其格式符合预期。
接下来,定义了一个 Attention
类,该类实现了一个注意力机制。它通过对输入特征进行处理,生成与卷积操作相关的权重。Attention
类的构造函数接受多个参数,包括输入通道数、降维比例、静态单元数量等。它还定义了一些线性层和归一化层,并在 _initialize_weights
方法中初始化这些层的权重。
KWconvNd
类是一个抽象基类,表示多维卷积操作。它的构造函数接受输入和输出通道数、卷积核大小、步幅、填充、扩张等参数,并将这些参数解析为适合的格式。该类还定义了 init_attention
方法,用于初始化注意力机制。
具体的卷积操作由 KWConv1d
、KWConv2d
和 KWConv3d
类实现,分别对应一维、二维和三维卷积。这些类继承自 KWconvNd
,并定义了适合各自维度的卷积函数和维度排列。
KWLinear
类实现了一维线性层,它使用 KWConv1d
来进行卷积操作,并对输入数据进行形状转换。
Warehouse_Manager
类是整个内核仓库管理的核心。它负责管理卷积层的权重,并提供动态分配和存储功能。该类的构造函数接受多个参数,包括降维比例、单元数量比例、共享范围等。它还定义了 reserve
方法,用于创建没有权重的动态卷积层,并记录其信息。
在 store
方法中,Warehouse_Manager
计算每个仓库的权重形状,并为每个卷积层分配权重。allocate
方法则负责将权重分配给网络中的卷积层,并初始化权重。
最后,KWConv
类是一个封装类,用于将卷积操作与批归一化和激活函数结合在一起。它的构造函数接受卷积参数,并在前向传播中依次执行卷积、归一化和激活操作。
此外,文件还定义了一个 get_temperature
函数,用于根据当前的训练迭代和周期计算温度值,这可能与模型的训练策略有关。
总体来说,这个文件实现了一个灵活的卷积层权重管理系统,通过注意力机制和动态权重分配来提高模型的表现和效率。
10.3 FreqFusion.py
以下是提取出的核心代码部分,并附上详细的中文注释:
import torch
import torch.nn as nn
import torch.nn.functional as F
class FreqFusion(nn.Module):
def __init__(self, channels, scale_factor=1, lowpass_kernel=5, highpass_kernel=3, **kwargs):
super().__init__()
hr_channels, lr_channels = channels
self.scale_factor = scale_factor
self.lowpass_kernel = lowpass_kernel
self.highpass_kernel = highpass_kernel
# 压缩高分辨率和低分辨率特征通道
self.compressed_channels = (hr_channels + lr_channels) // 8
self.hr_channel_compressor = nn.Conv2d(hr_channels, self.compressed_channels, 1)
self.lr_channel_compressor = nn.Conv2d(lr_channels, self.compressed_channels, 1)
# 内容编码器,用于生成低通滤波器
self.content_encoder = nn.Conv2d(
self.compressed_channels,
lowpass_kernel ** 2 * self.scale_factor * self.scale_factor,
kernel_size=3,
padding=1
)
# 初始化权重
self.init_weights()
def init_weights(self):
for m in self.modules():
if isinstance(m, nn.Conv2d):
nn.init.xavier_uniform_(m.weight) # 使用Xavier初始化卷积层权重
if m.bias is not None:
nn.init.constant_(m.bias, 0) # 将偏置初始化为0
def kernel_normalizer(self, mask, kernel):
# 对mask进行归一化处理
n, mask_c, h, w = mask.size()
mask_channel = int(mask_c / float(kernel**2))
mask = mask.view(n, mask_channel, -1, h, w)
mask = F.softmax(mask, dim=2) # 对mask进行softmax归一化
mask = mask.view(n, mask_channel, kernel, kernel, h, w)
mask = mask.permute(0, 1, 4, 5, 2, 3).view(n, -1, kernel, kernel)
mask /= mask.sum(dim=(-1, -2), keepdims=True) # 归一化处理
return mask
def forward(self, x):
hr_feat, lr_feat = x # 输入高分辨率和低分辨率特征
compressed_hr_feat = self.hr_channel_compressor(hr_feat) # 压缩高分辨率特征
compressed_lr_feat = self.lr_channel_compressor(lr_feat) # 压缩低分辨率特征
# 生成低通滤波器的mask
mask_lr = self.content_encoder(compressed_hr_feat)
mask_lr = self.kernel_normalizer(mask_lr, self.lowpass_kernel) # 归一化mask
# 对低分辨率特征进行处理
lr_feat = F.interpolate(lr_feat, size=hr_feat.shape[2:], mode='nearest') # 上采样
lr_feat = F.conv2d(lr_feat, mask_lr) # 应用低通滤波器
# 返回融合后的特征
return hr_feat + lr_feat # 将高分辨率特征与处理后的低分辨率特征相加
代码注释说明:
- 导入必要的库:导入PyTorch及其相关模块。
- FreqFusion类:定义了一个频率感知特征融合的神经网络模块。
__init__
方法:初始化网络的参数,包括高分辨率和低分辨率通道数、滤波器大小等,并定义了压缩和编码器的卷积层。init_weights
方法:初始化卷积层的权重和偏置。kernel_normalizer
方法:对生成的mask进行归一化处理,以确保其和为1。forward
方法:定义了前向传播过程,接收高分辨率和低分辨率特征,进行特征压缩、滤波和融合,最终返回融合后的特征。
以上代码是整个频率感知特征融合模块的核心部分,其他部分如辅助函数和类可以根据需要进行扩展和修改。
这个程序文件 FreqFusion.py
实现了一个名为 FreqFusion
的深度学习模块,主要用于密集图像预测任务。该模块的核心思想是通过频率感知的特征融合来提升图像的重建质量,尤其是在高分辨率图像生成的场景中。
首先,文件导入了必要的库,包括 PyTorch 和一些神经网络相关的模块。接着,定义了一些初始化函数,比如 normal_init
和 constant_init
,用于初始化神经网络层的权重和偏置。resize
函数用于调整输入张量的大小,支持不同的插值模式,并且在特定条件下会发出警告。
hamming2D
函数用于生成二维 Hamming 窗口,主要用于后续的卷积操作中,以便在频域上对特征进行加权处理。
FreqFusion
类是整个模块的核心,继承自 nn.Module
。在其构造函数中,定义了多个参数,包括通道数、卷积核大小、上采样因子等。类中包含了多个卷积层,用于对高分辨率和低分辨率特征进行压缩和编码。此外,还定义了高通和低通滤波器的卷积层,以便提取不同频率的特征。
在 init_weights
方法中,使用 Xavier 初始化和正态初始化来初始化网络的权重,以确保网络的收敛性和性能。
kernel_normalizer
方法用于对卷积核进行归一化处理,以确保在后续的卷积操作中保持数值稳定性。
forward
方法是模块的前向传播逻辑,接受高分辨率和低分辨率的特征作为输入。根据不同的配置,可能会使用检查点机制来节省内存。核心的 _forward
方法实现了特征的融合过程,包括对高分辨率和低分辨率特征的处理,使用卷积层生成掩码,并通过 Carafe(一个用于上采样的操作)进行特征的重建。
此外,LocalSimGuidedSampler
类用于生成偏移量,以便在特征重采样时使用。该类实现了一个局部相似性引导的采样器,通过计算高分辨率和低分辨率特征之间的相似性来生成偏移量,从而在重建过程中保持特征的一致性。
最后,compute_similarity
函数用于计算输入张量中每个点与其周围点的余弦相似度,帮助评估特征之间的相似性。
整体来看,这个程序文件实现了一个复杂的图像处理模块,结合了多种卷积操作和特征融合技术,旨在提高图像重建的质量和效率。
10.4 test_selective_scan_easy.py
以下是经过简化和注释的核心代码部分,主要保留了selective_scan_easy
函数及其相关的SelectiveScanEasy
类。代码中添加了详细的中文注释,以帮助理解每个部分的功能和作用。
import torch
import torch.nn.functional as F
def selective_scan_easy(us, dts, As, Bs, Cs, Ds, delta_bias=None, delta_softplus=False, return_last_state=False, chunksize=64):
"""
选择性扫描函数,执行一系列基于输入的状态更新和输出计算。
参数:
us: 输入张量,形状为 (B, G * D, L)
dts: 时间增量张量,形状为 (B, G * D, L)
As: 状态转移矩阵,形状为 (G * D, N)
Bs: 变换矩阵,形状为 (B, G, N, L)
Cs: 输出矩阵,形状为 (B, G, N, L)
Ds: 额外的输入张量,形状为 (G * D)
delta_bias: 可选的偏置项,形状为 (G * D)
delta_softplus: 是否对dts应用softplus变换
return_last_state: 是否返回最后的状态
chunksize: 处理的块大小
返回:
输出张量和(可选的)最后状态
"""
def selective_scan_chunk(us, dts, As, Bs, Cs, hprefix):
"""
处理每个块的选择性扫描计算。
参数:
us: 当前块的输入张量
dts: 当前块的时间增量张量
As: 状态转移矩阵
Bs: 变换矩阵
Cs: 输出矩阵
hprefix: 前一个状态的输出
返回:
ys: 当前块的输出
hs: 当前块的状态
"""
ts = dts.cumsum(dim=0) # 计算时间增量的累积和
Ats = torch.einsum("gdn,lbgd->lbgdn", As, ts).exp() # 计算状态转移矩阵的指数
rAts = Ats # 归一化
duts = dts * us # 计算输入与时间增量的乘积
dtBus = torch.einsum("lbgd,lbgn->lbgdn", duts, Bs) # 计算变换矩阵的乘积
hs_tmp = rAts * (dtBus / rAts).cumsum(dim=0) # 更新状态
hs = hs_tmp + Ats * hprefix.unsqueeze(0) # 加上前一个状态
ys = torch.einsum("lbgn,lbgdn->lbgd", Cs, hs) # 计算输出
return ys, hs
# 初始化
dtype = torch.float32
inp_dtype = us.dtype
has_D = Ds is not None
if chunksize < 1:
chunksize = Bs.shape[-1]
dts = dts.to(dtype) # 转换数据类型
if delta_bias is not None:
dts = dts + delta_bias.view(1, -1, 1).to(dtype) # 添加偏置
if delta_softplus:
dts = F.softplus(dts) # 应用softplus变换
# 处理输入的维度
Bs = Bs.unsqueeze(1) if len(Bs.shape) == 3 else Bs
Cs = Cs.unsqueeze(1) if len(Cs.shape) == 3 else Cs
B, G, N, L = Bs.shape
us = us.view(B, G, -1, L).permute(3, 0, 1, 2).to(dtype)
dts = dts.view(B, G, -1, L).permute(3, 0, 1, 2).to(dtype)
As = As.view(G, -1, N).to(dtype)
Bs = Bs.permute(3, 0, 1, 2).to(dtype)
Cs = Cs.permute(3, 0, 1, 2).to(dtype)
Ds = Ds.view(G, -1).to(dtype) if has_D else None
oys = [] # 输出列表
hprefix = us.new_zeros((B, G, D, N), dtype=dtype) # 初始化前一个状态
for i in range(0, L, chunksize):
ys, hs = selective_scan_chunk(
us[i:i + chunksize], dts[i:i + chunksize],
As, Bs[i:i + chunksize], Cs[i:i + chunksize], hprefix
)
oys.append(ys) # 添加当前块的输出
hprefix = hs[-1] # 更新前一个状态
oys = torch.cat(oys, dim=0) # 合并所有块的输出
if has_D:
oys = oys + Ds * us # 添加额外输入
oys = oys.permute(1, 2, 3, 0).view(B, -1, L) # 调整输出形状
return oys.to(inp_dtype) if not return_last_state else (oys.to(inp_dtype), hprefix.view(B, G * D, N).float())
class SelectiveScanEasy(torch.autograd.Function):
"""
自定义的PyTorch自动求导函数,用于选择性扫描。
"""
@staticmethod
@torch.cuda.amp.custom_fwd(cast_inputs=torch.float32)
def forward(ctx, us, dts, As, Bs, Cs, Ds, delta_bias=None, delta_softplus=False, return_last_state=False, chunksize=64):
# 调用选择性扫描主函数
return selective_scan_easy(us, dts, As, Bs, Cs, Ds, delta_bias, delta_softplus, return_last_state, chunksize)
@staticmethod
@torch.cuda.amp.custom_bwd
def backward(ctx, doys: torch.Tensor, *args):
# 反向传播的实现
pass # 这里可以实现反向传播的逻辑
# 选择性扫描的外部接口
def selective_scan_easy_fwdbwd(u, delta, A, B, C, D, delta_bias=None, delta_softplus=None,
return_last_state=False, chunksize=64):
return SelectiveScanEasy.apply(u, delta, A, B, C, D, delta_bias, delta_softplus, return_last_state, chunksize)
主要改动和注释说明:
- 函数和类的定义:保留了
selective_scan_easy
函数和SelectiveScanEasy
类的核心结构。 - 详细注释:为每个函数和重要步骤添加了中文注释,解释其输入、输出和主要逻辑。
- 简化的反向传播:反向传播部分的实现留空,实际应用中可以根据需要进行实现。
- 代码结构:保持了原有的代码结构和逻辑,确保可读性和可维护性。
这个程序文件 test_selective_scan_easy.py
主要实现了一个选择性扫描(Selective Scan)算法,并提供了相关的测试功能。程序使用了 PyTorch 库来进行张量计算和自动求导,涉及到的主要功能包括前向传播和反向传播的实现,以及一系列的测试用例。
首先,程序定义了一个名为 selective_scan_easy
的函数,它的输入包括多个张量(如 us
, dts
, As
, Bs
, Cs
, Ds
等),这些张量代表了算法中所需的不同参数。函数内部实现了选择性扫描的逻辑,使用了分块处理的方式(通过 chunksize
参数控制),以便在处理长序列时节省内存。
在 selective_scan_easy
函数中,首先定义了一个内部函数 selective_scan_chunk
,用于处理每个块的计算。该函数实现了选择性扫描的核心逻辑,包括对输入张量的逐步累加和变换。函数内部使用了 torch.einsum
来高效地进行张量运算。
接下来,程序定义了一个 SelectiveScanEasy
类,继承自 torch.autograd.Function
,用于实现自定义的前向和反向传播。类中的 forward
方法调用了 selective_scan_easy
函数,并保存必要的中间结果以供反向传播使用。backward
方法则实现了反向传播的逻辑,计算各个输入张量的梯度。
此外,程序还定义了多个版本的选择性扫描函数(如 selective_scan_easyv2
, selective_scan_easyv3
等),这些版本可能在实现细节上有所不同,旨在优化性能或适应不同的使用场景。
在文件的最后部分,使用 pytest
框架定义了一系列的测试用例,确保选择性扫描算法的正确性和性能。测试用例使用了不同的输入参数组合,验证了前向传播和反向传播的输出是否与参考实现一致。
总的来说,这个程序文件实现了一个高效的选择性扫描算法,并通过测试确保其正确性,适用于需要处理长序列数据的深度学习任务。
注意:由于此博客编辑较早,上面“10.YOLOv11核心改进源码讲解”中部分代码可能会优化升级,仅供参考学习,以“11.完整训练+Web前端界面+200+种全套创新点源码、数据集获取(由于版权原因,本博客仅提供【原始博客的链接】,原始博客提供下载链接)”的内容为准。
11.完整训练+Web前端界面+200+种全套创新点源码、数据集获取(由于版权原因,本博客仅提供【原始博客的链接】,原始博客提供下载链接)
参考原始博客1: https://gitee.com/Vision-Studios/NEW_PALLET97
参考原始博客2: https://github.com/Qunmasj-Vision-Studio/NEW_PALLET97