RT-DRET在实时目标检测上超越YOLO8

在这里插入图片描述导读
目标检测作为计算机视觉的核心任务之一,其研究已经从基于CNN的架构发展到基于Transformer的架构,如DETR,后者通过简化流程实现端到端检测,消除了手工设计的组件。尽管如此,DETR的高计算成本限制了其在实时目标检测领域的应用。为了解决这一问题,研究人员通过设计高效的编码器和改进查询初始化方法,优化了DETR,使其能够适应实时场景,同时避免了NMS带来的延迟,推动了目标检测技术在速度和准确性上的进一步提升。

复杂场景下的检测能力图示:
在这里插入图片描述

文章目录

  • 摘要
  • 一、介绍
  • 二、目标检测器相关工作
    • 1.从DETR到实时检测的创新
    • 2.利用多尺度特征提高性能
  • 三、端到端速度的优势
    • 1.对NMS的分析
    • 2.Anchor-Free检测其在实时推理中的优势
  • 四、The Real-time DETR
    • 1.RT-DETR的结构
    • 2.混合编码器
  • 实验
    • 1.与YOLO的比较
    • 1.不同模块的消融研究


摘要

在这项工作中,作者提出了一个名为RT-DETR的实时端到端检测器,它成功地将DETR扩展到实时检测场景,并实现了最先进的性能。RT-DETR包括两个关键增强:一个高效的混合编码器,它可以迅速处理多尺度特征,以及最小化不确定性的查询选择,这提高了初始对象查询的质量。此外,RT-DETR支持灵活的速度调整,无需重新训练,并消除了由两个NMS阈值引起的不便,从而促进了其实际应用。RT-DETR及其模型扩展策略拓宽了实时目标检测的技术方法,为YOLO之外的多样化实时场景提供了新的可能性。

一、介绍

本文深入分析了现代实时目标检测器中非极大值抑制(NMS)对推理速度的负面影响,并提出了一种新型的Transformer架构——实时DEtection TRansformer(RT-DETR),它通过简化目标检测流程实现了端到端的检测。尽管如此,DETR的高计算成本问题仍然是其在实际应用中的一个限制。为了克服这一挑战,文中引入了IoU-Aware查询选择器,该方法在训练阶段利用IoU约束来优化解码器的初始目标查询,从而提供更高质量的查询,进一步提升了检测性能。
在这里插入图片描述
本文提出了一种新型的实时目标检测框架RT-DETR,旨在解决现有基于Transformer的目标检测器在计算成本和推理速度上的挑战。文中展示了RT-DETR-R50和RT-DETR-R101两种变体的性能:RT-DETR-R50实现了53.1%的平均精度(AP)和108帧每秒(FPS),而更高级的RT-DETR-R101变体则实现了54.3%的AP和74 FPS。特别值得一提的是,RT-DETR-R50在准确度和帧率上均优于DINO-Deformable-DETR-R50,显示出在实时目标检测任务中的优势。这些结果表明,RT-DETR在维持高准确度的同时,还能提供高帧率的检测,满足实时处理的需求。

二、目标检测器相关工作

1.从DETR到实时检测的创新

端到端目标检测器DETR(DEtection Transformer)以其简化的检测流程而备受关注,它通过二分匹配直接预测对象,避免了传统检测的Anchor和NMS组件。但是,DETR仍然存在训练收敛缓慢和查询优化难度大的问题。为了解决这些问题,人们提出了很多种DETR变体,比如Deformable DETR和Conditional DERT以及DAB-DETR等等,它们通过不同的方法进行优化效率和查询。DINO以之前的作品为基础,不断完善已经取得了巨大的成果。

2.利用多尺度特征提高性能

现代目标检测器通过使用多尺度特征,特别是小物体,已经有了显著的性能提升。FPN(特征金字塔网络)是实现这一目标的关键技术,通过融合不同尺度的特征来构建特征金字塔。而在DERT(DEtection Transformer)中,zhu等人首次引入了多尺度的特征,虽然提高了性能检测和模型收敛的速度,但是也增加了计算成本。即使后面的Deformable Attention在在一定程度上降低了这一成本,但是高计算成本的问题还是没有得到解决。
为了应对这些问题。一些研究高效DETR模型的团队设计了不同的DETR变体,如Efficient DETR通过优化目标查询的初始化来减少过程中解码和编码器的数量,来降低成本。还有Lite DETR通过减少级别特征的更新频率来提升编码器效率。

