秋招面试专栏推荐 :深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转
💡💡💡本专栏所有程序均经过测试,可成功执行💡💡💡
专栏目录: 《YOLOv5入门 + 改进涨点》专栏介绍 & 专栏目录 |目前已有50+篇内容,内含各种Head检测头、损失函数Loss、Backbone、Neck、NMS等创新点改进
深度卷积神经网络(CNNs)在单图像超分辨率(SISR)方面得到了广泛的研究,并取得了显著的性能。然而,现有的基于CNN的SISR方法主要关注更宽或更深的架构设计,忽视了探索中间层的特征相关性,从而阻碍了CNN的表现力。为了解决这一问题,提出了一种第二阶注意力网络(SAN),用于更强大的特征表达和特征相关性学习。具体来说,开发了一个新颖的可训练的第二阶通道注意力(SOCA)模块,该模块通过使用第二阶特征统计自适应地重新缩放通道特征,以获得更具区分性的表示。此外,我们提出了一种非局部增强的残差组(NLRG)结构,该结构不仅融入了非局部操作以捕获远距离的空间上下文信息,而且还包含了重复的局部源残差注意力组(LSRAG),以学习更抽象的特征表示。文章在介绍主要的原理后,将手把手教学如何进行模块的代码添加和修改,并将修改后的完整代码放在文章的最后,方便大家一键运行,小白也可轻松上手实践。以帮助您更好地学习深度学习目标检测YOLO系列的挑战。
专栏地址:YOLOv5改进+入门——持续更新各种有效涨点方法——点击即可跳转 订阅专栏学习不迷路
1.原理
论文地址:Second-order Attention Network for Single Image Super-Resolution——点击即可跳转
官方代码: 官方代码仓库——点击即可跳转
SOCA(Second-order Channel Attention,二阶通道注意力)的主要原理涉及利用二阶特征统计来增强深度卷积神经网络(CNN)中通道之间的相关性,从而提升单幅图像超分辨率(SISR)的性能。以下是SOCA的主要原理和机制:
- 二阶特征统计
传统的通道注意力机制主要利用一阶特征统计(如全局平均池化)来重新调整通道特征的权重。然而,SOCA利用二阶特征统计信息,即通过计算特征的协方差矩阵,来捕捉更丰富的特征相关性。这样可以更好地识别并增强有用的特征,从而提升网络的判别能力。
- 结构设计
SOCA模块嵌入在深度卷积网络中,通过以下步骤实现其功能:
特征提取:从输入图像中提取初始的浅层特征。
深度特征提取:通过非局部增强残差组(NLRG)结构进一步提取深层特征。NLRG包含非局部操作和局部源残差注意力组(LSRAG),前者用于捕捉长距离的空间上下文信息,后者通过堆叠简化的残差块并结合SOCA模块进行特征学习。
特征重缩放:在每个LSRAG的末尾,通过SOCA模块对通道特征进行自适应的重缩放。具体来说,通过计算特征的二阶统计信息(协方差矩阵),并进行归一化处理,然后利用这些统计信息生成通道注意力权重,从而对各通道特征进行重新加权。
训练和优化
SOCA模块在网络训练过程中通过反向传播算法进行优化。使用L1损失函数来衡量超分辨率结果与高分辨率真实图像之间的差异,从而引导网络学习最优的特征表示。
- 实验验证
实验结果表明,加入SOCA模块的深度卷积网络(SAN,Second-order Attention Network)在多项公共数据集上的表现优于现有的最先进的单图像超分辨率方法,无论是定量指标还是视觉质量都有显著提升。
总结来说,SOCA通过引入二阶特征统计信息,显著增强了通道注意力机制的判别能力,从而有效提升了单幅图像超分辨率的性能。
2. 将SOCA加入YOLOv5中
2.1 SOCA的代码实现
关键步骤一:将下面代码添加到 yolov5/models/common.py中
import numpy as np
import torch
from torch import nn
from torch.nn import init
from torch.autograd import Function
class Covpool(Function):
@staticmethod
def forward(ctx, input):
x = input
batchSize = x.data.shape[0]
dim = x.data.shape[1]
h = x.data.shape[2]
w = x.data.shape[3]
M = h*w
x = x.reshape(batchSize,dim,M)
I_hat = (-1./M/M)*torch.ones(M,M,device = x.device) + (1./M)*torch.eye(M,M,device = x.device)
I_hat = I_hat.view(1,M,M).repeat(batchSize,1,1).type(x.dtype)
y = x.bmm(I_hat).bmm(x.transpose(1,2))
ctx.save_for_backward(input,I_hat)
return y
@staticmethod
def backward(ctx, grad_output):
input,I_hat = ctx.saved_tensors
x = input
batchSize = x.data.shape[0]
dim = x.data.shape[1]
h = x.data.shape[2]
w = x.data.shape[3]
M = h*w
x = x.reshape(batchSize,dim,M)
grad_input = grad_output + grad_output.transpose(1,2)
grad_input = grad_input.bmm(x).bmm(I_hat)
grad_input = grad_input.reshape(batchSize,dim,h,w)
return grad_input
class Sqrtm(Function):
@staticmethod
def forward(ctx, input, iterN):
x = input
batchSize = x.data.shape[0]
dim = x.data.shape[1]
dtype = x.dtype
I3 = 3.0*torch.eye(dim,dim,device = x.device).view(1, dim, dim).repeat(batchSize,1,1).type(dtype)
normA = (1.0/3.0)*x.mul(I3).sum(dim=1).sum(dim=1)
A = x.div(normA.view(batchSize,1,1).expand_as(x))
Y = torch.zeros(batchSize, iterN, dim, dim, requires_grad = False, device = x.device)
Z = torch.eye(dim,dim,device = x.device).view(1,dim,dim).repeat(batchSize,iterN,1,1)
if iterN < 2:
ZY = 0.5*(I3 - A)
Y[:,0,:,:] = A.bmm(ZY)
else:
ZY = 0.5*(I3 - A)
Y[:,0,:,:] = A.bmm(ZY)
Z[:,0,:,:] = ZY
for i in range(1, iterN-1):
ZY = 0.5*(I3 - Z[:,i-1,:,:].bmm(Y[:,i-1,:,:]))
Y[:,i,:,:] = Y[:,i-1,:,:].bmm(ZY)
Z[:,i,:,:] = ZY.bmm(Z[:,i-1,:,:])
ZY = 0.5*Y[:,iterN-2,:,:].bmm(I3 - Z[:,iterN-2,:,:].bmm(Y[:,iterN-2,:,:]))
y = ZY*torch.sqrt(normA).view(batchSize, 1, 1).expand_as(x)
ctx.save_for_backward(input, A, ZY, normA, Y, Z)
ctx.iterN = iterN
return y
@staticmethod
def backward(ctx, grad_output):
input, A, ZY, normA, Y, Z = ctx.saved_tensors
iterN = ctx.iterN
x = input
batchSize = x.data.shape[0]
dim = x.data.shape[1]
dtype = x.dtype
der_postCom = grad_output*torch.sqrt(normA).view(batchSize, 1, 1).expand_as(x)
der_postComAux = (grad_output*ZY).sum(dim=1).sum(dim=1).div(2*torch.sqrt(normA))
I3 = 3.0*torch.eye(dim,dim,device = x.device).view(1, dim, dim).repeat(batchSize,1,1).type(dtype)
if iterN < 2:
der_NSiter = 0.5*(der_postCom.bmm(I3 - A) - A.bmm(der_sacleTrace))
else:
dldY = 0.5*(der_postCom.bmm(I3 - Y[:,iterN-2,:,:].bmm(Z[:,iterN-2,:,:])) -
Z[:,iterN-2,:,:].bmm(Y[:,iterN-2,:,:]).bmm(der_postCom))
dldZ = -0.5*Y[:,iterN-2,:,:].bmm(der_postCom).bmm(Y[:,iterN-2,:,:])
for i in range(iterN-3, -1, -1):
YZ = I3 - Y[:,i,:,:].bmm(Z[:,i,:,:])
ZY = Z[:,i,:,:].bmm(Y[:,i,:,:])
dldY_ = 0.5*(dldY.bmm(YZ) -
Z[:,i,:,:].bmm(dldZ).bmm(Z[:,i,:,:]) -
ZY.bmm(dldY))
dldZ_ = 0.5*(YZ.bmm(dldZ) -
Y[:,i,:,:].bmm(dldY).bmm(Y[:,i,:,:]) -
dldZ.bmm(ZY))
dldY = dldY_
dldZ = dldZ_
der_NSiter = 0.5*(dldY.bmm(I3 - A) - dldZ - A.bmm(dldY))
grad_input = der_NSiter.div(normA.view(batchSize,1,1).expand_as(x))
grad_aux = der_NSiter.mul(x).sum(dim=1).sum(dim=1)
for i in range(batchSize):
grad_input[i,:,:] += (der_postComAux[i] \
- grad_aux[i] / (normA[i] * normA[i])) \
*torch.ones(dim,device = x.device).diag()
return grad_input, None
def CovpoolLayer(var):
return Covpool.apply(var)
def SqrtmLayer(var, iterN):
return Sqrtm.apply(var, iterN)
class SOCA(nn.Module):
# second-order Channel attention
def __init__(self, channel, reduction=8):
super(SOCA, self).__init__()
self.max_pool = nn.MaxPool2d(kernel_size=2)
self.conv_du = nn.Sequential(
nn.Conv2d(channel, channel // reduction, 1, padding=0, bias=True),
nn.ReLU(inplace=True),
nn.Conv2d(channel // reduction, channel, 1, padding=0, bias=True),
nn.Sigmoid()
)
def forward(self, x):
batch_size, C, h, w = x.shape # x: NxCxHxW
N = int(h * w)
min_h = min(h, w)
h1 = 1000
w1 = 1000
if h < h1 and w < w1:
x_sub = x
elif h < h1 and w > w1:
W = (w - w1) // 2
x_sub = x[:, :, :, W:(W + w1)]
elif w < w1 and h > h1:
H = (h - h1) // 2
x_sub = x[:, :, H:H + h1, :]
else:
H = (h - h1) // 2
W = (w - w1) // 2
x_sub = x[:, :, H:(H + h1), W:(W + w1)]
cov_mat = CovpoolLayer(x_sub) # Global Covariance pooling layer
cov_mat_sqrt = SqrtmLayer(cov_mat,5) # Matrix square root layer( including pre-norm,Newton-Schulz iter. and post-com. with 5 iteration)
cov_mat_sum = torch.mean(cov_mat_sqrt,1)
cov_mat_sum = cov_mat_sum.view(batch_size,C,1,1)
y_cov = self.conv_du(cov_mat_sum)
return y_cov*x
SOCA(Second-order Channel Attention,二阶通道注意力)模块在单幅图像超分辨率(SISR)中的应用主要通过一个深度卷积神经网络(即SAN,Second-order Attention Network)实现。以下是它处理图像的主要流程:
- 输入与浅层特征提取
首先,低分辨率(LR)图像被输入到网络中,通过一个卷积层提取初始的浅层特征。这一过程可以表示为: 其中,是输入的低分辨率图像, 是卷积操作, 是提取的浅层特征。
- 深度特征提取
浅层特征 进入深度特征提取模块,该模块由多个非局部增强残差组(NLRG)组成。NLRG结构包括以下两个主要部分:
- 非局部模块(RL-NL):捕捉图像中长距离的空间上下文信息。
- 局部源残差注意力组(LSRAG):通过堆叠简化的残差块,并在每个残差块的末尾加入SOCA模块来进行特征学习。
每个NLRG的输出深度特征可以表示为: 其中,表示NLRG结构。
- 上采样
从NLRG中提取的深度特征 )经过上采样模块,将低分辨率特征映射到高分辨率空间。常用的上采样方法包括转置卷积和亚像素卷积等。上采样模块的输出表示为: 其中, 表示上采样操作, 是上采样后的特征。
- 重建高分辨率图像
上采样后的特征通过一个卷积层映射到最终的高分辨率HR图像: 其中, HR表示重建卷积层, ISR是生成的高分辨率图像。
- SOCA模块的作用
在上述流程的深度特征提取部分,每个LSRAG的末尾都嵌入了SOCA模块。SOCA模块的主要步骤如下:
- 计算二阶特征统计:对输入特征计算协方差矩阵,捕捉通道间的相关性。
- 归一化与重缩放:对协方差矩阵进行归一化处理,并生成通道注意力权重。
- 自适应调整特征:利用注意力权重对通道特征进行自适应重缩放,以增强有用特征。
总结
SOCA通过在深度特征提取过程中利用二阶特征统计,显著提升了通道注意力机制的有效性。整个处理流程包括输入图像的浅层特征提取、深度特征提取(包括NLRG和LSRAG模块)、上采样和最终的高分辨率图像重建。这一机制在图像超分辨率任务中展现了优越的性能。
2.2 新增yaml文件
关键步骤二:在下/yolov5-6.1/models下新建文件 yolov5_SOCA.yaml并将下面代码复制进去
# YOLOv5 🚀 by YOLOAir, GPL-3.0 license
# Parameters
nc: 80 # number of classes
depth_multiple: 1.0 # model depth multiple
width_multiple: 1.0 # layer channel multiple
anchors:
- [10,13, 16,30, 33,23] # P3/8
- [30,61, 62,45, 59,119] # P4/16
- [116,90, 156,198, 373,326] # P5/32
# YOLOv5 v6.0 backbone
backbone:
# [from, number, module, args]
[[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
[-1, 3, C3, [128]],
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
[-1, 6, C3, [256]],
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
[-1, 9, C3, [512]],
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
[-1, 3, C3, [1024]],
[-1, 1, SPPF, [1024, 5]], # 9
]
# YOLOv5 v6.0 head
head:
[[-1, 1, Conv, [512, 1, 1]],
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
[[-1, 6], 1, Concat, [1]], # cat backbone P4
[-1, 3, C3, [512, False]], # 13
[-1, 1, Conv, [256, 1, 1]],
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
[[-1, 4], 1, Concat, [1]], # cat backbone P3
[-1, 3, C3, [256, False]], # 17 (P3/8-small)
[-1, 1, Conv, [256, 3, 2]],
[[-1, 14], 1, Concat, [1]], # cat head P4
[-1, 3, C3, [512, False]], # 20 (P4/16-medium)
[-1, 1, Conv, [512, 3, 2]],
[[-1, 10], 1, Concat, [1]], # cat head P5
[-1, 3, C3, [1024, False]], # 23 (P5/32-large)
[-1, 1, SOCA, [1024]],
[[17, 20, 24], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
]
温馨提示:本文只是对yolov5基础上添加模块,如果要对yolov5n/l/m/x进行添加则只需要指定对应的depth_multiple 和 width_multiple。
# YOLOv5n
depth_multiple: 0.33 # model depth multiple
width_multiple: 0.25 # layer channel multiple
# YOLOv5s
depth_multiple: 0.33 # model depth multiple
width_multiple: 0.50 # layer channel multiple
# YOLOv5l
depth_multiple: 1.0 # model depth multiple
width_multiple: 1.0 # layer channel multiple
# YOLOv5m
depth_multiple: 0.67 # model depth multiple
width_multiple: 0.75 # layer channel multiple
# YOLOv5x
depth_multiple: 1.33 # model depth multiple
width_multiple: 1.25 # layer channel multiple
2.3 注册模块
关键步骤三:在yolo.py的parse_model函数中注册 添加“SOCA",
2.4 执行程序
在train.py中,将cfg的参数路径设置为yolov5_SOCA.yaml的路径
建议大家写绝对路径,确保一定能找到
🚀运行程序,如果出现下面的内容则说明添加成功🚀
from n params module arguments
0 -1 1 3520 models.common.Conv [3, 32, 6, 2, 2]
1 -1 1 18560 models.common.Conv [32, 64, 3, 2]
2 -1 1 18816 models.common.C3 [64, 64, 1]
3 -1 1 73984 models.common.Conv [64, 128, 3, 2]
4 -1 2 115712 models.common.C3 [128, 128, 2]
5 -1 1 295424 models.common.Conv [128, 256, 3, 2]
6 -1 3 625152 models.common.C3 [256, 256, 3]
7 -1 1 1180672 models.common.Conv [256, 512, 3, 2]
8 -1 1 1182720 models.common.C3 [512, 512, 1]
9 -1 1 656896 models.common.SPPF [512, 512, 5]
10 -1 1 131584 models.common.Conv [512, 256, 1, 1]
11 -1 1 0 torch.nn.modules.upsampling.Upsample [None, 2, 'nearest']
12 [-1, 6] 1 0 models.common.Concat [1]
13 -1 1 361984 models.common.C3 [512, 256, 1, False]
14 -1 1 33024 models.common.Conv [256, 128, 1, 1]
15 -1 1 0 torch.nn.modules.upsampling.Upsample [None, 2, 'nearest']
16 [-1, 4] 1 0 models.common.Concat [1]
17 -1 1 90880 models.common.C3 [256, 128, 1, False]
18 -1 1 147712 models.common.Conv [128, 128, 3, 2]
19 [-1, 14] 1 0 models.common.Concat [1]
20 -1 1 296448 models.common.C3 [256, 256, 1, False]
21 -1 1 590336 models.common.Conv [256, 256, 3, 2]
22 [-1, 10] 1 0 models.common.Concat [1]
23 -1 1 1182720 models.common.C3 [512, 512, 1, False]
24 -1 1 1537 models.common.SOCA [512, 512]
25 [17, 20, 24] 1 229245 models.yolo.Detect [80, [[10, 13, 16, 30, 33, 23], [30, 61, 62, 45, 59, 119], [116, 90, 156, 198, 373, 326]], [128, 256, 512]]
Model Summary: 277 layers, 7236926 parameters, 7236926 gradients
3. 完整代码分享
https://pan.baidu.com/s/1JtnUUhuAgIwGV5lg1IweiQ?pwd=cwwm
提取码: cwwm
4. GFLOPs
关于GFLOPs的计算方式可以查看:百面算法工程师 | 卷积基础知识——Convolution
未改进的GFLOPs
改进后的GFLOPs
现在手上没有卡了,等过段时候有卡了把这补上,需要的同学自己测一下
5. 进阶
可以结合损失函数或者卷积模块进行多重改进
YOLOv5改进 | 损失函数 | EIoU、SIoU、WIoU、DIoU、FocuSIoU等多种损失函数——点击即可跳转
6. 总结
SOCA(Second-order Channel Attention,二阶通道注意力)通过引入二阶特征统计信息来增强通道之间的相关性,从而提升深度卷积神经网络的特征表示能力。具体而言,SOCA模块首先对输入特征计算协方差矩阵,以捕捉通道间的相关性,然后对协方差矩阵进行归一化处理,生成通道注意力权重。利用这些权重对特征进行自适应重缩放,增强有用特征,从而提高网络的判别能力和最终性能。在单幅图像超分辨率任务中,SOCA模块嵌入在网络的深度特征提取阶段,通过多层次的特征学习和特征重缩放,实现对低分辨率图像的有效处理和高分辨率图像的精确重建。
版权归原作者 kay_545 所有, 如有侵权,请联系我们删除。