1. 文章主要内容
本篇博客主要涉及轻量级上采样算子CARAFE,将YOLOv5/YOLOv8模型中最近邻上采样算子改为CARAFE算子,使模型聚合上下文信息,助力模型涨点。
2. 简要概括
论文地址:CARAFE论文地址
论文Github代码:Github代码
CARAFE具有以下特点:
1.感受野大。不同于以往只利用亚像素邻域的工作(如双线性插值),CARAFE可以在一个大的接收域中聚合上下文信息。
2.内容感知。CARAFE不是为所有的样本使用一个固定的内核(例如反卷积,也就是transposed conv),而是支持特定于实例的内容感知处理,它可以动态地生成自适应的内核。
3.轻量级、计算速度快。CARAFE引入了很少的计算开销,可以很容易地集成到现有的网络架构中。其结构如下图所示:
分析:CARAFE作为上采样算子,其轻量级、计算资源小并且高效的特点能够助力YOLOv5/YOLOv8模型涨点。YOLOv5/YOLOv8模型的金字塔结构,也就是Neck结构部分,是需要通过上采样的算子将图片的分辨率进行扩大,以便层级的进行融合,从而融合层级的特征。然而,在上采样的过程中,原模型使用的是最近邻的静态上采样方法,容易丢失信息,尤其是小目标的信息特征(因为小目标本来在图像中的占比像素就比较少),这样就会导致模型识别目标的精度会降低,出现漏检、误检等问题。而CARAFE算子动态的进行上采样,而且有一个大的感受野聚合上下文信息,从而能够抑制小目标信息过多丢失,助力模型涨点!
3. 详细代码改进流程
接下来手把手将CARAFE算子添加到YOLOv5/YOLOv8模型中某一个地方的全实验过程。首先给出CARAFE算子的源码,新建一个CARAFE.py存放源代码,如下所示:
import torch
from torch import nn
from models.common import Conv
class CARAFE(nn.Module):
def __init__(self, c, k_enc=3, k_up=5, c_mid=64, scale=2):
""" The unofficial implementation of the CARAFE module.
The details are in "https://arxiv.org/abs/1905.02188".
Args:
c: The channel number of the input and the output.
c_mid: The channel number after compression.
scale: The expected upsample scale.
k_up: The size of the reassembly kernel.
k_enc: The kernel size of the encoder.
Returns:
X: The upsampled feature map.
"""
super(CARAFE, self).__init__()
self.scale = scale
self.comp = Conv(c, c_mid)
self.enc = Conv(c_mid, (scale * k_up) ** 2, k=k_enc, act=False)
self.pix_shf = nn.PixelShuffle(scale)
self.upsmp = nn.Upsample(scale_factor=scale, mode='nearest')
self.unfold = nn.Unfold(kernel_size=k_up, dilation=scale,
padding=k_up // 2 * scale)
def forward(self, X):
b, c, h, w = X.size()
h_, w_ = h * self.scale, w * self.scale
W = self.comp(X) # b * m * h * w
W = self.enc(W) # b * 100 * h * w
W = self.pix_shf(W) # b * 25 * h_ * w_
W = torch.softmax(W, dim=1) # b * 25 * h_ * w_
X = self.upsmp(X) # b * c * h_ * w_
X = self.unfold(X) # b * 25c * h_ * w_
X = X.view(b, c, -1, h_, w_) # b * 25 * c * h_ * w_
X = torch.einsum('bkhw,bckhw->bchw', [W, X]) # b * c * h_ * w_
return X
然后分别来讲融合到YOLOv5和YOLOv8模型的教程,这里先从YOLOv5模型开始。
3.1 YOLOv5融合CARAFE算子
3.1.1 新建一个yolov5-CARAFE.yaml文件
然后,新建一个yolov5-CARAFE.yaml文件,同时 注意nc改为自己数据集的类别数,这里是将CARAFE算子替换了Neck部分的两个上采样部分。yaml文件源代码如下所示:
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
# Parameters
nc: 4 # number of classes
depth_multiple: 0.33 # model depth multiple
width_multiple: 0.50 # 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 output_channel, kernel_size, stride, padding
[-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, CARAFE, [3, 5]],
#[-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, CARAFE, [3, 5]],
#[-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)
[[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
]
3.1.2 将CARAFE引入到yolo.py文件中
在下图的红色圈内位置处,引入CARAFE,并手动导入相应的包即可。代码和示意图如下:
elif m is CARAFE:
c2 = ch[f]
args = [c2, *args]
3.1.3 修改train.py启动文件
修改配置文件为yolov5-CARAFE.yaml即可,如下图所示:
3.2 YOLOv8融合CARAFE算子
3.2.1 新建一个yolov8-CARAFE.yaml文件
然后,新建一个yolov8-CARAFE.yaml文件,同时 注意nc改为自己数据集的类别数,这里是将CARAFE算子替换了Neck部分的两个上采样部分。yaml文件源代码如下所示:
# 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: 10 # 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, [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, CARAFE, []]
- [[-1, 6], 1, Concat, [1]] # cat backbone P4
- [-1, 3, C2f, [512]] # 12
- [-1, 1, CARAFE, []]
- [[-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)
3.2.2 将CARAFE源码拷贝到conv.py中并在__init__.py全局注册
conv.py的路径在这里:ultralytics/nn/modules/conv.py,
init.py的路径在这里:ultralytics/nn/modules/init.py
第一步:将源码拷贝到conv.py的随便一处,然后在conv.py的最上面有个__all__的地方注册CARAFE引用,如下图所示:
第二步:在__init__.py中的.conv中import CARAFE算子,然后同时在此文件的最下放的__all__处也注册CARAFE算子,分别如下图所示:
3.2.3 将CARAFE引入到task.py中,并添加相应的代码
将CARAFE的逻辑代码引入到task.py中,并导入相应的包,这一步其实和YOLOv5中的yolo.py有异曲同工之妙。另外task.py的路径在这里:ultralytics/nn/tasks.py,代码和操作如下所示:
elif m is CARAFE:
c1 = ch[f]
args = [c1]
3.2.4 启动train.py即可
将train.py中训练YOLOv8的模型文件改为YOLOv8n-CARAFE.yaml即可。这里需要注意到,我在YOLOv8后面加了一个n,程序会自动识别调用n的模型大小,我们的yaml文件不需要重命名后面多加一个n。
4. 总结
本篇博客主要介绍了CARAFE轻量级上采样算子,聚合上下文信息,助力YOLOv5/YOLOv8模型涨点。另外,在修改过程中,要是有任何问题,评论区交流;如果博客对您有帮助,请帮忙点个赞,收藏一下;后续会持续更新本人实验当中觉得有用的点子,如果很感兴趣的话,可以关注一下,谢谢大家啦!