RTMDet原理与代码解析

paper:RTMDet: An Empirical Study of Designing Real-Time Object Detectors

official implementation:https://github.com/open-mmlab/mmdetection/tree/main/configs/rtmdet

本文的创新点

Backbone and Neck

  1. 在backbone的basic building block中采用large-kernel depth-wise convolution,提高了模型捕获全局上下文的能力。
  2. 直接添加depth-wise卷积会增加模型深度,减慢推理速度。因此通过减小building block的个数来减小模型深度,同时通过增加模型宽度来补偿模型容量。
  3. 作者还观察到,在neck部分设置更多参数,使其容量与backbone兼容,可以实现更好的速度-精度的平衡。

论文中提到的增加模型宽度,以及通过增加neck部分的expand ratio来增加neck部分的参数在代码中都没有看到。

block的结构设计中采用大核深度可分离卷积,而没有采用重参数化方法的原因:其它YOLO系列中结构重参数化技术被广泛使用,但有一些副作用,比如训练速度变慢,增加训练占用显存,以及在量化到较低的比特后增加了误差间隙,这需要通过重参数化优化器或量化感知训练来补偿。

Head

  1. 不同尺度的head之间共享卷积参数,但BN层独立。

Label Assignment and Loss

提出在计算matching cost时使用soft label来扩大高质量匹配和低质量配置之间的差异,从而稳定训练加速收敛。基于SimOTA进行的改动。

  1. 分类损失引入soft label,就是GFL
  2. 回归损失添加log,增大了低质量匹配的cost,增大了高质量匹配和低质量匹配之间的差异。
  3. center损失采用soft center region cost。

Data Augmentation

cross-sample数据增强比如MixUp和CutMix有两个缺点:(1)每个iteration需要load多张图片,增加了data loading cost减慢了训练速度。(2)生成的样本带有噪声有可能不属于真实数据的实际分布,影响模型的训练。

  1. 引入caching mechanism改进MixUp和CutMix。
  2. 对于第二点,YOLOX通过使用两阶段的训练策略,第一阶段使用强数据增强,第二阶段使用弱数据增强,由于第一阶段的强数据增强包括随机旋转和剪切,导致输入和转换后的box之间有错位,YOLOX在第二阶段增加L1损失来微调回归分支
    为了解耦数据增强和损失函数的使用,本文去除了这些数据增强,在280个epoch的第一个阶段将混合图片的数量增加到8个来补偿数据增强的强度。在最后20个epoch中,切换到Large Scale Jittering,从而在一个与真实数据分布更更一致的doman中对模型进行微调。

Training Strategy

  1. 为了稳定训练优化器采用AdamW,这个在卷积目标检测模型中很少使用,但在vision transformer中是default。

方法介绍 & 代码解析

骨干网络部分用depth-wise卷积增加了网络深度,因此作者减少了第2个和第3个stage中block的数量,如下表所示,block数量由9减到6延迟降低了20%,但AP也降低了0.5,为了弥补精度的所示,作者在每个stage的最后添加了一个channel attention,实现了更好的精度-速度的权衡。

以RTMDet-s为例,deepen_factor=0.33, widen_factor=0.5,因此每个stage的block数量变成了1-2-2-1。stage2的结构如下

Head部分共享卷积参数,但BN独立。官方实现中在定义head时是分开的,最后将每个head的卷积就赋值为相同。 

if self.share_conv:for n in range(len(self.prior_generator.strides)):for i in range(self.stacked_convs):self.cls_convs[n][i].conv = self.cls_convs[0][i].convself.reg_convs[n][i].conv = self.reg_convs[0][i].conv# print(self.cls_convs[n][0])  # 共享conv,但BN独立
ConvModule((conv): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)(bn): SyncBatchNorm(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(activate): SiLU(inplace=True)
)

标签分配的实现在dynamic_soft_label_assigner.py中,基于SimOTA,只不过对cost的计算进行了改进,具体如下,其值为分类cost、回归cost、区域cost的加权和

cost_matrix = soft_cls_cost + iou_cost + soft_center_prior

 其中分类cost采用了Generalized focal loss中的quality focal loss

