【YOLOv8改进 - Backbone主干】VanillaNet:极简的神经网络,利用VanillaNet替换YOLOV8主干

简介: 【YOLOv8改进 - Backbone主干】VanillaNet:极简的神经网络,利用VanillaNet替换YOLOV8主干

YOLOv8目标检测创新改进与实战案例专栏

专栏目录: YOLOv8有效改进系列及项目实战目录 包含卷积,主干 注意力,检测头等创新机制 以及 各种目标检测分割项目实战案例

专栏链接: YOLOv8基础解析+创新改进+实战案例

介绍

image-20240613204732182

摘要

基础模型的核心理念是“更多即不同”,这一理念在计算机视觉和自然语言处理领域取得了惊人的成功。然而,Transformer 模型的优化挑战和内在复杂性要求我们向简约的范式转变。在这项研究中,我们介绍了 VanillaNet,这是一种追求设计优雅的神经网络架构。通过避免高深度、捷径以及自注意等复杂操作,VanillaNet 展现出简洁明了却功能强大的特点。每一层都经过精心设计,结构紧凑且直观,训练后去除非线性激活函数以恢复原始架构。VanillaNet 克服了内在复杂性的挑战,非常适合资源受限的环境。其易于理解且高度简化的架构为高效部署开辟了新可能。大量实验表明,VanillaNet 的性能与著名的深度神经网络和视觉 Transformer 相媲美,展示了深度学习中极简主义的力量。VanillaNet 的这一创新旅程具有重新定义基础模型领域并挑战现状的巨大潜力,为优雅和有效的模型设计开辟了新路径。预训练模型和代码可在以下地址获得:https://github.com/huawei-noah/VanillaNethttps://gitee.com/mindspore/models/tree/master/research/cv/vanillanet。

文章链接

论文地址:论文地址

代码地址:代码地址

基本原理

  1. 简化的设计:VanillaNet避免了过度的深度、捷径和复杂的操作,如自注意力机制,使得网络结构变得简洁而强大。每一层都经过精心设计,紧凑而直观,训练后修剪非线性激活函数,以恢复原始架构[T5]。

  2. 网络架构:VanillaNet的架构包括三个主要部分:干细胞块(stem block)、主体和全连接层。主体通常包括四个阶段,每个阶段由堆叠相同的块构成。在每个阶段之后,特征的通道会扩展,而高度和宽度会减小[T3]。

  3. 训练策略:为了训练VanillaNet,研究人员进行了全面分析,针对简化的架构设计了“深度训练”策略。该策略从包含非线性激活函数的几层开始,随着训练的进行,逐渐消除这些非线性层,从而实现易于合并并保持推理速度。为增强网络的非线性,提出了一种高效的基于序列的激活函数,包含多个可学习的仿射变换[T4]。

  4. 实时处理性能:VanillaNet在实时处理方面表现出色,尤其是在图像分类任务中。通过调整通道数和池化大小,VanillaNet-13-1.5×在ImageNet上实现了83.11%的Top-1准确率,显示出VanillaNet在大规模图像分类任务中仍然具有强大的性能[T2]。

核心代码


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)

    def change_act(self, m):
        for i in range(self.depth):
            self.stages[i].act_learn = m
        self.act_learn = m

    def forward(self, x):
        if self.deploy:
            x = self.stem(x)
        else:
            x = self.stem1(x)
            x = torch.nn.functional.leaky_relu(x,self.act_learn)
            x = self.stem2(x)

        for i in range(self.depth):
            x = self.stages[i](x)

        if self.deploy:
            x = self.cls(x)
        else:
            x = self.cls1(x)
            x = torch.nn.functional.leaky_relu(x,self.act_learn)
            x = self.cls2(x)
        return x.view(x.size(0),-1)

    def _fuse_bn_tensor(self, conv, bn):
        kernel = conv.weight
        bias = conv.bias
        running_mean = bn.running_mean
        running_var = bn.running_var
        gamma = bn.weight
        beta = bn.bias
        eps = bn.eps
        std = (running_var + eps).sqrt()
        t = (gamma / std).reshape(-1, 1, 1, 1)
        return kernel * t, beta + (bias - running_mean) * gamma / std

    def switch_to_deploy(self):
        self.stem2[2].switch_to_deploy()
        kernel, bias = self._fuse_bn_tensor(self.stem1[0], self.stem1[1])
        self.stem1[0].weight.data = kernel
        self.stem1[0].bias.data = bias
        kernel, bias = self._fuse_bn_tensor(self.stem2[0], self.stem2[1])
        self.stem1[0].weight.data = torch.einsum('oi,icjk->ocjk', kernel.squeeze(3).squeeze(2), self.stem1[0].weight.data)
        self.stem1[0].bias.data = bias + (self.stem1[0].bias.data.view(1,-1,1,1)*kernel).sum(3).sum(2).sum(1)
        self.stem = torch.nn.Sequential(*[self.stem1[0], self.stem2[2]])
        self.__delattr__('stem1')
        self.__delattr__('stem2')

        for i in range(self.depth):
            self.stages[i].switch_to_deploy()

        kernel, bias = self._fuse_bn_tensor(self.cls1[2], self.cls1[3])
        self.cls1[2].weight.data = kernel
        self.cls1[2].bias.data = bias
        kernel, bias = self.cls2[0].weight.data, self.cls2[0].bias.data
        self.cls1[2].weight.data = torch.matmul(kernel.transpose(1,3), self.cls1[2].weight.data.squeeze(3).squeeze(2)).transpose(1,3)
        self.cls1[2].bias.data = bias + (self.cls1[2].bias.data.view(1,-1,1,1)*kernel).sum(3).sum(2).sum(1)
        self.cls = torch.nn.Sequential(*self.cls1[0:3])
        self.__delattr__('cls1')
        self.__delattr__('cls2')
        self.deploy = True