三、端到端速度的优势

1.对NMS的分析

NMS(非极大值抑制)是一种用于去除重叠预测框的常用后处理技术。它依赖于两个关键的超参数:得分阈值和loU(交并比)阈值。
具体操作中,首先过滤得分低于设定阈值的预测框。然后,对于剩余的预测框,如果俩个框之间的loU超过设定的loU阈值,那么得分低的框会被舍弃。这个过程会一直重复执行直到所有类别的预测框都被执行处理完成。所以NMS执行的时间主要是看,预测框的数量和俩个超参数。
在这里插入图片描述
本图就是通过直方图的形式展示在相同输入图像下不同阈值下剩余的预测框的数量,此外还对YOLOv8进行不同NMS超参数下在COCO val2017的模型的准确性和时间,下图所示:
在这里插入图片描述

2.Anchor-Free检测其在实时推理中的优势

作者为了减少外在因素的影响建立了一个端到端的测试基准,并用COCO val2017作为默认数据集,同时也为后处理的实时检测器添加了TensorRT的NMS后处理插件。
在这里插入图片描述
根据数据可以看出来RT-DETR检测器在精度上要优于YOLO检测器。而出现这种情况也是因为Anchor-Free检测器上的预测框比Anchor-Base检测器多的多。

四、The Real-time DETR

1.RT-DETR的结构

RT-DETR是由混合编码器和Backbone以及辅助预测头Transformer解码器组成。具体如下图所示:
在这里插入图片描述

2.混合编码器

作者等人为了加速训练收敛并提高性能提出了一种混合高效的混合编码器,混合编码器主要是俩个模块组成,即注意力的尺度内特征交互(AIFI)模块和神经网络的尺度特征融合模块(CCFM)。
在这里插入图片描述

CCFM也基于变体D进行完善,如上图所示,也可以通过公式表达出来:
在这里插入图片描述
然而无论是Effificient detr和Dino以及Deformable detr的查询方案的哦是以Top-K个特征来初始化查询,导致探测器的性能削弱。为此作者提出了loU-Aware查询选择,它能为不同得分产生不同的分数线,具体的表示如下:
在这里插入图片描述
为了证明loU感知查询的可靠性,进行了如图所示的实验:
在这里插入图片描述
实验通过可视化和定量分析表明,使用IoU感知查询选择训练的模型(蓝色点)在生成高质量编码器特征方面优于普通查询选择训练的模型(红色点),这体现在蓝色点在散点图右上角的集中分布,以及在分类得分和IoU得分大于0.5时,蓝色点数量显著多于红色点,分别高出138%和120%。进而表示loU感知查询选择可以为对象查询提供更多具有准确分类和精确定位的编码器特征,从而提高检测器的准确性。

源码链接:hybrid_encoder

@register
@serializable
class HybridEncoder(nn.Layer):
    __shared__ = ['depth_mult', 'act', 'trt', 'eval_size']
    __inject__ = ['encoder_layer']

