试着了解YOLOx

news2025/1/21 15:29:37

在特征提取上来说,主干部分使用了focus网络结构,对特征点进行了划分,将特征点信息堆叠到通道上。

同时采用CSPnet结构,在残差网络堆叠的同时,构建大的残差边,经过少量处理直接连接到最后。

过去的YOLO将分类和回归放在一个1*1的卷积中实现,YOLOx认为这给网络的识别带来了很多的不利,在YOLOx中,YOLOHead被划分成了两个部分,分别实现,最后预测的时候将这两个部分结合到一起。

数据增强方面,使用了Mosaic数据增强方法。利用四张图片进行拼接来实现数据中的数据增强。优点是可以用来丰富被检测物体背景。

Anchor Free:基础的Anchor检测器需要对先验框进行聚类,需要很多时间成本。YOLOx中使用的是Anchor Free,解码逻辑更加简单,可读性也强很多。

SimOTA动态这正样本匹配:根据每个真实框与当前各个特征点的预测框的重合程度,计算每个真实框的k,代表每个真实框有k个特征点与之对应。根据真实框和各个特征点的预测准确度和包含情况计算Cost代价矩阵。将Cost最低的k个特征点作为该真实框的正样本。

最重要的部分来了,也就是网络结构:

关注YOLOHead部分,将预测分为两部分,左侧的部分预测的是物体的种类,右侧的部分预测的是是否包含物体以及是否特征点的回归系数(预测框的坐标)。

YOLOx使用到了SiLU激活函数,该激活函数是ReLU和Sigmoid的改进版,可以看做是一个平滑的ReLU激活函数。

使用了SPP结构,通过不同池化核大小的最大池化进行特征提取,提高网络的感受野,在YOLOV4中,SPP结构是用在FPN里面的,在YOLOX中,使用到主干特征提取网络里面的。

