话接上文。之前学习了backbone,这里就学习neck了。我这里还是会以先以yolov8-pose的网络结构为例进行展示,然后再学习其neck层如何搭建。而Neck(颈部,连接部)是一个中间层,用于对来自backbone的特征进行融合,以提升模型地性能。yolov8并没有使用Neck这个概念,但其中架构图中Head中类似PANet功能地部分也可以归为Neck。所以我们这里会主要学习一下FPN和PANet,而下一节主要是说目标检测模型的决策部分,负责产生最终的检测结果。
在学习YOLOv8的neck之前,我这里对其backbone再啰嗦一下。
Backbone是yolov8的主干特征提取网络,输入图像首先会在主干网络里进行特征提取,提取到的特征可以被称为特征层,是输入图像的特征集合。在主干部分,我们获得了三个特征层进行下一步网络的构建,这三个特征层,我们称为有效特征层。之前也搭建了(在上个博客)。
而这节课的重点就是YOLOv8的Neck层。
1,yolov8的yaml配置文件
首先,我们仍然展示一下yolov8-pose.yaml文件。看看其网络的构造:
# Ultralytics YOLO 🚀, AGPL-3.0 license
# YOLOv8-pose keypoints/pose estimation model. For Usage examples see https://docs.ultralytics.com/tasks/pose
# Parameters
nc: 1 # number of classes
kpt_shape: [17, 3] # number of keypoints, number of dims (2 for x,y or 3 for x,y,visible)
scales: # model compound scaling constants, i.e. 'model=yolov8n-pose.yaml' will call yolov8-pose.yaml with scale 'n'
# [depth, width, max_channels]
n: [0.33, 0.25, 1024]
s: [0.33, 0.50, 1024]
m: [0.67, 0.75, 768]
l: [1.00, 1.00, 512]
x: [1.00, 1.25, 512]
# 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, [128, True]]
- [-1, 1, Conv, [256, 3, 2]] # 3-P3/8
- [-1, 6, C2f, [256, True]]
- [-1, 1, Conv, [512, 3, 2]] # 5-P4/16
- [-1, 6, C2f, [512, True]]
- [-1, 1, Conv, [1024, 3, 2]] # 7-P5/32
- [-1, 3, C2f, [1024, 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, Pose, [nc, kpt_shape]] # Pose(P3, P4, P5)
确实如我们所说,yolov8并没有neck之说,特征融合都集中在head里面。因为我们上一节已经展示了yolov8如何通过yaml文件加载backbone,所以我们这里直接通过yaml里面的head内容进行打印即可。
为了回顾,我先打印整个yolo-pose的网络结构(因为通过yaml内容只打印head会报错。。要么修改一下代码,要么就全打印了。这里为了方便,就全打印了):
我们通过yaml解析的内容,可以看到上面的neck的内容。neck的代码的解析
第10层:[-1, 1, nn.Upsample, [None, 2, 'nearest']] :本层是上采样层。-1代表将上层的输出作为本层的输入。None代表上采样的size(输出尺寸)不指定。2代表scale_factor=2,表示输出的尺寸是输入尺寸的2倍。nearest代表使用的上采样算法为最近邻插值算法。经过这层之后,特征图的长和宽变成原来的两倍,通道数不变,所以最终尺寸为40*40*1024。
第11层:[[-1, 6], 1, Concat, [1]] # cat backbone P4 :本层是concat层,[-1, 6]代表将上层和第6层的输出作为本层的输入。[1]代表concat拼接的维度是1。从上面的分析可知,上层的输出尺寸是40*40*1024,第6层的输出是40*40*512,最终本层的输出尺寸为40*40*1536。
第12层:[-1, 3, C2f, [512]] :本层是C2f模块。3代表本层重复3次。512代表输出通道数。与Backbone中C2f不同的是,此处的C2f的bottleneck模块的shortcut=False。
第13层:[-1, 1, nn.Upsample, [None, 2, 'nearest']] :本层也是上采样层(参考第10层)。经过这层之后,特征图的长和宽变成原来的两倍,通道数不变,所以最终尺寸为80*80*512。
第14层:[[-1, 4], 1, Concat, [1]] # cat backbone P3 :本层是concat层,[-1, 4]代表将上层和第4层的输出作为本层的输入。[1]代表concat拼接的维度是1。从上面的分析可知,上层的输出尺寸是80*80*512,第6层的输出是80*80*256,最终本层的输出尺寸为80*80*768。 -
第15层:[-1, 3, C2f, [256]] # 15 (P3/8-small) :本层是C2f模块。3代表本层重复3次。256代表输出通道数。经过这层之后,特征图尺寸变为80*80*