秋招面试专栏推荐 :深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转
💡💡💡本专栏所有程序均经过测试,可成功执行💡💡💡
专栏目录 :《YOLOv8改进有效涨点》专栏介绍 & 专栏目录 | 目前已有90+篇内容,内含各种Head检测头、损失函数Loss、Backbone、Neck、NMS等创新点改进——点击即可跳转
在目标检测领域内,尽管YOLO系列的算法傲视群雄,但在某些方面仍然存在改进的空间。在YOLOv8提取特征的时候,卷积的核是固定的K*K大小,导致参数数量随着大小的增加呈平方级增长。显然,不同数据集和目标的形状及大小各异,而固定形状和大小的卷积核无法灵活适应这种变化。本文给大家带来的教程是将原来的普通的卷积替换为可变核的卷积AKConv。文章在介绍主要的原理后,将手把手教学如何进行模块的代码添加和修改,并将修改后的完整代码放在文章的最后,方便大家一键运行,小白也可轻松上手实践。以帮助您更好地学习深度学习目标检测YOLO系列的挑战。
专栏地址:YOLOv8改进——更新各种有效涨点方法——点击即可跳转 订阅学习不迷路
目录
1.原理
2. 将C2f_AKConv代码添加到YOLOv8中
2.1 C2f_AKConv的代码实现
2.2 更改__init__.py文件
2.3 添加yaml文件
2.4 注册模块
2.5 执行程序
3. 完整代码分享
4. GFLOPs
5. 进阶
6. 总结
1.原理
论文地址: AKConv: Convolutional Kernel with Arbitrary Sampled Shapes and Arbitrary Number of Parameters——点击即可跳转
官方代码:官方代码仓库——点击即可跳转
AKconv 是一种用于卷积神经网络的新型卷积操作方法,其目的是提高网络的特征提取能力,同时解决传统卷积操作中的一些固有局限性。
核心原理:
-
灵活的卷积参数数量:
-
AKconv 提供了灵活的卷积核参数数量,可以让卷积核适应不同形状的目标,而不是像传统卷积那样依赖固定大小和形状的卷积核。这种灵活性可以通过调整卷积核的大小和采样点的数量来实现。
-
-
任意采样形状:
-
AKconv 可以根据不同的目标动态调整卷积核的采样形状,而不仅仅是固定的正方形采样格子。通过引入偏移(offset),AKconv 能够更好地适应目标的形状变化,从而提高特征提取的准确性。
-
-
线性增长的参数数量:
-
与传统的卷积操作不同,AKconv 的卷积参数数量随着卷积核大小的增加呈线性增长,而不是平方增长。这种线性增长有助于降低计算和内存的开销,尤其是在需要大卷积核进行特征提取的情况下。
-
-
适用于不规则卷积操作:
-
AKconv 能够执行不规则卷积操作,即允许卷积核具有不规则的采样点分布。这种灵活性使得 AKconv 能够更有效地捕获不同尺度和形状的特征,提高卷积神经网络在复杂任务中的表现。
-
-
与现有卷积操作的兼容性:
-
AKconv 可以无缝替换现有的卷积操作,从而提升网络性能。此外,AKconv 还可以与其他新型卷积模块(如 FasterBlock 和 GSBottleneck)结合使用,进一步增强这些模块的性能。
-
AKconv 的设计旨在通过提供更大的灵活性和有效性,克服传统卷积操作中的局限性,从而为深度学习中的特征提取提供更强大的工具。AKconv 是一种用于卷积神经网络的新型卷积操作方法,其目的是提高网络的特征提取能力,同时解决传统卷积操作中的一些固有局限性。
核心原理:
-
灵活的卷积参数数量:
-
AKconv 提供了灵活的卷积核参数数量,可以让卷积核适应不同形状的目标,而不是像传统卷积那样依赖固定大小和形状的卷积核。这种灵活性可以通过调整卷积核的大小和采样点的数量来实现。
-
-
任意采样形状:
-
AKconv 可以根据不同的目标动态调整卷积核的采样形状,而不仅仅是固定的正方形采样格子。通过引入偏移(offset),AKconv 能够更好地适应目标的形状变化,从而提高特征提取的准确性。
-
-
线性增长的参数数量:
-
与传统的卷积操作不同,AKconv 的卷积参数数量随着卷积核大小的增加呈线性增长,而不是平方增长。这种线性增长有助于降低计算和内存的开销,尤其是在需要大卷积核进行特征提取的情况下。
-
-
适用于不规则卷积操作:
-
AKconv 能够执行不规则卷积操作,即允许卷积核具有不规则的采样点分布。这种灵活性使得 AKconv 能够更有效地捕获不同尺度和形状的特征,提高卷积神经网络在复杂任务中的表现。
-
-
与现有卷积操作的兼容性:
-
AKconv 可以无缝替换现有的卷积操作,从而提升网络性能。此外,AKconv 还可以与其他新型卷积模块(如 FasterBlock 和 GSBottleneck)结合使用,进一步增强这些模块的性能。
-
AKconv 的设计旨在通过提供更大的灵活性和有效性,克服传统卷积操作中的局限性,从而为深度学习中的特征提取提供更强大的工具。
2. 将C2f_AKConv代码添加到YOLOv8中
2.1 C2f_AKConv的代码实现
关键步骤一:将下面代码粘贴到在/ultralytics/ultralytics/nn/modules/block.py中,并在该文件的__all__中添加“C2f_AKconv”
import math
from einops import rearrange
class AKConv(nn.Module):
def __init__(self, inc, outc, num_param=5, stride=1, bias=None):
super(AKConv, self).__init__()
self.num_param = num_param
self.stride = stride
self.conv = nn.Sequential(nn.Conv2d(inc, outc, kernel_size=(num_param, 1), stride=(num_param, 1), bias=bias),nn.BatchNorm2d(outc),nn.SiLU()) # the conv adds the BN and SiLU to compare original Conv in YOLOv5.
self.p_conv = nn.Conv2d(inc, 2 * num_param, kernel_size=3, padding=1, stride=stride)
nn.init.constant_(self.p_conv.weight, 0)
self.p_conv.register_full_backward_hook(self._set_lr)
@staticmethod
def _set_lr(module, grad_input, grad_output):
grad_input = (grad_input[i] * 0.1 for i in range(len(grad_input)))
grad_output = (grad_output[i] * 0.1 for i in range(len(grad_output)))
def forward(self, x):
# N is num_param.
offset = self.p_conv(x)
dtype = offset.data.type()
N = offset.size(1) // 2
# (b, 2N, h, w)
p = self._get_p(offset, dtype)
# (b, h, w, 2N)
p = p.contiguous().permute(0, 2, 3, 1)
q_lt = p.detach().floor()
q_rb = q_lt + 1
q_lt = torch.cat([torch.clamp(q_lt[..., :N], 0, x.size(2) - 1), torch.clamp(q_lt[..., N:], 0, x.size(3) - 1)],
dim=-1).long()
q_rb = torch.cat([torch.clamp(q_rb[..., :N], 0, x.size(2) - 1), torch.clamp(q_rb[..., N:], 0, x.size(3) - 1)],
dim=-1).long()
q_lb = torch.cat([q_lt[..., :N], q_rb[..., N:]], dim=-1)
q_rt = torch.cat([q_rb[..., :N], q_lt[..., N:]], dim=-1)
# clip p
p = torch.cat([torch.clamp(p[..., :N], 0, x.size(2) - 1), torch.clamp(p[..., N:], 0, x.size(3) - 1)], dim=-1)
# bilinear kernel (b, h, w, N)
g_lt = (1 + (q_lt[..., :N].type_as(p) - p[..., :N])) * (1 + (q_lt[..., N:].type_as(p) - p[..., N:]))
g_rb = (1 - (q_rb[..., :N].type_as(p) - p[..., :N])) * (1 - (q_rb[..., N:].type_as(p) - p[..., N:]))
g_lb = (1 + (q_lb[..., :N].type_as(p) - p[..., :N])) * (1 - (q_lb[..., N:].type_as(p) - p[..., N:]))
g_rt = (1 - (q_rt[..., :N].type_as(p) - p[..., :N])) * (1 + (q_rt[..., N:].type_as(p) - p[..., N:]))
# resampling the features based on the modified coordinates.
x_q_lt = self._get_x_q(x, q_lt, N)
x_q_rb = self._get_x_q(x, q_rb, N)
x_q_lb = self._get_x_q(x, q_lb, N)
x_q_rt = self._get_x_q(x, q_rt, N)
# bilinear
x_offset = g_lt.unsqueeze(dim=1) * x_q_lt + \
g_rb.unsqueeze(dim=1) * x_q_rb + \
g_lb.unsqueeze(dim=1) * x_q_lb + \
g_rt.unsqueeze(dim=1) * x_q_rt
x_offset = self._reshape_x_offset(x_offset, self.num_param)
out = self.conv(x_offset)
return out
# generating the inital sampled shapes for the AKConv with different sizes.
def _get_p_n(self, N, dtype):
base_int = round(math.sqrt(self.num_param))
row_number = self.num_param // base_int
mod_number = self.num_param % base_int
p_n_x,p_n_y = torch.meshgrid(
torch.arange(0, row_number),
torch.arange(0,base_int))
p_n_x = torch.flatten(p_n_x)
p_n_y = torch.flatten(p_n_y)
if mod_number > 0:
mod_p_n_x,mod_p_n_y = torch.meshgrid(
torch.arange(row_number,row_number+1),
torch.arange(0,mod_number))
mod_p_n_x = torch.flatten(mod_p_n_x)
mod_p_n_y = torch.flatten(mod_p_n_y)
p_n_x,p_n_y = torch.cat((p_n_x,mod_p_n_x)),torch.cat((p_n_y,mod_p_n_y))
p_n = torch.cat([p_n_x,p_n_y], 0)
p_n = p_n.view(1, 2 * N, 1, 1).type(dtype)
return p_n
# no zero-padding
def _get_p_0(self, h, w, N, dtype):
p_0_x, p_0_y = torch.meshgrid(
torch.arange(0, h * self.stride, self.stride),
torch.arange(0, w * self.stride, self.stride))
p_0_x = torch.flatten(p_0_x).view(1, 1, h, w).repeat(1, N, 1, 1)
p_0_y = torch.flatten(p_0_y).view(1, 1, h, w).repeat(1, N, 1, 1)
p_0 = torch.cat([p_0_x, p_0_y], 1).type(dtype)
return p_0
def _get_p(self, offset, dtype):
N, h, w = offset.size(1) // 2, offset.size(2), offset.size(3)
# (1, 2N, 1, 1)
p_n = self._get_p_n(N, dtype)
# (1, 2N, h, w)
p_0 = self._get_p_0(h, w, N, dtype)
p = p_0 + p_n + offset
return p
def _get_x_q(self, x, q, N):
b, h, w, _ = q.size()
padded_w = x.size(3)
c = x.size(1)
# (b, c, h*w)
x = x.contiguous().view(b, c, -1)
# (b, h, w, N)
index = q[..., :N] * padded_w + q[..., N:] # offset_x*w + offset_y
# (b, c, h*w*N)
index = index.contiguous().unsqueeze(dim=1).expand(-1, c, -1, -1, -1).contiguous().view(b, c, -1)
x_offset = x.gather(dim=-1, index=index).contiguous().view(b, c, h, w, N)
return x_offset
# Stacking resampled features in the row direction.
@staticmethod
def _reshape_x_offset(x_offset, num_param):
b, c, h, w, n = x_offset.size()
# using Conv3d
# x_offset = x_offset.permute(0,1,4,2,3), then Conv3d(c,c_out, kernel_size =(num_param,1,1),stride=(num_param,1,1),bias= False)
# using 1 × 1 Conv
# x_offset = x_offset.permute(0,1,4,2,3), then, x_offset.view(b,c×num_param,h,w) finally, Conv2d(c×num_param,c_out, kernel_size =1,stride=1,bias= False)
# using the column conv as follow, then, Conv2d(inc, outc, kernel_size=(num_param, 1), stride=(num_param, 1), bias=bias)
x_offset = rearrange(x_offset, 'b c h w n -> b c (h n) w')
return x_offset
class Bottleneck_AKConv(Bottleneck):
"""Standard bottleneck with FocusedLinearAttention."""
def __init__(self, c1, c2, shortcut=True, g=1, k=(3, 3), e=0.5): # ch_in, ch_out, shortcut, groups, kernels, expand
super().__init__(c1, c2, shortcut, g, k, e)
if k[0] == 3:
self.cv1 = AKConv(c1, c2, k[0])
self.cv2 = AKConv(c2, c2, k[1])
class C3_AKConv(C3):
def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5):
super().__init__(c1, c2, n, shortcut, g, e)
c_ = int(c2 * e) # hidden channels
self.m = nn.Sequential(*(Bottleneck_AKConv(c_, c_, shortcut, g, k=(1, 3), e=1.0) for _ in range(n)))
class C2f_AKConv(C2f):
def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5):
super().__init__(c1, c2, n, shortcut, g, e)
self.m = nn.ModuleList(Bottleneck_AKConv(self.c, self.c, shortcut, g, k=(3, 3), e=1.0) for _ in range(n))
AKConv 处理图像的主要流程可以分为以下几个关键步骤:
1. 初始采样点的生成:
-
首先,AKConv 通过一个算法生成卷积核的初始采样点。这些采样点可以是任意形状和数量,不再局限于传统的正方形格子。生成的采样点既可以是规则的(如标准的 3x3 或 5x5 网格),也可以是不规则的,适应不同卷积核大小和形状的需求。
2. 计算偏移量(Offsets):
-
AKConv 在处理图像时,会根据输入图像的特征,计算出每个采样点的偏移量。这些偏移量用于动态调整采样点的位置,使得卷积核能够更好地适应图像中的目标形状和尺度变化。偏移量的计算通常通过一个小的神经网络来完成,该网络根据输入图像的特征生成偏移。
3. 特征提取:
-
在调整采样点后,AKConv 使用这些经过偏移调整的采样点进行卷积操作,提取图像的局部特征。由于采样点的位置是动态调整的,AKConv 能够捕获更多元和复杂的特征信息,而不仅仅局限于固定位置的局部信息。
4. 线性参数增长:
-
与传统卷积核的参数数量随卷积核大小平方增长不同,AKConv 的参数数量随着卷积核大小线性增长。这意味着,在处理大卷积核时,AKConv 仍然能够有效控制计算和内存开销。这一步在处理图像时直接影响到模型的效率和资源使用。
5. 输出结果:
-
最终,AKConv 将通过调整后的卷积操作得到的特征图作为输出,这些特征图能够更准确地表示输入图像的内容。与传统卷积操作相比,AKConv 的输出特征具有更高的灵活性和表达力,这为后续的图像分析任务(如分类、检测等)提供了更丰富的信息。
6. 集成到网络:
-
AKConv 可以作为模块无缝集成到现有的卷积神经网络架构中,替换标准的卷积层以提升整个网络的性能。集成后的网络可以在处理复杂的图像任务时,展示出更强的适应性和更好的表现。
通过以上流程,AKConv 能够在处理图像时更好地捕获和表示图像中的多样性特征,特别是在目标形状和尺度变化较大的场景中,提供了显著的性能提升。
2.2 更改__init__.py文件
关键步骤二:修改modules文件夹下的__init__.py文件,先导入函数
然后在下面的__all__中声明函数
2.3 添加yaml文件
关键步骤三:在/ultralytics/ultralytics/cfg/models/v8下面新建文件yolov8_AKConv.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_AKConv, [128, True]]
- [-1, 1, Conv, [256, 3, 2]] # 3-P3/8
- [-1, 6, C2f_AKConv, [256, True]]
- [-1, 1, Conv, [512, 3, 2]] # 5-P4/16
- [-1, 6, C2f_AKConv, [512, True]]
- [-1, 1, AKConv, [1024, 3, 2]] # 7-P5/32
- [-1, 3, C2f_AKConv, [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, 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_AKConv, [128, True]]
- [-1, 1, Conv, [256, 3, 2]] # 3-P3/8
- [-1, 6, C2f_AKConv, [256, True]]
- [-1, 1, Conv, [512, 3, 2]] # 5-P4/16
- [-1, 6, C2f_AKConv, [512, True]]
- [-1, 1, AKConv, [1024, 3, 2]] # 7-P5/32
- [-1, 3, C2f_AKConv, [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, 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.4 注册模块
关键步骤四:在task.py的parse_model函数添加 C2f_AKConv,AKConv下面的内容
2.5 执行程序
在train.py中,将model的参数路径设置为yolov8_C2f_AKConv.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 6028 ultralytics.nn.modules.block.C2f_AKConv [32, 32, 1, True]
3 -1 1 18560 ultralytics.nn.modules.conv.Conv [32, 64, 3, 2]
4 -1 2 32024 ultralytics.nn.modules.block.C2f_AKConv [64, 64, 2, True]
5 -1 1 73984 ultralytics.nn.modules.conv.Conv [64, 128, 3, 2]
6 -1 2 113176 ultralytics.nn.modules.block.C2f_AKConv [128, 128, 2, True]
7 -1 1 105734 ultralytics.nn.modules.block.AKConv [128, 256, 3, 2]
8 -1 1 277516 ultralytics.nn.modules.block.C2f_AKConv [256, 256, 1, 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_AKConv summary: 264 layers, 2681310 parameters, 2681294 gradients
3. 完整代码分享
https://pan.baidu.com/s/1F1REf4aTcXr8-1BI6D2_aQ?pwd=vbav
提取码: vbav
4. GFLOPs
关于GFLOPs的计算方式可以查看:百面算法工程师 | 卷积基础知识——Convolution
未改进的YOLOv8nGFLOPs
改进后的GFLOPs
现在手上没有卡了,等过段时候有卡了把这补上,需要的同学自己测一下
5. 进阶
可以与其他的注意力机制或者损失函数等结合,进一步提升检测效果
6. 总结
AKConv 的主要原理在于通过灵活调整卷积核的参数数量和采样形状,克服传统卷积操作的局限性,实现更高效的特征提取。具体而言,AKConv 允许卷积核的参数数量随需求线性增长,而非传统的平方增长,这大大减少了计算和内存开销。此外,AKConv 能够动态调整卷积核的采样位置,以适应不同目标的形状变化,从而提高对复杂特征的捕捉能力。它支持不规则的卷积操作,能够灵活处理各种尺寸和形状的卷积核,使得网络在复杂任务中的表现更加出色。最重要的是,AKConv 可以无缝集成到现有的卷积神经网络中,替换传统卷积操作,从而提升网络的整体性能。