#初始化定义
    def __init__(self,
                 in_channels=[512, 1024, 2048],
                 feat_strides=[8, 16, 32],
                 hidden_dim=256,
                 use_encoder_idx=[2],
                 num_encoder_layers=1,
                 encoder_layer='TransformerLayer',
                 pe_temperature=10000,
                 expansion=1.0,
                 depth_mult=1.0,
                 act='silu',
                 trt=False,
                 eval_size=None):
        super(HybridEncoder, self).__init__()
        self.in_channels = in_channels
        self.feat_strides = feat_strides
        self.hidden_dim = hidden_dim
        self.use_encoder_idx = use_encoder_idx
        self.num_encoder_layers = num_encoder_layers
        self.pe_temperature = pe_temperature
        self.eval_size = eval_size

        # channel projection
        self.input_proj = nn.LayerList()
        for in_channel in in_channels:
            self.input_proj.append(
                nn.Sequential(
                    nn.Conv2D(
                        in_channel, hidden_dim, kernel_size=1, bias_attr=False),
                    nn.BatchNorm2D(
                        hidden_dim,
                        weight_attr=ParamAttr(regularizer=L2Decay(0.0)),
                        bias_attr=ParamAttr(regularizer=L2Decay(0.0)))))
        # encoder transformer
        self.encoder = nn.LayerList([
            TransformerEncoder(encoder_layer, num_encoder_layers)
            for _ in range(len(use_encoder_idx))
        ])

        act = get_act_fn(
            act, trt=trt) if act is None or isinstance(act,
                                                       (str, dict)) else act
        # top-down fpn
        self.lateral_convs = nn.LayerList()
        self.fpn_blocks = nn.LayerList()
        for idx in range(len(in_channels) - 1, 0, -1):
            self.lateral_convs.append(
                BaseConv(
                    hidden_dim, hidden_dim, 1, 1, act=act))
            self.fpn_blocks.append(
                CSPRepLayer(
                    hidden_dim * 2,
                    hidden_dim,
                    round(3 * depth_mult),
                    act=act,
                    expansion=expansion))

        # bottom-up pan
        self.downsample_convs = nn.LayerList()
        self.pan_blocks = nn.LayerList()
        for idx in range(len(in_channels) - 1):
            self.downsample_convs.append(
                BaseConv(
                    hidden_dim, hidden_dim, 3, stride=2, act=act))
            self.pan_blocks.append(
                CSPRepLayer(
                    hidden_dim * 2,
                    hidden_dim,
                    round(3 * depth_mult),
                    act=act,
                    expansion=expansion))

        self._reset_parameters()

    def _reset_parameters(self):
        if self.eval_size:
            for idx in self.use_encoder_idx:
                stride = self.feat_strides[idx]
                pos_embed = self.build_2d_sincos_position_embedding(
                    self.eval_size[1] // stride, self.eval_size[0] // stride,
                    self.hidden_dim, self.pe_temperature)
                setattr(self, f'pos_embed{idx}', pos_embed)

    #添加位置信息;
    @staticmethod
    def build_2d_sincos_position_embedding(w,
                                           h,
                                           embed_dim=256,
                                           temperature=10000.):
        grid_w = paddle.arange(int(w), dtype=paddle.float32)
        grid_h = paddle.arange(int(h), dtype=paddle.float32)
        grid_w, grid_h = paddle.meshgrid(grid_w, grid_h)
        assert embed_dim % 4 == 0, \
            'Embed dimension must be divisible by 4 for 2D sin-cos position embedding'
        pos_dim = embed_dim // 4
        omega = paddle.arange(pos_dim, dtype=paddle.float32) / pos_dim
        omega = 1. / (temperature**omega)

        out_w = grid_w.flatten()[..., None] @omega[None]
        out_h = grid_h.flatten()[..., None] @omega[None]

        return paddle.concat(
            [
                paddle.sin(out_w), paddle.cos(out_w), paddle.sin(out_h),
                paddle.cos(out_h)
            ],
            axis=1)[None, :, :]

    def forward(self, feats, for_mot=False, is_teacher=False):
        assert len(feats) == len(self.in_channels)
        # get projection features
        proj_feats = [self.input_proj[i](feat) for i, feat in enumerate(feats)]
        # encoder
        if self.num_encoder_layers > 0:
            for i, enc_ind in enumerate(self.use_encoder_idx):
                h, w = proj_feats[enc_ind].shape[2:]
                # flatten [B, C, H, W] to [B, HxW, C]
                src_flatten = proj_feats[enc_ind].flatten(2).transpose(
                    [0, 2, 1])
                if self.training or self.eval_size is None or is_teacher:
                    pos_embed = self.build_2d_sincos_position_embedding(
                        w, h, self.hidden_dim, self.pe_temperature)
                else:
                    pos_embed = getattr(self, f'pos_embed{enc_ind}', None)
                memory = self.encoder[i](src_flatten, pos_embed=pos_embed)
                proj_feats[enc_ind] = memory.transpose([0, 2, 1]).reshape(
                    [-1, self.hidden_dim, h, w])

        # top-down fpn
        inner_outs = [proj_feats[-1]]
        for idx in range(len(self.in_channels) - 1, 0, -1):
            feat_heigh = inner_outs[0]
            feat_low = proj_feats[idx - 1]
            feat_heigh = self.lateral_convs[len(self.in_channels) - 1 - idx](
                feat_heigh)
            inner_outs[0] = feat_heigh

            upsample_feat = F.interpolate(
                feat_heigh, scale_factor=2., mode="nearest")
            inner_out = self.fpn_blocks[len(self.in_channels) - 1 - idx](
                paddle.concat(
                    [upsample_feat, feat_low], axis=1))
            inner_outs.insert(0, inner_out)

        # bottom-up pan
        outs = [inner_outs[0]]
        for idx in range(len(self.in_channels) - 1):
            feat_low = outs[-1]
            feat_height = inner_outs[idx + 1]
            downsample_feat = self.downsample_convs[idx](feat_low)
            out = self.pan_blocks[idx](paddle.concat(
                [downsample_feat, feat_height], axis=1))
            outs.append(out)

        return outs

    @classmethod
    def from_config(cls, cfg, input_shape):
        return {
            'in_channels': [i.channels for i in input_shape],
            'feat_strides': [i.stride for i in input_shape]
        }

    @property
    def out_shape(self):
        return [
            ShapeSpec(
                channels=self.hidden_dim, stride=self.feat_strides[idx])
            for idx in range(len(self.in_channels))
        ]

