YOLO目标检测创新改进与实战案例专栏
专栏目录: YOLO有效改进系列及项目实战目录 包含卷积,主干 注意力,检测头等创新机制 以及 各种目标检测分割项目实战案例
专栏链接: YOLO基础解析+创新改进+实战案例
摘要
卷积和循环神经网络中的操作都是一次处理一个局部邻域,在这篇文章中,作者提出了一个非局部的操作来作为捕获远程依赖的通用模块。
受计算机视觉中经典的非局部均值方法启发,我们的非局部操作计算某一位置的响应为所有位置特征的加权和。而且,这个模块可以插入到许多计算机视觉网络架构中去。
创新点
- 提出的non-local operations通过计算任意两个位置之间的交互直接捕捉远程依赖,而不用局限于相邻点,其相当于构造了一个和特征图谱尺寸一样大的卷积核, 从而可以维持更多信息。
- non-local可以作为一个组件,和其它网络结构结合,经过作者实验,证明了其可以应用于图像分类、目标检测、目标分割、姿态识别等视觉任务中,并且效果不错。
- Non-local在视频分类上效果很好,倾向于使用在视频分类这个领域中。
1. Non-local自注意力模型
Non-Local是由王小龙等人在2018年的计算机视觉与模式识别会议(CVPR 2018)提出的一种自注意力模型。该模型的灵感来源于非局部均值去噪滤波(Non-Local Means),它不同于传统的基于小区域(如3×3卷积核)的滤波方法。Non-Local操作通过在更大的搜索范围内进行加权,从而捕捉更广泛的上下文信息。更多细节可以参考这篇博客。
在Non-Local神经网络(NN)中,'Local'指的是与卷积神经网络中的感受野相关的概念。传统卷积层的感受野通常有限(如3×3或5×5),而Non-Local模块允许感受野覆盖整个输入空间,从而实现全局信息的整合。
Non-Local模块与其他注意力机制模块(如CBAM、SE、BAM、SK)相似,都是可插拔的组件,用于对特征图进行信息细化(refinement)。它是一种有效的注意力机制实现,不过其理论基础更为丰富,可能会相对复杂和难以理解。
Non-local的通用公式:
$x$ 是输入信号,通常是特征图(feature map)。
$i$ 表示输出位置的索引,响应值是通过对所有可能的$j$位置加权求和得到的。
$f$ 是一个函数,用于计算位置$i$和$j$之间的相似度。
$g$ 是一个函数,用于在位置$j$计算特征图的表示。
$y$ 是经过响应因子$C(x)$标准化处理后得到的输出。
yolov8 引入
class NLBlockND(nn.Module):
def __init__(self, in_channels, inter_channels=None, mode='embedded',
dimension=2, bn_layer=True):
"""
非局部(Non-Local)模块的实现,包含四种不同的成对函数,但不包括子采样技巧。
参数:
in_channels: 输入通道数(论文中为1024)
inter_channels: 模块内部的通道数,如果未指定,则减半(论文中为512)
mode: 支持高斯、嵌入式高斯、点积和连接四种模式
dimension: 可以是1(时间维度),2(空间维度),3(时空维度)
bn_layer: 是否添加批量归一化层
"""
super(NLBlockND, self).__init__()
assert dimension in [1, 2, 3]
if mode not in ['gaussian', 'embedded', 'dot', 'concatenate']:
raise ValueError('`mode` 必须是 `gaussian`, `embedded`, `dot` 或 `concatenate` 中的一个')
self.mode = mode
self.dimension = dimension
self.in_channels = in_channels
self.inter_channels = inter_channels
# 在模块内部,通道数减半
if self.inter_channels is None:
self.inter_channels = in_channels // 2
if self.inter_channels == 0:
self.inter_channels = 1
# 根据不同的维度分配适当的卷积、最大池化和批量归一化层
if dimension == 3:
conv_nd = nn.Conv3d
max_pool_layer = nn.MaxPool3d(kernel_size=(1, 2, 2))
bn = nn.BatchNorm3d
elif dimension == 2:
conv_nd = nn.Conv2d
max_pool_layer = nn.MaxPool2d(kernel_size=(2, 2))
bn = nn.BatchNorm2d
else:
conv_nd = nn.Conv1d
max_pool_layer = nn.MaxPool1d(kernel_size=(2))
bn = nn.BatchNorm1d
# 论文中的函数g,通过1x1的卷积核进行卷积
self.g = conv_nd(in_channels=self.in_channels, out_channels=self.inter_channels, kernel_size=1)
# 在最后一个卷积层后添加BatchNorm层
if bn_layer:
self.W_z = nn.Sequential(
conv_nd(in_channels=self.inter_channels, out_channels=self.in_channels, kernel_size=1),
bn(self.in_channels)
)
# 根据论文第4.1节,初始化BN参数以确保非局部模块的初始状态是恒等映射
nn.init.constant_(self.W_z[1].weight, 0)
nn.init.constant_(self.W_z[1].bias, 0)
else:
self.W_z = conv_nd(in_channels=self.inter_channels, out_channels=self.in_channels, kernel_size=1)
# 根据论文第3.3节,通过将Wz初始化为0,这个模块可以被插入到任何现有的架构中
nn.init.constant_(self.W_z.weight, 0)
nn.init.constant_(self.W_z.bias, 0)
# 为高斯以外的所有操作定义theta和phi
if self.mode in ["embedded", "dot", "concatenate"]:
self.theta = conv_nd(in_channels=self.in_channels, out_channels=self.inter_channels, kernel_size=1)
self.phi = conv_nd(in_channels=self.in_channels, out_channels=self.inter_channels, kernel_size=1)
if self.mode == "concatenate":
self.W_f = nn.Sequential(
nn.Conv2d(in_channels=self.inter_channels * 2, out_channels=1, kernel_size=1),
nn.ReLU()
)
task与yaml配置
详见:https://blog.csdn.net/shangyanaf/article/details/139105131