YOLOv8目标检测创新改进与实战案例专栏
专栏目录: YOLOv8有效改进系列及项目实战目录 包含卷积,主干 注意力,检测头等创新机制 以及 各种目标检测分割项目实战案例
专栏链接: YOLOv8基础解析+创新改进+实战案例
介绍
摘要
基础模型的核心理念是“更多即不同”,这一理念在计算机视觉和自然语言处理领域取得了惊人的成功。然而,Transformer 模型的优化挑战和内在复杂性要求我们向简约的范式转变。在这项研究中,我们介绍了 VanillaNet,这是一种追求设计优雅的神经网络架构。通过避免高深度、捷径以及自注意等复杂操作,VanillaNet 展现出简洁明了却功能强大的特点。每一层都经过精心设计,结构紧凑且直观,训练后去除非线性激活函数以恢复原始架构。VanillaNet 克服了内在复杂性的挑战,非常适合资源受限的环境。其易于理解且高度简化的架构为高效部署开辟了新可能。大量实验表明,VanillaNet 的性能与著名的深度神经网络和视觉 Transformer 相媲美,展示了深度学习中极简主义的力量。VanillaNet 的这一创新旅程具有重新定义基础模型领域并挑战现状的巨大潜力,为优雅和有效的模型设计开辟了新路径。预训练模型和代码可在以下地址获得:https://github.com/huawei-noah/VanillaNet 和 https://gitee.com/mindspore/models/tree/master/research/cv/vanillanet。
文章链接
论文地址:论文地址
代码地址:代码地址
# 基本原理
简化的设计:VanillaNet避免了过度的深度、捷径和复杂的操作,如自注意力机制,使得网络结构变得简洁而强大。每一层都经过精心设计,紧凑而直观,训练后修剪非线性激活函数,以恢复原始架构[T5]。
网络架构:VanillaNet的架构包括三个主要部分:干细胞块(stem block)、主体和全连接层。主体通常包括四个阶段,每个阶段由堆叠相同的块构成。在每个阶段之后,特征的通道会扩展,而高度和宽度会减小[T3]。
训练策略:为了训练VanillaNet,研究人员进行了全面分析,针对简化的架构设计了“深度训练”策略。该策略从包含非线性激活函数的几层开始,随着训练的进行,逐渐消除这些非线性层,从而实现易于合并并保持推理速度。为增强网络的非线性,提出了一种高效的基于序列的激活函数,包含多个可学习的仿射变换[T4]。
实时处理性能:VanillaNet在实时处理方面表现出色,尤其是在图像分类任务中。通过调整通道数和池化大小,VanillaNet-13-1.5×在ImageNet上实现了83.11%的Top-1准确率,显示出VanillaNet在大规模图像分类任务中仍然具有强大的性能[T2]。
网络架构
上图展示了VanillaNet的架构示意图,非常简洁。
- Stem部分:采用卷积进行特征变换。
- Body部分:每个阶段先用MaxPool进行特征下采样,然后采用一个卷积进行特征处理。
- Head部分:采用两个非线性层进行分类处理。
值得注意的是:(1) 每个阶段只有一个卷积;(2) VanillaNet没有跳过连接。
核心代码
class VanillaNet(nn.Module):
def __init__(self, in_chans=3, num_classes=1000, dims=[96, 192, 384, 768],
drop_rate=0, act_num=3, strides=[2,2,2,1], deploy=False, ada_pool=None, **kwargs):
super().__init__()
self.deploy = deploy
stride, padding = (4, 0) if not ada_pool else (3, 1)
if self.deploy:
self.stem = nn.Sequential(
nn.Conv2d(in_chans, dims[0], kernel_size=4, stride=stride, padding=padding),
activation(dims[0], act_num, deploy=self.deploy)
)
else:
self.stem1 = nn.Sequential(
nn.Conv2d(in_chans, dims[0], kernel_size=4, stride=stride, padding=padding),
nn.BatchNorm2d(dims[0], eps=1e-6),
)
self.stem2 = nn.Sequential(
nn.Conv2d(dims[0], dims[0], kernel_size=1, stride=1),
nn.BatchNorm2d(dims[0], eps=1e-6),
activation(dims[0], act_num)
)
self.act_learn = 1
self.stages = nn.ModuleList()
for i in range(len(strides)):
if not ada_pool:
stage = Block(dim=dims[i], dim_out=dims[i+1], act_num=act_num, stride=strides[i], deploy=deploy)
else:
stage = Block(dim=dims[i], dim_out=dims[i+1], act_num=act_num, stride=strides[i], deploy=deploy, ada_pool=ada_pool[i])
self.stages.append(stage)
self.depth = len(strides)
if self.deploy:
self.cls = nn.Sequential(
nn.AdaptiveAvgPool2d((1,1)),
nn.Dropout(drop_rate),
nn.Conv2d(dims[-1], num_classes, 1),
)
else:
self.cls1 = nn.Sequential(
nn.AdaptiveAvgPool2d((1,1)),
nn.Dropout(drop_rate),
nn.Conv2d(dims[-1], num_classes, 1),
nn.BatchNorm2d(num_classes, eps=1e-6),
)
self.cls2 = nn.Sequential(
nn.Conv2d(num_classes, num_classes, 1)
)
self.apply(self._init_weights)
def _init_weights(self, m):
if isinstance(m, (nn.Conv2d, nn.Linear)):
weight_init.trunc_normal_(m.weight, std=.02)
nn.init.constant_(m.bias, 0)
task与yaml配置
详见:https://blog.csdn.net/shangyanaf/article/details/139664922