(小记一下SPP:

传统的CNN网络结构要求输入图像的尺寸是固定的,所以在训练和推理阶段所有输入的图像都必须经过调整到相同的尺寸,但这个过程必定导致损失部分信息。

SPP网络允许接收不同尺寸的图像,核心思想是通过多层的池化操作来提取不同尺度的特征。

小记一下FPN:

FPN通过构建特征金字塔,有效的利用多尺度特征。包括:主干网络,自底向上的特征提取,,自顶向下的特征融合。

改进的点:SiLU在深层模型上是优于ReLU的,以后在多层神经网络结构中遇到ReLU可以使用SiLU替换试一试。

总结而言,在head部分,将之前的共享同一个卷积层用来预测类别置信度和位置改为了分别用不同的卷积层来预测。使用的是anchor-free网络。损失计算上面采用的是分类损失+置信度损失+5*定位损失 的和除以被分为正样本的Anchor Point数量。正负样本匹配策略采用SIMOTA,我的理解就是(对于每一个GT,算出对于所有的Anchor Point计算出cost和IOU,根据IOU来计算选择的正样本数,根据cost来选择选取哪些样本作为正样本)。

对yolox的官方代码进行以下讲解:(只关注models部分和loss部分)

首先对于注意力部分,以CAM为例:

定义Mish激活函数,该激活函数是一个相对较新的激活函数,可以看做是ReLU的sigmoid版本,也就是平滑的ReLU函数,在很多网络中表现优于ReLU以及Swish,改进的时候可以试试。

再者就是CAM注意力模块儿,首先将输入的向量进行全局的池化,拼接过程中伴随着通道数的缩小(减少计算量),将得到的结果进行mish处理,然后再做逆向切分,将得到的两个部分都使用sigmoid激活函数的处理,处理后的结果扩展为x的形状,与x做逐元素相乘得到返回的结果。

上代码:

import torch
import torch.nn as nn
import torch.nn.functional as F
class Mish(nn.Module):
    """_summary_

    Args:
        nn (_type_): _description_
    一个相对较新的激活函数,公式是Mish(x)=x*tanh(ln(1+exp(x))))
    优势是平滑且可以训练,但速度较慢。
    在YOLOv5中,使用Mish激活函数代替了ReLU,以获得更好的性能。
    在很多深度学习任务中,表现优于RelU以及Swish,可以用它来试着改进。
    """
    def __init__(self):
        super().__init__()
        print("Mish activation loaded...")
    def forward(self,x):
        x = x * (torch.tanh(F.softplus(x)))
        return x
class CAM(nn.Module):
    """_summary_

    Args:
        nn (_type_): _description_
    
    先将输入特征层进行全局平均池化,后拼接。再进行一个1*1的卷积缩小通道数。使用Mish激活函数来处理。
    将处理后的结果再进行两部分的切分,对切分的结果进行激活处理。切分后的结果再进行通道数的扩张
    将得到的结果扩张到x的形状然后将张量x与扩展后的s_h,s_w逐元素相乘得到结果
    """
    def __init__(self, channels, reduction=32):
        super(CAM, self).__init__()
        self.conv_1x1 = nn.Conv2d(in_channels=channels, out_channels=channels // reduction, kernel_size=1, stride=1,
                                  bias=False)                        
        self.mish = Mish()
        self.bn = nn.BatchNorm2d(channels // reduction)  
        self.F_h = nn.Conv2d(in_channels=channels // reduction, out_channels=channels, kernel_size=1, stride=1, bias=False)
        self.F_w = nn.Conv2d(in_channels=channels // reduction, out_channels=channels, kernel_size=1, stride=1, bias=False)  
        self.sigmoid_h = nn.Sigmoid() 
        self.sigmoid_w = nn.Sigmoid()
 
    def forward(self, x):
        h, w = x.shape[2], x.shape[3]
        #作用是根据输入特征图的大小自动计算池化窗口和步幅,确保输出的特征图大小满足(h,1)
        avg_pool_x = nn.AdaptiveAvgPool2d((h, 1))
        avg_pool_y = nn.AdaptiveAvgPool2d((1, w))
        x_h = avg_pool_x(x).permute(0, 1, 3, 2) 
        x_w = avg_pool_y(x)  
        x_cat_conv_relu = self.mish(self.conv_1x1(torch.cat((x_h, x_w), 3))) #拼接后的形状是(batch_size,channels,1,w+h)
        x_cat_conv_split_h, x_cat_conv_split_w = x_cat_conv_relu.split([h, w], 3)
        s_h = self.sigmoid_h(self.F_h(x_cat_conv_split_h.permute(0, 1, 3, 2)))
        s_w = self.sigmoid_w(self.F_w(x_cat_conv_split_w))
        out = x * s_h.expand_as(x) * s_w.expand_as(x)
        return out

SiLU激活函数的图像如下:

另一些层的源代码,加上注释后如下:

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# Copyright (c) Megvii Inc. All rights reserved.

import torch
import torch.nn as nn


class SiLU(nn.Module):
    """
    SilU激活函数其实也就是Swish函数,x = x * sigmoid(x)
    """
    """export-friendly version of nn.SiLU()"""

    @staticmethod
    def forward(x):
        return x * torch.sigmoid(x)


def get_activation(name="silu", inplace=True):
    if name == "silu":
        module = nn.SiLU(inplace=inplace)
    elif name == "relu":
        module = nn.ReLU(inplace=inplace)
    elif name == "lrelu":
        module = nn.LeakyReLU(0.1, inplace=inplace)
    else:
        raise AttributeError("Unsupported act type: {}".format(name))
    return module


class BaseConv(nn.Module):
    """A Conv2d -> Batchnorm -> silu/leaky relu block
    groups表示将输入通道分为groups个组,分别对每一组进行卷积操作,卷积后的结果进行拼接得到输出结果
    经输入进行二维卷积,输出做归一化处理然后激活
    简单来讲就是卷积+归一化+激活
    其中fuseforward不对输出进行归一化处理,而是直接激活
    """

    def __init__(
        self, in_channels, out_channels, ksize, stride, groups=1, bias=False, act="silu"
    ):
        super().__init__()
        # same padding
        pad = (ksize - 1) // 2
        self.conv = nn.Conv2d(
            in_channels,
            out_channels,
            kernel_size=ksize,
            stride=stride,
            padding=pad,
            groups=groups,
            bias=bias,
        )
        #表示对out_channels个通道进行归一化处理
        self.bn = nn.BatchNorm2d(out_channels)
        self.act = get_activation(act, inplace=True)

    def forward(self, x):
        return self.act(self.bn(self.conv(x)))

    def fuseforward(self, x):
        return self.act(self.conv(x))


class DWConv(nn.Module):
    """Depthwise Conv + Conv
    对输入进行卷积+归一化+激活(此时不修改通道数),得到的结果再进行卷积(1*1,用于修改通道数)+
    归一化+激活
    相当于重复了两个BaseConv
    """

    def __init__(self, in_channels, out_channels, ksize, stride=1, act="silu"):
        super().__init__()
        self.dconv = BaseConv(
            in_channels,
            in_channels,
            ksize=ksize,
            stride=stride,
            groups=in_channels,
            act=act,
        )
        self.pconv = BaseConv(
            in_channels, out_channels, ksize=1, stride=1, groups=1, act=act
        )

    def forward(self, x):
        x = self.dconv(x)
        return self.pconv(x)


class Bottleneck(nn.Module):
    """_summary_

    Args:
        nn (_type_): _description_

    Returns:
        _type_: _description_
    隐藏层的作用是为了减少计算量
    根据参数来选择是否使用深度卷积,对输入的特征层首先进行基础卷积,然后选择性使用深度卷积
    通过参数选择是否进行残差连接
    """
    # Standard bottleneck
    def __init__(
        self,
        in_channels,
        out_channels,
        shortcut=True,
        expansion=0.5,
        depthwise=False,
        act="silu",
    ):
        super().__init__()
        hidden_channels = int(out_channels * expansion)
        Conv = DWConv if depthwise else BaseConv#根据depthwise参数来选择是使用基础卷积还是深度卷积(基础卷积*2)
        self.conv1 = BaseConv(in_channels, hidden_channels, 1, stride=1, act=act)#在Base_Conv中有定义padding,保证处理后特征层的形状不发生改变
        self.conv2 = Conv(hidden_channels, out_channels, 3, stride=1, act=act)
        self.use_add = shortcut and in_channels == out_channels

    def forward(self, x):
        y = self.conv2(self.conv1(x))
        if self.use_add:
            y = y + x
        return y


class ResLayer(nn.Module):
    "Residual layer with `in_channels` inputs."
    """
    残差层对输入使用两个卷积层(通过隐藏层来减小计算量)
    将输入经过两个卷积层得到的结果与原始输入进行相加
    """

    def __init__(self, in_channels: int):
        super().__init__()
        mid_channels = in_channels // 2
        self.layer1 = BaseConv(
            in_channels, mid_channels, ksize=1, stride=1, act="lrelu"
        )
        self.layer2 = BaseConv(
            mid_channels, in_channels, ksize=3, stride=1, act="lrelu"
        )

    def forward(self, x):
        out = self.layer2(self.layer1(x))
        return x + out


class SPPBottleneck(nn.Module):
    """Spatial pyramid pooling layer used in YOLOv3-SPP"""
    """
    首先将输入送到一个卷积层,然后使用三个池化层,每个池化层的核大小为5,9,13,
    然后对三个池化层得到的结果进行拼接,加上原始输入通过第一个卷积层得到的结果,再送入一个卷积层
    俗称空间金字塔(包含两个卷积层和多个池化层)
    """

    def __init__(
        self, in_channels, out_channels, kernel_sizes=(5, 9, 13), activation="silu"
    ):
        super().__init__()
        hidden_channels = in_channels // 2
        self.conv1 = BaseConv(in_channels, hidden_channels, 1, stride=1, act=activation)
        self.m = nn.ModuleList(
            [
                #padding=ks//2的作用是保证输入和输出的空间尺寸一致
                nn.MaxPool2d(kernel_size=ks, stride=1, padding=ks // 2)
                for ks in kernel_sizes
            ]
        )
        conv2_channels = hidden_channels * (len(kernel_sizes) + 1)
        self.conv2 = BaseConv(conv2_channels, out_channels, 1, stride=1, act=activation)

    def forward(self, x):
        x = self.conv1(x)
        x = torch.cat([x] + [m(x) for m in self.m], dim=1)
        x = self.conv2(x)
        return x


class CSPLayer(nn.Module):
    """C3 in yolov5, CSP Bottleneck with 3 convolutions"""

    """
    将原始输入分别进入两个基础卷积,得到结果1和2,对1进行n个深层卷积,得到的结果与2拼接,拼接的结果进入另一个卷积层
    """
    def __init__(
        self,
        in_channels,
        out_channels,
        n=1,
        shortcut=True,
        expansion=0.5,
        depthwise=False,
        act="silu",
    ):
        """
        Args:
            in_channels (int): input channels.
            out_channels (int): output channels.
            n (int): number of Bottlenecks. Default value: 1.
        """
        # ch_in, ch_out, number, shortcut, groups, expansion
        super().__init__()
        hidden_channels = int(out_channels * expansion)  # hidden channels
        self.conv1 = BaseConv(in_channels, hidden_channels, 1, stride=1, act=act)
        self.conv2 = BaseConv(in_channels, hidden_channels, 1, stride=1, act=act)
        self.conv3 = BaseConv(2 * hidden_channels, out_channels, 1, stride=1, act=act)
        module_list = [#选择使用n个深层残差网络形成空间金字塔
            Bottleneck(
                hidden_channels, hidden_channels, shortcut, 1.0, depthwise, act=act
            )
            for _ in range(n)
        ]
        #Sequential是一个容器模块,将多个子模块儿按顺序组合在一起,为了更加清晰
        self.m = nn.Sequential(*module_list)

    def forward(self, x):
        x_1 = self.conv1(x)
        x_2 = self.conv2(x)
        x_1 = self.m(x_1)
        x = torch.cat((x_1, x_2), dim=1)
        return self.conv3(x)


class Focus(nn.Module):
    """Focus width and height information into channel space."""
    """
    对图像进行间隔切片后对四个区域进行通道维度的拼接,再送入一个卷积层,得到的结果是形状上变为原来的1/4
    """
    def __init__(self, in_channels, out_channels, ksize=1, stride=1, act="silu"):
        super().__init__()
        self.conv = BaseConv(in_channels * 4, out_channels, ksize, stride, act=act)

    def forward(self, x):
        """
        切片操作:以第一个为例,前两个维度不变,从第三个维度开始,在该维度上每隔两个元素去一个点
        第二行表示,每隔一定的元素数量取点的时候,索引从1开始(default表示从0开始)

        Args:
            x (_type_): _description_

        Returns:
            _type_: _description_
        """
        # shape of x (b,c,w,h) -> y(b,4c,w/2,h/2)
        patch_top_left = x[..., ::2, ::2]
        patch_top_right = x[..., ::2, 1::2]
        patch_bot_left = x[..., 1::2, ::2]
        patch_bot_right = x[..., 1::2, 1::2]
        x = torch.cat(
            (
                patch_top_left,
                patch_bot_left,
                patch_top_right,
                patch_bot_right,
            ),
            dim=1,
        )
        return self.conv(x)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2213543.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据库系统概论之关系数据库标准语言SQL(一)【超详细】

教材: 数据库系统概论(第6版)王珊,杜小勇,陈红编著 目录 一、SQL概述 1.1 SQL 的产生与发展 1.2 SQL的特点 1.3 SQL的基本概念 二、数据定义 2.1 数据库的定义 2.2 数据表的定义 2.3 模式的定义 一、SQL概述 1974年IBM为关系DBMS设…

组合式二值编码

论文名称:《A Practical Approach to 3D Scanning in the Presence ofInterreflections, Subsurface Scattering and Defocus》 简介:组合式二值编码(包含传统格雷码,XOR-02,XOR-04,minSW)&…

Java学习Day25:基础篇15:反射

Java 反射(Reflection) 1.前置反应 是 Java 编程语言的一个强大特性,它允许程序在运行时检查或修改类的行为。这包括获取类的信息(如字段、方法、构造函数等),以及动态地创建对象、调用方法、访问和修改字…

linux的学习第二天

1.vmware的功能: 快照 创建快照: 拍摄此虚拟机的快照:记录保存虚拟机的当前状态,如果系统出现故障,可以通过快照还原(错删系统时可以找到快照的系统状态,然后恢复系统) 恢复快照…

java项目之精品在线试题库系统设计与实现源码(springboot+vue+mysql)

风定落花生,歌声逐流水,大家好我是风歌,混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的精品在线试题库系统设计与实现。项目源码以及部署相关请联系风歌,文末附上联系信息 。 项目简介: 精品在线试…

sentinel dashboard分布式改造落地设计实现解释(二)-分布式discovery组件

discovery discovery负责维护app/机器资料库,transport健康检测, transport上下线处理。discovery关键是分布式存储,后续研究一下raft,其复制,状态机,快照技术,但个人觉得,discover…

软件分享 | 截图工具 Snipaste

今天分享: 今日分享的是一款截图界的神器——Snipaste! 特点: 🎨 超清截图: Snipaste的截图功能,清晰度超乎你的想象。无论是工作文档还是游戏画面,都能一键捕捉,细节尽在掌握。 …

Excel:将一列拆分成多列

实现的效果是: 操作步骤如下: 1.选中列 → 点击菜单栏中的"数据" → 分列 2.选择"分列符号",点击下一步 3.我想要按照空格分列,就选择空格 4.点击完成,就可以实现分列的效果了

uniapp学习(004-2 组件 Part.2生命周期)

零基础入门uniapp Vue3组合式API版本到咸虾米壁纸项目实战,开发打包微信小程序、抖音小程序、H5、安卓APP客户端等 总时长 23:40:00 共116P 此文章包含第31p-第p35的内容 文章目录 组件生命周期我们主要使用的三种生命周期setup(创建组件时执行)不可以操作dom节点…

Shell编程-if判断

作者介绍:简历上没有一个精通的运维工程师。请点击上方的蓝色《运维小路》关注我,下面的思维导图也是预计更新的内容和当前进度(不定时更新)。 我们前面学习了那么多命令,以及涉及到部分逻辑判断的问题。从简单来说,他就是Shell编…

【XYFrame unity框架使用文档】封装unity小框架工具集 —— XYFrame

文章目录 XYFrame介绍优点获取框架源码引入的第三方插件作者信息技术交流群反馈企鹅裙画饼使用文档导入文件目录启动1、单例模式不继承MonoBehaviour的单例模式基类继承MonoBehaviour的单例模式基类 2、Mono管理器3、事件管理系统4、工具类封装unity协程工具,避免 G…

每周心赏|必备AI神器第二弹

大家的假期都是怎么度过的?是已经玩嗨了?还是在家葛优躺,感叹时间飞逝呢? 别急,假期还没完全说拜拜呢!赶紧抓住假期最后的尾巴,和AI神器一起,把快乐放大,不留遗憾&#x…

prompt learning

prompt learning 对于CLIP(如上图所示)而言,对其prompt构造的更改就是在zero shot应用到下游任务的时候对其输入的label text进行一定的更改,比如将“A photo of a{obj}”改为“[V1][V2]…[Vn][Class]”这样可学习的V1-Vn的token…

Docker配置网站环境

Mysql 先安装mysql 启动并后台运行:run -d 容器名称:--name 设置端口映射:-p 主机端口:容器端口 环境变量:-e 最后指定镜像名称 sudo docker run -d \--name mysql\-p 3306:3306\-e MYSQL_ROOT_PASSWORD123456\…

Microsoft SQL Server 2008 R2 (RTM) - 10.50.1600.0 SP1升级到SP3操作方法(x64)

1、首先安装时候选择升级SQLEXPRADV_x64_CHS.exe。 2、接着安装SQLServer2008R2SP1-KB2528583-x64-sp1补丁后10.50.2500.0。 3、接着安装升级SQLEXPRWT_x64_CHS.exe。 4、继续安装SP3:SQLServer2008R2SP3-KB2979597-x64-CHS。 5、最后安装SP3补丁:SQ…

ARM64使能kdump

摘要 需要使用的工具或者配置如下: 使用kdump-defconfig编译后的kdump内核 : https://download.csdn.net/download/weixin_43412488/89886775https://download.csdn.net/download/weixin_43412488/89886775 引导kdump内核加载的ramdisk: https://download.csdn.net/dow…

【通知】红帽认证:RHCE免费补考福利来袭!

现有2024年第四季度红帽RHCE培训补考政策的通知: 为促进红帽第四季度(10月~12月)RHCE的招生及交付,红帽推出免费补考政策如下: 在此期间参加RHCE(EX200/EX294)考试,且考试订单在有效期内,考试如有未能通过的科目&…

移动技术开发:备忘录

1 实验名称 备忘录 2 实验目的 掌握SQLite数据库的基本操作&#xff0c;实现备忘录基本功能。 3 实验源代码 布局文件代码&#xff1a; &#xff08;1&#xff09;activity_main.xml <?xml version"1.0" encoding"utf-8"?> <LinearLayout …

在IMX6ul中,使用GPT定时器实现高精度延时

在上一节讲解过了。IMX6UL中的EPIT定时器&#xff0c;这一节我们讲解通用寄存器 在STM32中&#xff0c;我们使用过SYSTICK来实现高精度的延时。IMX6U当中没有SYSTICK定时器&#xff0c;但是IMX6U有其他的定时器&#xff0c;前面的EPIT以及这一节我们将要使用的GPT定时器…

算法备案必须做吗?不做有什么风险?

算法备案是一种强制性备案制度&#xff0c;旨在保障算法技术的合法性和合规性。 《互联网信息服务算法推荐管理规定》第二十四条明确规定应当在10个工作日内备案&#xff0c;发生变更的在10个工作日内完成变更&#xff0c;注销的在20个工作日内完成注销。 ​未履行备案的&…