本文研究yolo系列的Neck模块。yolov1、yolov2没有使用Neck模块,yolov3开始使用。Neck模块的目的是融合不同层的特征检测大中小目标。
模块 | |
yolov3 | FPN |
yolov4 | spp+PANet |
yolov5 | spp+PANet,Concat层后的CBL模块改成了CSP_V5模块 |
yolox | spp+FPN |
yolov7 | sppscp+优化的PAN(Concat层前的CBL改成MPConv,Concat层后使用E-ELAN) |
在进行yolo系列Neck模块研究前,先研究FPN、SPP和PAN模块。
1.FPN(feature pyramid networks)
目的:提高小目标的检出。
原来很多目标检测算法都是只采用高层特征进行预测,高层的特征语义信息比较丰富,但是分辨率较低,目标位置比较粗略。假设在深层网络中,最后的高层特征图中一个像素可能对应着输出图像20*20的像素区域,那么小于20*20像素的小物体的特征大概率已经丢失。与此同时,低层的特征语义信息比较少,但是目标位置准确,这是对小目标检测有帮助的。FPN将高层特征与底层特征进行融合,从而同时利用低层特征的高分辨率和高层特征的丰富语义信息,并进行了多尺度特征的独立预测,对小物体的检测效果有明显的提升。
2.SPP(Spatial Pyramid Pooling)
SPP,即空间金字塔池化。SPP的目的是解决了输入数据大小任意的问题。SPP网络用在YOLOv4中的目的是增加网络的感受野
SPP的使用方法:
- 首先划分输入:将输入特征分别划分成不同份:最左边有16个蓝色小格子的图,它的意思是将从输入特征分成16份,16X256中的256表示的是channel,即SPP对每一层都分成16份(不一定是等比分)。中间的4个绿色小格子和右边1个紫色大格子也同理,即将输入特征分别分成4X256和1X256份。(注意上面划分成多少份是可以自定义)
- 对每份特征进行池化:一般选择MAX Pooling,即对每一份进行最大池化。看上图,通过SPP层,输入特征被转化成了16X256+4X256+1X256 = 21X256的矩阵。
- 后面连接一个全连接层:连接一个1X10752的全连接层。这样就解决了输入数据大小任意的问题了。
SPP中卷积核的 尺寸、和步长大小计算方法:
假设输入数据大小是 (7,11), 池化数量 (4,4):
那么核大小为 (2,3), 步长大小为 (2,3), padding 为 (1,1), 得到池化后的矩阵大小的确是 4∗4。
SPP的pytorch实现:
#coding=utf-8
import math
import torch
import torch.nn.functional as F
# 构建SPP层(空间金字塔池化层)
class SPPLayer(torch.nn.Module):
def __init__(self, num_levels, pool_type='max_pool'):
super(SPPLayer, self).__init__()
self.num_levels = num_levels
self.pool_type = pool_type
def forward(self, x):
num, c, h, w = x.size() # num:样本数量 c:通道数 h:高 w:宽
for i in range(self.num_levels):
level = i+1
kernel_size = (math.ceil(h / level), math.ceil(w / level))
stride = (math.ceil(h / level), math.ceil(w / level))
pooling = (math.floor((kernel_size[0]*level-h+1)/2), math.floor((kernel_size[1]*level-w+1)/2))
# 选择池化方式
if self.pool_type == 'max_pool':
tensor = F.max_pool2d(x, kernel_size=kernel_size, stride=stride, padding=pooling).view(num, -1)
else:
tensor = F.avg_pool2d(x, kernel_size=kernel_size, stride=stride, padding=pooling).view(num, -1)
# 展开、拼接
if (i == 0):
x_flatten = tensor.view(num, -1)
else:
x_flatten = torch.cat((x_flatten, tensor.view(num, -1)), 1)
return x_flatten
SPPCSP
SPP的优化,在SPP模块基础上在最后增加concat操作,与SPP模块之前的特征图进行融合,更加丰富了特征信息。
3.PANet
网络结构如下图所示,与FPN相比,PANet 在UpSample之后又加了DownSample的操作。PANet对不同层次的特征进行疯狂融合,其在FPN模块的基础上增加了自底向上的特征金字塔结构,保留了更多的浅层位置特征,将整体特征提取能力进一步提升。
PAN模块的优化:
PAN模块在每个Concat层后面引入一个E-ELAN结构,使用expand、shuffle、merge cardinality等策略实现在不破坏原始梯度路径的情况下,提高网络的学习能力。
4.yolov3
yolov3的NECK模块引入了FPN的思想,并对原始FPN进行修改。
YOLOv3设置了三个不同的尺寸,分别是19*19,38*38,76*76
YOLOv3采用全卷积的思路,在Neck侧也不例外(YOLOv1-v2中采用池化层做特征图的下采样, v3中采用卷积层来实现)。
5.yolov4
yolov4的Neck模块主要包含了SPP模块和PAN模块。
YOLOv4引入PAN时,特征图最后的融合操作相比于原论文发生了变化,从add操作改为concat操作,增加了特征图的通道数:
6. yolov5
YOLOv5的Neck侧也使用了SPP模块和PAN模块,但是在PAN模块进行融合后,将YOLOv4中使用的CBL模块替换成借鉴CSPnet设计的CSP_v5结构,加强网络特征融合的能力。
7.yolovx
YOLOx的Neck侧依然使用了YOLOv3的结构,并且使用了SPP模块。
在Neck侧,yolox和yov3的差别在于:在高层特征支路上使用了SPP模块:
8.yolov7
YOLOv7的Neck侧主要包含了SPPSCP模块和优化的PAN模块。
SPPCSP模块在SPP模块基础上在最后增加concat操作,与SPP模块之前的特征图进行融合,更加丰富了特征信息。
PAN模块引入E-ELAN结构,使用expand、shuffle、merge cardinality等策略实现在不破坏原始梯度路径的情况下,提高网络的学习能力。