实验

1.与YOLO的比较

在这里插入图片描述

1.不同模块的消融研究

在这里插入图片描述
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

React useState基本类型变量的使用

在 React 中,useState 是一个 Hook,用于在函数组件中添加状态,它可以让函数组件拥有状态。基本使用方法如下: // App.jsx import React, { useState } from reactfunction App() {// 使用 useState 创建一个状态变量,初…

如何用Java实现SpringCloud Alibaba Sentinel的熔断功能?

在Java中使用Spring Cloud Alibaba Sentinel实现熔断功能的步骤如下&#xff1a; 添加依赖 在项目的pom.xml文件中添加Spring Cloud Alibaba Sentinel的依赖&#xff1a; <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud…

C语言——malloc和free用法和常见误区

最近写了个关于动态数组的代码&#xff0c;遇到了一个大坑&#xff0c;特此记录 先说结论&#xff1a; 1.利用malloc创建堆空间&#xff0c;大小最好设置大一点&#xff0c;不然后面存进去的值需要的空间过大会导致各种的堆、指针问题 2.只能使用realloc对已经创建的空间进行修…

没有电商经验的人去操作抖音小店,难度大不大?好操作吗?

大家好&#xff0c;我是电商小V 很多新手小伙伴想去操作抖音小店项目&#xff0c;咨询的最多的问题就是我没有电商运营的经验可以去操作吗&#xff1f; 当然是可以操作的&#xff0c;抖音小店项目对于新手来说是一个非常友好的项目&#xff0c;很多小伙伴都是感觉没有电商经验去…

C++——list的实现以及源码

前言&#xff1a; 最近学习了clist的实现&#xff0c;这让我对迭代器的理解又上升了一个新的高度&#xff0c;注意&#xff1a;代码里的list是放在一个叫zgw的命名空间里边&#xff0c;但是在实现list的代码中没有加namespace&#xff0c;这里给个注意&#xff0c;以后复习时能…

整理了10个靠谱且热门的赚钱软件,适合普通人长期做的赚钱副业

作为一名普通的上班族&#xff0c;我们每天都在辛勤工作&#xff0c;但工资的增长速度却如同蜗牛般缓慢。不过&#xff0c;别担心&#xff0c;信息时代总是带给我们无尽的惊喜&#xff01;今天&#xff0c;我将为大家推荐一些赚钱的宝藏软件&#xff0c;让你在闲暇之余轻松实现…

五分钟搭建一个Suno AI音乐站点

五分钟搭建一个Suno AI音乐站点 在这个数字化时代&#xff0c;人工智能技术正以惊人的速度改变着我们的生活方式和创造方式。音乐作为一种最直接、最感性的艺术形式&#xff0c;自然也成为了人工智能技术的应用场景之一。今天&#xff0c;我们将以Vue和Node.js为基础&#xff…

三十六计的笔记

系列文章目录 三十六计的笔记 文章目录 系列文章目录1、瞒天过海2、围魏救赵3、借刀杀人4、以逸待劳5、趁火打劫6、声东击西7、无中生有8、暗渡陈仓9、隔岸观火10、笑里藏刀11、李代桃僵12、顺手牵羊13、打草惊蛇14、借尸还魂15、调虎离山16、欲擒故纵17、抛砖引玉18、擒贼擒王…

牛客NC302 环形数组的连续子数组最大和【中等 动态规划 Java/Go/PHP/C++】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/e9f3282363844355aa51497c5410beee 思路 动态规划 两种情况&#xff08;首位相连的&#xff09;和首位不相连的 首尾相连的可以算最小的连续子数组得出&#xff0c;sum-就是。Java代码 import java.util.*;pub…