task与yaml配置

详见:https://blog.csdn.net/shangyanaf/article/details/139665923

相关文章
|
4月前
|
编解码 Go 文件存储
【YOLOv8改进 - 特征融合NECK】 DAMO-YOLO之RepGFPN :实时目标检测的创新型特征金字塔网络
【YOLOv8改进 - 特征融合NECK】 DAMO-YOLO之RepGFPN :实时目标检测的创新型特征金字塔网络
|
4月前
|
机器学习/深度学习 自然语言处理 计算机视觉
【YOLOv8改进 - Backbone主干】VanillaNet:极简的神经网络,利用VanillaBlock降低YOLOV8参数
【YOLOv8改进 - Backbone主干】VanillaNet:极简的神经网络,利用VanillaBlock降低YOLOV8参数
|
1月前
|
算法 计算机视觉 Python
YOLOv8优改系列二:YOLOv8融合ATSS标签分配策略,实现网络快速涨点
本文介绍了如何将ATSS标签分配策略融合到YOLOv8中,以提升目标检测网络的性能。通过修改损失文件、创建ATSS模块文件和调整训练代码,实现了网络的快速涨点。ATSS通过自动选择正负样本,避免了人工设定阈值,提高了模型效率。文章还提供了遇到问题的解决方案,如模块载入和环境配置问题。
76 0
YOLOv8优改系列二:YOLOv8融合ATSS标签分配策略,实现网络快速涨点
|
1月前
|
机器学习/深度学习 计算机视觉 异构计算
YOLOv8优改系列一:YOLOv8融合BiFPN网络,实现网络快速涨点
本文介绍了将BiFPN网络应用于YOLOv8以增强网络性能的方法。通过双向跨尺度连接和加权特征融合,BiFPN能有效捕获多尺度特征,提高目标检测效果。文章还提供了详细的代码修改步骤,包括修改配置文件、创建模块文件、修改训练代码等,以实现YOLOv8与BiFPN的融合。
111 0
YOLOv8优改系列一:YOLOv8融合BiFPN网络,实现网络快速涨点
|
1月前
|
机器学习/深度学习 计算机视觉 异构计算
YOLOv8优改系列一:YOLOv8融合BiFPN网络,实现网络快速涨点
该专栏专注于YOLOv8的 Neck 部分改进,融合了 BiFPN 网络,大幅提升检测性能。BiFPN 通过高效的双向跨尺度连接和加权特征融合,解决了传统 FPN 的单向信息流限制。文章详细介绍了 BiFPN 的原理及其实现方法,并提供了核心代码修改指导。点击链接订阅专栏,每周定时更新,助您快速提升模型效果。推荐指数:⭐️⭐️⭐️⭐️,涨点指数:⭐️⭐️⭐️⭐️。
112 0
|
4月前
|
机器学习/深度学习 计算机视觉
【YOLOv8改进 - 注意力机制】c2f结合CBAM:针对卷积神经网络(CNN)设计的新型注意力机制
【YOLOv8改进 - 注意力机制】c2f结合CBAM:针对卷积神经网络(CNN)设计的新型注意力机制
|
5月前
|
机器学习/深度学习 PyTorch 算法框架/工具
【从零开始学习深度学习】26.卷积神经网络之AlexNet模型介绍及其Pytorch实现【含完整代码】
【从零开始学习深度学习】26.卷积神经网络之AlexNet模型介绍及其Pytorch实现【含完整代码】
|
5月前
|
机器学习/深度学习 PyTorch 算法框架/工具
【从零开始学习深度学习】28.卷积神经网络之NiN模型介绍及其Pytorch实现【含完整代码】
【从零开始学习深度学习】28.卷积神经网络之NiN模型介绍及其Pytorch实现【含完整代码】
|
3月前
|
机器学习/深度学习 PyTorch 算法框架/工具
PyTorch代码实现神经网络
这段代码示例展示了如何在PyTorch中构建一个基础的卷积神经网络(CNN)。该网络包括两个卷积层,分别用于提取图像特征,每个卷积层后跟一个池化层以降低空间维度;之后是三个全连接层,用于分类输出。此结构适用于图像识别任务,并可根据具体应用调整参数与层数。
|
3月前
|
机器学习/深度学习 数据可视化 Python
如何可视化神经网络的神经元节点之间的连接?附有Python预处理代码
该博客展示了如何通过Python预处理神经网络权重矩阵并将其导出为表格,然后使用Chiplot网站来可视化神经网络的神经元节点之间的连接。
56 0
如何可视化神经网络的神经元节点之间的连接?附有Python预处理代码

热门文章

最新文章