秋招面试专栏推荐 :深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转
💡💡💡本专栏所有程序均经过测试,可成功执行💡💡💡
专栏目录 :《YOLOv8改进有效涨点》专栏介绍 & 专栏目录 | 目前已有100+篇内容,内含各种Head检测头、损失函数Loss、Backbone、Neck、NMS等创新点改进——点击即可跳转
本文介绍了多边形核初始网络(PKINet)应对遥感图像目标检测的挑战,通过无扩张的多尺度卷积核提取目标特征,并结合上下文锚点注意力(CAA)模块捕获长距离上下文,有效提升了在多个遥感检测基准数据集上的性能。文章在介绍主要的原理后,将手把手教学如何进行模块的代码添加和修改,并将修改后的完整代码放在文章的最后,方便大家一键运行,小白也可轻松上手实践。以帮助您更好地学习深度学习目标检测YOLO系列的挑战。
目录
1. 原理
论文地址:Poly Kernel Inception Network for Remote Sensing Detection——点击即可跳转
官方代码:官方代码仓库——点击即可跳转
PKINet,即多核初始网络,专为遥感物体检测而设计,可解决物体尺度变化大和背景多样化等挑战。PKINet 背后的关键思想是:
1. 多尺度卷积:PKINet 使用初始式深度卷积和不同大小的内核来捕获不同尺度上的物体特征,而无需依赖扩张。这种方法可确保密集的特征提取,同时最大限度地减少由大核卷积引起的问题,例如背景噪声。
2. 上下文锚点注意 (CAA):为了满足对远程上下文信息的需求,PKINet 采用了利用全局平均池化和 1D 条带卷积的 CAA 模块。这有助于捕获远距离像素之间的关系并增强中心区域特征,从而提高对较大物体和上下文信息的理解。
3. 跨阶段部分 (CSP) 结构:网络分为多个阶段,其中输入分为两条路径:一条通过前馈网络 (FFN) 处理,另一条通过 PKI 块处理,后者包括多尺度卷积和 CAA 模块。此结构可在局部和全局上下文中实现高效的特征提取。
总体而言,PKINet 通过比以前的方法更有效地解决尺度变化和上下文挑战,在遥感基准上取得了更好的性能。
2. 将C2f_PKIModule添加到yolov8网络中
2.1 C2f_PKIModule代码实现
关键步骤一: 将下面代码粘贴到在/ultralytics/ultralytics/nn/modules/block.py中,并在该文件的__all__中添加“C2f_PKIModule”
from ultralytics.utils.torch_utils import make_divisible
class PKIModule_CAA(nn.Module):
def __init__(self, ch, h_kernel_size = 11, v_kernel_size = 11) -> None:
super().__init__()
self.avg_pool = nn.AvgPool2d(7, 1, 3)
self.conv1 = Conv(ch, ch)
self.h_conv = nn.Conv2d(ch, ch, (1, h_kernel_size), 1, (0, h_kernel_size // 2), 1, ch)
self.v_conv = nn.Conv2d(ch, ch, (v_kernel_size, 1), 1, (v_kernel_size // 2, 0), 1, ch)
self.conv2 = Conv(ch, ch)
self.act = nn.Sigmoid()
def forward(self, x):
attn_factor = self.act(self.conv2(self.v_conv(self.h_conv(self.conv1(self.avg_pool(x))))))
return attn_factor
class PKIModule(nn.Module):
def __init__(self, inc, ouc, kernel_sizes=(3, 5, 7, 9, 11), expansion=1.0, with_caa=True, caa_kernel_size=11, add_identity=True) -> None:
super().__init__()
hidc = make_divisible(int(ouc * expansion), 8)
self.pre_conv = Conv(inc, hidc)
self.dw_conv = nn.ModuleList(nn.Conv2d(hidc, hidc, kernel_size=k, padding=autopad(k), groups=hidc) for k in kernel_sizes)
self.pw_conv = Conv(hidc, hidc)
self.post_conv = Conv(hidc, ouc)
if with_caa:
self.caa_factor = PKIModule_CAA(hidc, caa_kernel_size, caa_kernel_size)
else:
self.caa_factor = None
self.add_identity = add_identity and inc == ouc
def forward(self, x):
x = self.pre_conv(x)
y = x
x = self.dw_conv[0](x)
x = torch.sum(torch.stack([x] + [layer(x) for layer in self.dw_conv[1:]], dim=0), dim=0)
x = self.pw_conv(x)
if self.caa_factor is not None:
y = self.caa_factor(y)
if self.add_identity:
y = x * y
x = x + y
else:
x = x * y
x = self.post_conv(x)
return x
class C2f_PKIModule(C2f):
def __init__(self, c1, c2, n=1, kernel_sizes=(3, 5, 7, 9, 11), expansion=1.0, with_caa=True, caa_kernel_size=11, add_identity=True, g=1, e=0.5):
super().__init__(c1, c2, n, True, g, e)
self.m = nn.ModuleList(PKIModule(self.c, self.c, kernel_sizes, expansion, with_caa, caa_kernel_size, add_identity) for _ in range(n))
2.2 C2f_PKIModule的神经网络模块代码解析
此代码定义了三个类,PKIModule_CAA
、PKIModule
和 C2f_PKIModule
,它们是自定义神经网络模块,可能受到用于遥感图像中物体检测的 Poly Kernel Inception Network (PKINet) 的启发。让我们分解每个类:
1. PKIModule_CAA
此模块实现了上下文锚点注意 (CAA) 机制来捕获长距离依赖关系。
-
__init__
方法: -
avg_pool
:应用 2D 平均池化操作,内核大小为 7,步长为 1。 -
conv1
:处理池化输入的卷积层(可能是标准的,在其他地方定义为Conv
)。 -
h_conv
:沿水平方向的深度卷积,具有可自定义的内核大小。 -
v_conv
:沿垂直方向的深度卷积,具有可自定义的内核大小。 -
conv2
:另一个卷积层,用于处理深度卷积的输出。 -
act
:一个 S 型激活函数,用于产生注意力因子。 -
forward
方法: -
输入
x
通过上面定义的操作序列进行处理,产生一个attn_factor
,通过捕获长距离上下文信息来增强输入特征。
2. PKIModule
此模块表示一个特征提取块,它结合了多尺度卷积并可选地集成 CAA 机制。
-
__init__
方法: -
pre_conv
:处理输入通道的卷积层。 -
dw_conv
:具有不同内核大小的深度卷积列表,用于捕获多个尺度的特征。 -
pw_conv
:结合深度卷积输出的点式卷积 (1x1)。 -
post_conv
:另一个处理最终输出的卷积层。 -
caa_factor
:如果with_caa
为True
,则为可选的 CAA 模块,用于捕获长距离依赖关系。 -
add_identity
:一个标志,指示在输入和输出通道相同时是否添加残差连接(身份映射)。 -
forward
方法: -
输入
x
由pre_conv
处理。 -
输出通过
dw_conv
中的每个深度卷积,并将结果相加。 -
如果存在
caa_factor
,它会使用上下文信息增强输入特征(y
)。 -
根据
add_identity
,输出要么与输入相加(残差连接),要么乘以 CAA 增强特征。 -
最后,
post_conv
处理输出,然后返回它。
3. C2f_PKIModule
此模块扩展了类 C2f
(可能是在其他地方定义的自定义类),以按顺序包含多个 PKIModule
实例。
-
__init__
方法: -
它使用提供的参数和
PKIModule
实例列表初始化C2f
。 -
PKIModule
实例配置了kernel_sizes
、expansion
、with_caa
和add_identity
等参数来控制其行为。 -
forward 方法(继承自
C2f
): -
前向传递将涉及通过列表中的每个
PKIModule
处理输入。
此代码有效地实现了受 PKINet 架构启发的神经网络模块,其组件旨在捕获多尺度特征和远程上下文信息,以实现稳健的特征提取。
2.3 更改init.py文件
关键步骤二:修改modules文件夹下的__init__.py文件,先导入函数
然后在下面的__all__中声明函数
2.4 添加yaml文件
关键步骤三:在/ultralytics/ultralytics/cfg/models/v8下面新建文件yolov8_C2f_PKIModule.yaml文件,粘贴下面的内容
- OD【目标检测】
# Ultralytics YOLO 🚀, AGPL-3.0 license
# YOLOv8 object detection model with P3-P5 outputs. For Usage examples see https://docs.ultralytics.com/tasks/detect
# Parameters
nc: 80 # number of classes
scales: # model compound scaling constants, i.e. 'model=yolov8n.yaml' will call yolov8.yaml with scale 'n'
# [depth, width, max_channels]
n: [0.33, 0.25, 1024] # YOLOv8n summary: 225 layers, 3157200 parameters, 3157184 gradients, 8.9 GFLOPs
s: [0.33, 0.50, 1024] # YOLOv8s summary: 225 layers, 11166560 parameters, 11166544 gradients, 28.8 GFLOPs
m: [0.67, 0.75, 768] # YOLOv8m summary: 295 layers, 25902640 parameters, 25902624 gradients, 79.3 GFLOPs
l: [1.00, 1.00, 512] # YOLOv8l summary: 365 layers, 43691520 parameters, 43691504 gradients, 165.7 GFLOPs
x: [1.00, 1.25, 512] # YOLOv8x summary: 365 layers, 68229648 parameters, 68229632 gradients, 258.5 GFLOPs
# YOLOv8.0n backbone
backbone:
# [from, repeats, module, args]
- [-1, 1, Conv, [64, 3, 2]] # 0-P1/2
- [-1, 1, Conv, [128, 3, 2]] # 1-P2/4
- [-1, 3, C2f_PKIModule, [128, [3, 5, 7, 9, 11], 1.0, True, 11, True]]
- [-1, 1, Conv, [256, 3, 2]] # 3-P3/8
- [-1, 6, C2f_PKIModule, [256, [3, 5, 7, 9, 11], 1.0, True, 11, True]]
- [-1, 1, Conv, [512, 3, 2]] # 5-P4/16
- [-1, 6, C2f_PKIModule, [512, [3, 5, 7, 9, 11], 1.0, True, 11, True]]
- [-1, 1, Conv, [1024, 3, 2]] # 7-P5/32
- [-1, 3, C2f_PKIModule, [1024, [3, 5, 7, 9, 11], 1.0, True, 11, True]]
- [-1, 1, SPPF, [1024, 5]] # 9
# YOLOv8.0n head
head:
- [-1, 1, nn.Upsample, [None, 2, 'nearest']]
- [[-1, 6], 1, Concat, [1]] # cat backbone P4
- [-1, 3, C2f, [512]] # 12
- [-1, 1, nn.Upsample, [None, 2, 'nearest']]
- [[-1, 4], 1, Concat, [1]] # cat backbone P3
- [-1, 3, C2f, [256]] # 15 (P3/8-small)
- [-1, 1, Conv, [256, 3, 2]]
- [[-1, 12], 1, Concat, [1]] # cat head P4
- [-1, 3, C2f, [512]] # 18 (P4/16-medium)
- [-1, 1, Conv, [512, 3, 2]]
- [[-1, 9], 1, Concat, [1]] # cat head P5
- [-1, 3, C2f, [1024]] # 21 (P5/32-large)
- [[15, 18, 21], 1, Detect, [nc]] # Detect(P3, P4, P5)
- Seg【语义分割】
# Ultralytics YOLO 🚀, AGPL-3.0 license
# YOLOv8 object detection model with P3-P5 outputs. For Usage examples see https://docs.ultralytics.com/tasks/detect
# Parameters
nc: 80 # number of classes
scales: # model compound scaling constants, i.e. 'model=yolov8n.yaml' will call yolov8.yaml with scale 'n'
# [depth, width, max_channels]
n: [0.33, 0.25, 1024] # YOLOv8n summary: 225 layers, 3157200 parameters, 3157184 gradients, 8.9 GFLOPs
s: [0.33, 0.50, 1024] # YOLOv8s summary: 225 layers, 11166560 parameters, 11166544 gradients, 28.8 GFLOPs
m: [0.67, 0.75, 768] # YOLOv8m summary: 295 layers, 25902640 parameters, 25902624 gradients, 79.3 GFLOPs
l: [1.00, 1.00, 512] # YOLOv8l summary: 365 layers, 43691520 parameters, 43691504 gradients, 165.7 GFLOPs
x: [1.00, 1.25, 512] # YOLOv8x summary: 365 layers, 68229648 parameters, 68229632 gradients, 258.5 GFLOPs
# YOLOv8.0n backbone
backbone:
# [from, repeats, module, args]
- [-1, 1, Conv, [64, 3, 2]] # 0-P1/2
- [-1, 1, Conv, [128, 3, 2]] # 1-P2/4
- [-1, 3, C2f_PKIModule, [128, [3, 5, 7, 9, 11], 1.0, True, 11, True]]
- [-1, 1, Conv, [256, 3, 2]] # 3-P3/8
- [-1, 6, C2f_PKIModule, [256, [3, 5, 7, 9, 11], 1.0, True, 11, True]]
- [-1, 1, Conv, [512, 3, 2]] # 5-P4/16
- [-1, 6, C2f_PKIModule, [512, [3, 5, 7, 9, 11], 1.0, True, 11, True]]
- [-1, 1, Conv, [1024, 3, 2]] # 7-P5/32
- [-1, 3, C2f_PKIModule, [1024, [3, 5, 7, 9, 11], 1.0, True, 11, True]]
- [-1, 1, SPPF, [1024, 5]] # 9
# YOLOv8.0n head
head:
- [-1, 1, nn.Upsample, [None, 2, 'nearest']]
- [[-1, 6], 1, Concat, [1]] # cat backbone P4
- [-1, 3, C2f, [512]] # 12
- [-1, 1, nn.Upsample, [None, 2, 'nearest']]
- [[-1, 4], 1, Concat, [1]] # cat backbone P3
- [-1, 3, C2f, [256]] # 15 (P3/8-small)
- [-1, 1, Conv, [256, 3, 2]]
- [[-1, 12], 1, Concat, [1]] # cat head P4
- [-1, 3, C2f, [512]] # 18 (P4/16-medium)
- [-1, 1, Conv, [512, 3, 2]]
- [[-1, 9], 1, Concat, [1]] # cat head P5
- [-1, 3, C2f, [1024]] # 21 (P5/32-large)
- [[15, 18, 21], 1, Segment, [nc, 32, 256]] # Segment(P3, P4, P5)
温馨提示:因为本文只是对yolov8基础上添加模块,如果要对yolov8n/l/m/x进行添加则只需要指定对应的depth_multiple 和 width_multiple。不明白的同学可以看这篇文章: yolov8yaml文件解读——点击即可跳转
# YOLOv8n
depth_multiple: 0.33 # model depth multiple
width_multiple: 0.25 # layer channel multiple
max_channels: 1024 # max_channels
# YOLOv8s
depth_multiple: 0.33 # model depth multiple
width_multiple: 0.50 # layer channel multiple
max_channels: 1024 # max_channels
# YOLOv8l
depth_multiple: 1.0 # model depth multiple
width_multiple: 1.0 # layer channel multiple
max_channels: 512 # max_channels
# YOLOv8m
depth_multiple: 0.67 # model depth multiple
width_multiple: 0.75 # layer channel multiple
max_channels: 768 # max_channels
# YOLOv8x
depth_multiple: 1.33 # model depth multiple
width_multiple: 1.25 # layer channel multiple
max_channels: 512 # max_channels
2.5 注册模块
关键步骤四:在task.py的parse_model函数中注册
2.6 执行程序
在train.py中,将model的参数路径设置为yolov8_C2f_PKIModule.yaml的路径
建议大家写绝对路径,确保一定能找到
from ultralytics import YOLO
import warnings
warnings.filterwarnings('ignore')
from pathlib import Path
if __name__ == '__main__':
# 加载模型
model = YOLO("ultralytics/cfg/v8/yolov8.yaml") # 你要选择的模型yaml文件地址
# Use the model
results = model.train(data=r"你的数据集的yaml文件地址",
epochs=100, batch=16, imgsz=640, workers=4, name=Path(model.cfg).stem) # 训练模型
🚀运行程序,如果出现下面的内容则说明添加成功🚀
from n params module arguments
0 -1 1 464 ultralytics.nn.modules.conv.Conv [3, 16, 3, 2]
1 -1 1 4672 ultralytics.nn.modules.conv.Conv [16, 32, 3, 2]
2 -1 1 9152 ultralytics.nn.modules.block.C2f_PKIModule [32, 32, 1, [3, 5, 7, 9, 11], 1.0, True, 11, True]
3 -1 1 18560 ultralytics.nn.modules.conv.Conv [32, 64, 3, 2]
4 -1 2 43520 ultralytics.nn.modules.block.C2f_PKIModule [64, 64, 2, [3, 5, 7, 9, 11], 1.0, True, 11, True]
5 -1 1 73984 ultralytics.nn.modules.conv.Conv [64, 128, 3, 2]
6 -1 2 132096 ultralytics.nn.modules.block.C2f_PKIModule [128, 128, 2, [3, 5, 7, 9, 11], 1.0, True, 11, True]
7 -1 1 295424 ultralytics.nn.modules.conv.Conv [128, 256, 3, 2]
8 -1 1 288256 ultralytics.nn.modules.block.C2f_PKIModule [256, 256, 1, [3, 5, 7, 9, 11], 1.0, True, 11, True]
9 -1 1 164608 ultralytics.nn.modules.block.SPPF [256, 256, 5]
10 -1 1 0 torch.nn.modules.upsampling.Upsample [None, 2, 'nearest']
11 [-1, 6] 1 0 ultralytics.nn.modules.conv.Concat [1]
12 -1 1 148224 ultralytics.nn.modules.block.C2f [384, 128, 1]
13 -1 1 0 torch.nn.modules.upsampling.Upsample [None, 2, 'nearest']
14 [-1, 4] 1 0 ultralytics.nn.modules.conv.Concat [1]
15 -1 1 37248 ultralytics.nn.modules.block.C2f [192, 64, 1]
16 -1 1 36992 ultralytics.nn.modules.conv.Conv [64, 64, 3, 2]
17 [-1, 12] 1 0 ultralytics.nn.modules.conv.Concat [1]
18 -1 1 123648 ultralytics.nn.modules.block.C2f [192, 128, 1]
19 -1 1 147712 ultralytics.nn.modules.conv.Conv [128, 128, 3, 2]
20 [-1, 9] 1 0 ultralytics.nn.modules.conv.Concat [1]
21 -1 1 493056 ultralytics.nn.modules.block.C2f [384, 256, 1]
22 [15, 18, 21] 1 897664 ultralytics.nn.modules.head.Detect [80, [64, 128, 256]]
YOLOv8_C2f_Parc summary: 345 layers, 2915280 parameters, 2915264 gradients, 8.5 GFLOPs
3. 完整代码分享
https://pan.baidu.com/s/13Cd-UuqTe4Gn7dXNAVtKmw?pwd=x29m
提取码: x29m
4. GFLOPs
关于GFLOPs的计算方式可以查看:百面算法工程师 | 卷积基础知识——Convolution
未改进的YOLOv8nGFLOPs
改进后的GFLOPs
5. 进阶
可以与其他的注意力机制或者损失函数等结合,进一步提升检测效果
6. 总结
PKIModule 是一种旨在提高特征提取能力的卷积模块,特别针对具有多尺度和复杂上下文信息的图像。它通过多个不同大小的深度可分离卷积核并行提取输入特征的多尺度信息,以捕捉图像中不同尺度的物体特征。随后,这些多尺度的特征通过逐点卷积层进行融合,确保在特征提取过程中保持丰富的上下文信息。此外,PKIModule 可以选择性地包含一个上下文锚注意力模块(CAA),该模块通过捕捉长距离的上下文依赖关系来进一步增强特征表示。最后,模块还支持残差连接,将输入特征与输出特征结合,以保留输入信息并防止梯度消失。整体而言,PKIModule 通过有效的多尺度卷积和注意力机制,提高了对图像中不同尺度和上下文复杂性特征的捕捉能力,适用于需要高精度特征提取的计算机视觉任务。