pairwise_ious = self.iou_calculator(valid_decoded_bbox, gt_bboxes)
soft_label = gt_onehot_label * pairwise_ious[..., None]
scale_factor = soft_label - valid_pred_scores.sigmoid()
soft_cls_cost = F.binary_cross_entropy_with_logits(valid_pred_scores, soft_label,reduction='none') * scale_factor.abs().pow(2.0)
soft_cls_cost = soft_cls_cost.sum(dim=-1)

其中 \(Y_{soft}\) 是预测框与gt框之间的IoU,作为soft label取来原始的标签1。

当用IoU以及相关变体作为回归损失时,最佳匹配和最差匹配之间的差值小于1,这使得区分高质量匹配和低质量匹配变得困难,因此作者使用IoU的对数作为回归的代价,这增大了低质量匹配即IoU较小匹配的cost

iou_cost = -torch.log(pairwise_ious + EPS) * self.iou_weight

至于区域代价 \(C_{region}\),和FCOS、YOLOX等采用的fixed center prior方法不同,本文采用了一种soft center region cost来稳定dynamic cost的匹配

distance = (valid_prior[:, None, :2] - gt_center[None, :, :]).pow(2).sum(-1).sqrt() / strides[:, None]
soft_center_prior = torch.pow(10, distance - self.soft_center_radius)

数据增强部分,Mosaic和MixUp中引入缓存机制。这一部分实现在mmdet/datasets/transforms/transforms.py中。

在mmdet中要使用Mosaic,需要同时使用MultiImageMixDataset。原本results字典中保存的是一张图的相关信息包括img、gt_bboxes、gt_labels等,在MultiImageMixDataset类中调用Mosaic类中的get_indexes方法,随机再挑出其它三张图的索引。然后将这3张图的信息放到列表中作为key 'mix_results'的value加到原始的results中,这样results就包含了4张图的信息。

而在CachedMosaic中,是维护了一个缓存列表self.results_cache,max_cached_images指定最大缓存数量,默认为40,作者指出10张缓存就可以满足随机的要求了。

def __init__(self,*args,max_cached_images: int = 40,random_pop: bool = True,**kwargs) -> None:super().__init__(*args, **kwargs)self.results_cache = []self.random_pop = random_popassert max_cached_images >= 4, 'The length of cache must >= 4, ' \f'but got {max_cached_images}.'self.max_cached_images = max_cached_images

在原始mosaic中,每个iteration需要从整个训练集中随机挑选3张与当前张进行combine,而在cachedmosaic中是从cache中挑选3张,因此挑选索引的原始大小不同,如下

# mosaic
def get_indexes(self, dataset: BaseDataset) -> int:"""Call function to collect indexes.Args:dataset (:obj:`MultiImageMixDataset`): The dataset.Returns:list: indexes."""indexes = [random.randint(0, len(dataset)) for _ in range(3)]return indexes# cachedmosaic
def get_indexes(self, cache: list) -> list:"""Call function to collect indexes.Args:cache (list): The results cache.Returns:list: indexes."""indexes = [random.randint(0, len(cache) - 1) for _ in range(3)]return indexes

首先进行append和pop更新缓存列表

# cache and pop images
self.results_cache.append(copy.deepcopy(results))
if len(self.results_cache) > self.max_cached_images:if self.random_pop:index = random.randint(0, len(self.results_cache) - 1)else:index = 0self.results_cache.pop(index)

然后根据get_indexes方法得到的索引从缓存列表中得到mix_results,其中包含3张图片的信息用于与当前图片进行组合,当前图片保存在results中。

indices = self.get_indexes(self.results_cache)
mix_results = [copy.deepcopy(self.results_cache[i]) for i in indices]

而在原始的mosaic中,results中除了当前图片还包含从整个训练集中挑选的3张图片,即mix_results包含在results中传进函数的。

assert 'mix_results' in results
results_patch = copy.deepcopy(results['mix_results'][i - 1])

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2023年【R1快开门式压力容器操作】考试资料及R1快开门式压力容器操作复审考试

题库来源:安全生产模拟考试一点通公众号小程序 R1快开门式压力容器操作考试资料参考答案及R1快开门式压力容器操作考试试题解析是安全生产模拟考试一点通题库老师及R1快开门式压力容器操作操作证已考过的学员汇总,相对有效帮助R1快开门式压力容器操作复…