第20届文博会:“特别呈现”—周瑛瑾雷米·艾融双个展,著名美术评论家,批评家彭德教授对周瑛瑾作品进行评论

周瑛瑾不是学院派艺术家&#xff0c;但在彩墨画领域的天赋超出中国八大美院的同类型画家。相比具有批判意识的当代艺术&#xff0c;他的彩墨艺术如同我们这个苦难世界的创可贴和安慰剂。当我面对他的彩墨画&#xff0c;首先是惊艳&#xff0c;随之想到屈原的离骚&#xff0c;还…

slint esp32 tokio

源码&#xff1a;https://github.com/xiaguangbo/slint_esp32_tokio cpu 是 esp32c2&#xff0c;屏幕是 ili9341&#xff0c;触摸是 xpt2046&#xff0c;使用 spi 半双工 不使用DMA&#xff08;esp-rs还没支持&#xff09;&#xff0c;SPI 40M&#xff0c;240*320全屏刷新为1.5…

Windows、Linux下,基于QT的打包方法

整理这篇文档的意义在于&#xff1a;自己走了很多弯路&#xff0c;淋过雨所以想为别人撑伞&#xff0c;也方便回顾&#xff0c;仅供参考 ps: 第一次做Windows下打包&#xff0c;用了2小时&#xff0c;第二次20秒第一次做Linux(ubuntu)下打包&#xff0c;用了8小时&#xff0c;…

Linux 内核

查看内核的发行版 $ uname -r 5.4.0-150-genericcd /lib/modules/5.4.0-150-generic, 内核源码所在的位置&#xff1a;/usr/src 这里的内核源码路径&#xff08;–kernel-source-path&#xff09;即为&#xff1a; cd /usr/src/linux-headers-5.4.0-150-generic/ 临时生效: …

JMETER工具:以录制手机app为例

JMETER工具&#xff1a;以录制手机app为例子 JMETER安装和环境配置 pc需要安装jdk&#xff0c;并进行jdk的环境配置&#xff0c;安装好jdk并配置好后&#xff0c;通过命令行输入java –version出现以下界面就表示安装成功&#xff1a; &#xff08;对应的jdk版本不可太低&…

网络通信(二)

UDP通信 特点&#xff1a;无连不是先接、不可靠通信 不事先建立连接&#xff1b;发送端每次把要发送的数据&#xff08;限制在64KB内&#xff09;、接收端IP、等信息封装成一个数据包&#xff0c;发出去就不管了 java提供了一个java.net.DatagramSocket类来实现UDP通信 Dat…

第13章-循迹功能 循迹小车讲解 原理分析 STM32智能小车循迹教程 红外对管使用 PID循迹算法分析

讲解一下我们小车里面的循迹部分&#xff0c;包括红外基础使用&#xff0c;无PID循迹和有PID循迹。 第13章-循迹功能 13.1-非PID循迹功能完成 先红外对管调试 我们这里学习一下&#xff0c;如何实现循迹功能 如何才能让小车沿着黑线运动、要让小车感知到黑线的位置&#x…

【SpringBoot】SpringBoot中防止接口重复提交(单机环境和分布式环境)

&#x1f4dd;个人主页&#xff1a;哈__ 期待您的关注 目录 &#x1f33c;前言 &#x1f512;单机环境下防止接口重复提交 &#x1f4d5;导入依赖 &#x1f4c2;项目结构 &#x1f680;创建自定义注解 ✈创建AOP切面 &#x1f697;创建Conotroller &#x1f4bb;分布…

[CISCN 2024] Crypto部分复现

文章目录 OvOez_rsacheckin浅记一下 迟来的文章 OvO 题目描述&#xff1a; from Crypto.Util.number import * from secret import flagnbits 512 p getPrime(nbits) q getPrime(nbits) n p * q phi (p-1) * (q-1) while True:kk getPrime(128)rr kk 2e 65537 kk …

3d打印问题总结

1.打印拉丝&#xff1a;https://zhuanlan.zhihu.com/p/152221550 解决方案&#xff1a;温度过高&#xff0c;PLA材料材料喷嘴温度一般设置为200度比较合适。

string OJ题

下面分享一下string做题心得 1. 明白字符串中存储的数字为0 8 9与0 8 9 完全不同&#xff0c;字符0其实在串中存储的是48&#xff0c;要有意识的转化。字符串中如果存数字8&#xff0c;意味着存了BS&#xff08;退格&#xff09; 例如1&#xff1a; 算出结果为5&#xff0c;存…