2023年【危险化学品经营单位安全管理人员】考试内容及危险化学品经营单位安全管理人员最新解析

题库来源:安全生产模拟考试一点通公众号小程序 危险化学品经营单位安全管理人员考试内容是安全生产模拟考试一点通生成的,危险化学品经营单位安全管理人员证模拟考试题库是根据危险化学品经营单位安全管理人员最新版教材汇编出危险化学品经营单位安全管…

Java核心知识点整理大全12-笔记

Java核心知识点整理大全-笔记_希斯奎的博客-CSDN博客 Java核心知识点整理大全2-笔记_希斯奎的博客-CSDN博客 Java核心知识点整理大全3-笔记_希斯奎的博客-CSDN博客 Java核心知识点整理大全4-笔记-CSDN博客 Java核心知识点整理大全5-笔记-CSDN博客 Java核心知识点整理大全6…

【多线程】Thread类的使用

目录 1.概述 2.Thread的常见构造方法 3.Thread的几个常见属性 4.启动一个线程-start() 5.中断一个线程 5.1通过共享的标记来进行沟通 5.2 调用 interrupt() 方法来通知 6.等待一个进程 7.获取当前线程引用 8.线程的状态 8.1所有状态 8.2线程状态和转移的意义 1.概述 …

使用向日葵开机棒进行远程开机

文章目录 1\. 前言2\. 说明3\. 开机棒设置4\. 电脑端设置4.1. 电脑端允许网卡唤醒4.1.1. 关闭设备节能4.2. 将电脑端设备加入设备列表 5\. 手机端5.1. 添加开机棒5.2. 绑定主机5.2.1. 添加成功的主机 6\. 唤醒 1. 前言 如果我们出差在外或者人不在实验室,如果可以使…

【极客技术】真假GPT-4?微调 Llama 2 以替代 GPT-3.5/4 已然可行!

近日小编在使用最新版GPT-4-Turbo模型(主要特点是支持128k输入和知识库截止日期是2023年4月)时,发现不同商家提供的模型回复出现不一致的情况,尤其是模型均承认自己知识库达到2023年4月,但当我们细问时,Fak…

【Spring篇】JDK动态代理

目录 什么是代理? 代理模式 动态代理 Java中常用的代理模式 问题来了,如何动态生成代理类? 动态代理底层实现 什么是代理? 顾名思义,代替某个对象去处理一些问题,谓之代理,那么何为动态&a…

Zabbix-Liunx服务器内存使用率测试

要在Python 2.7中运行内存消耗脚本并安装psutil,您需要先安装pip。以下是完整的步骤,包括如何在Python 2.7环境中安装pip,然后安装psutil,以及最后如何运行内存消耗脚本。 步骤1: 安装pip 在Python 2.7中安装pip: 首先…

积分球吸收光谱测量的领域有哪些?

积分球吸收光谱测量是一种常用的吸收光谱测量方法,它通过将样品放置在积分球的入口处,球内的光线经过多次反射后形成均匀的照度分布,然后使用光度计或光谱仪对光线进行测量,可以获得样品的相关参数。 在积分球吸收光谱测量中&…

Python入门03变量

目录 1 什么是变量2 变量声明3 变量命名规则4 变量类型5 类型转换总结 1 什么是变量 编程语言中变量就像容器一样,可以用来存放东西 我的变量就像杯子一样,可以用来盛放各种饮料。在Python中变量用来存放各种各样的数据,比如整数、浮点数、…

NLP的使用

参考: Apache openNLP 简介 - 链滴 (ld246.com) opennlp 模型下载地址:Index of /apache/opennlp/models/ud-models-1.0/ (tencent.com) OpenNLP是一个流行的开源自然语言处理工具包,它提供了一系列的NLP模型和算法。然而,Open…

Linux的基本指令(3)

16.cal指令 cal命令可以用来显示公历(阳历)日历。公历是现在国际通用的历法,又称格列历,通称阳历。“阳历”又名“太阳历”,系以地球绕行太阳一周为一年,为西方各国所通用,故又名“西历”。 命…