视频教程:RT-DETR | 2、backbone_哔哩哔哩_bilibili
一、图像预处理
- 经过图像预处理、图像增强后的图片尺寸都为640*640
- 超参数multi_scale设置了不同的尺寸
- sz是经过对multi-scale随机选择得到的一个尺寸,这里假设是576
- 则640*640图像会通过双线性插值(interpolate)方法resize到576*576
为什么要设置多个尺寸?为什么要随机选择一个尺寸resize?
二、backbone
讲解中使用的是r50的backbone版
这里的ResNet是Progressive ResNet-50版本,在原ResNet50的基础上做了两点改进:
2.1 标准ResNet一开始使用的7*7卷积层,改进版改为了三个3*3卷积层
输出都是一样的,下采样2倍,通道数改为64
对应代码:
为什么要将一个7*7卷积层替换为3个3*3卷积层?
Inception 和 MobileNet系列论文中都有提及到用小卷积代替大卷积操作减少计算量
2.2 将stage2-4的下采样卷积修改为平均池化+卷积
改为
为什么要这样修改?
因为stride=2的1*1卷积会间隔一个像素,导致信息的缺失;改造后能够保留更多的信息
对应代码:
2.3 backbone的输出
对应代码:
三、AIFI
3.1前处理
-
输入是从backbone出来的S3,S4,S5
-
需要对这三个特征图统一通道数
- 处理后尺寸:
3.2 读取S5的特征图,处理为encoder的序列输入
这里的num_encoder_layers设置的是1
self.encoder_idx设置的是S5对应backbone输出的索引
这里循环相当于只有一次,i返回索引0,enc_ind返回的use_encoder_idx元素中索引0对应的值-2,即S5在backbone输出的索引值
读取S5特征图的尺寸信息并展平为序列然后permute
(bs,256,18,18)->(bs,256,324)->(bs,324,256) 相当于324个token,每个token长度256
3.3 进入encoder
输入是(bs,324,256)输出memory也是(bs,324,256)
然后再permute,reshape(bs,324,256)->(bs,256,324)->(bs,256,18,18),替换最开始backbone输出的S5部分,在结构图中是F5
四、CCFM
4.1 第一个Fusion
经过AIFI输出然后调整尺寸为F5,再次经过1*1卷积,大小和通道数不变(bs,256,18,18),然后经过上采样尺寸变为和S4尺寸一样大小的(bs,256,36,36);
将经过上采样得到的特征图与S4在通道维度concat拼接,变为(bs,512,36,36),经过两条支路;
支路1:1*1Conv,s=1,通道数从512-->256;
支路2:先经过1*1Conv,s=1通道数从512-->256;然后经过N个RepBlock,得到(bs,256,36,36)
两条支路的输出尺寸一样,经过element-wise add逐元素相加得到第一个Fusion的输出(bs,256,36,36)
4.2 第二个Fusion
4.3 其余Fusion
4.4 输出即Decoder的输入
最后输出部分outs为list即上图紫色部分的三个输出,尺寸分别为(bs,256,72,72)(bs,256,36,36) (bs,256,18,18)
三个输出风别经过一个1*1Conv卷积,尺寸通道都不变,再分别进行flatten展平然后permute替换位置,最后在token的维度上进行拼接得到(bs,6804,256),这部分即Decoder的输入
对应代码:
三个特征图经过1*1卷积后,经过flatten,permute得到feat_flatten=list{3}
最后展平得到(bs,6804,256)
再存储三个特征图的尺寸spatial_shapes以及序列长度level_start_index(第i个特征图token开始的索引)
第三个特征图后面没有元素了,所以使用pop删除掉最后一个特征图的结束位置
返回值:
五、Decoder整体网络
如下图所示,原始DETR的decoder输入部分是初始值为0的query以及由nn.Embedding得到的嵌入矩阵组成,这样的输入完全没有先验性息导致收敛慢。
RT-DETR的Decoder结构如下图:
Query的上部分(绿色)是由IoU-aware query-selection得到的300个类别置信度靠前的query;
Query的下部分(黄色)是由Denoising加噪得到的,200是举例,实际数量是由GT中的object数量决定;
对应代码:
六、Denoising
Denoising部分主要参考DN-DETR:
6.1 DN-DETR提出背景
匈牙利匹配的不稳定性导致坐标偏移量难以学习,进一步导致模型收敛速度慢;
如下图所示,不同的epoch中,同一个query通常匹配到不同的GT,尤其在早期训练,使得模型优化有二义性
6.2 DN-DETR提出的解决方案
注意:在DN-DETR中,Denoising part 和 Matching part是两个独立的部分,他们两个分别做损失函数的计算;Denosing part当作是shortcut绕过了二分图匹配帮助decoder快速优化参数。
在训练阶段, decoder的输入是(noised gt queries,decoder queries)concat拼接输入;
在推理阶段, decoder的输入只有(decoder queries)输入;
6.3 DN-DETR和RT-DETR的加噪方式
RT-DETR正样本的加噪方式如上图所示,中间透明框是GT,坐标框的左上角点的范围在粉色区域;右下角点的移动范围在橙色区域;负样本就会超出这个区域,如下图所示
6.4 加噪组
一张图片中有多个目标,不同的噪声组对ground truth进行加噪,不同组之间的加噪方式都不一样,即不一样;
DN-DETR的ground truth和noise query是一对一关系;
RT-DETR的ground truth和noise query是一对二关系;
6.5 AttentionMask
- matching part不能看到noise group,因为noise group有正确答案
- denoising part 中不同组之间不能相互看,因为别的组会包含自己组的正确答案
6.6 代码解读
示例中bs是2,即一个batch中有两张图片;
超参数设置:
- num_queries:300 matching part部分的query个数
- class_embed:将类别标签编码为向量表达 (81,256) 80个类别加上背景,编码为256通道
- num_denosing:100 要加噪query 的数量,即100个正样本,100个负样本
- label_noise_ratio:要改变GT 标签的占比,即这里200个样本有多少需要把标签调整为非真实标签;
- box_noise_scale:box尺度缩放系数
num_gets:列表保存一个bs中每张图片有多少个目标即有多少个GT(示例中第一张图片一个gt,第二张图片5个gt)
max_gt_nums:找到一个bs中一张图片最多有几个gt,后续所有图像的object都会统一为这个最大值;小于这个值的就用mask给标记上,如上图白色框部分
num_group:计算需要创建多少个组(超参数num_denosing / 最大gt值 )
每个组中都有10个query,正样本5个,负样本5个
创建input_query_class(bs,max_gt_nums)即(2,5),元素值为num_classes即背景80
创建input_query_bbox(bs,5,4),元素为全0,4表示xywh
创建pad_gt_mask(2,5),元素全0,用来标记哪些位置真的有object,哪些位置没有object;后续会将有object的位置置为1,没有object的位置仍为0
将上面的三个内容复制40份,即2*20
分别生成positive mask和negative mask
先成成一个初始值全为0的(bs,2*max_gt_num,1)即(2,10,1)的矩阵;
将后面五个negtivate位置的值都置为1,再复制20份;
直接negtivate mask取反,再× pad_gt_mask(用来标记哪些位置真有object)得到positive mask
dn_positive_idx是将positive_gt_mask返回不为0的索引,输出为(1,120)的张量;(一共有120个,正样本一共有20个,负样本一共有100个)
然后根据每张图片的真实gt分割成tuple,即每张图片上返回不为0的列索引
重新计算num_denosing
对gt标签进行加噪
从0~1的均匀分布中,随机采样2*200 (input_query_class形状)个数,如果数值小于0.5*0.5就把这个值置为True,否则为False。(即将bs*200 个denosing query的25%部分的标签调整为非真实标签)
对bbox加噪
- 先将标注框xywh的形式转换为x_min,y_min,w_max,y_max的形式;
- 取宽高的一半,扩展得到diff [w/2,h/2,w/2,h/2];
- *上噪声比例box_noise_sclase调整扰动强度;
- rand_sign:生成的随机的1和-1当作随机的加减;
rand_sign = torch.randint_like(input_query_bbox, 0, 2) * 2.0 - 1.0
# 先随机生成0和1
# 再计算变为0和2
# 最后减去1得到-1 和 1
# 不能用rand_sign = torch.randint_like(input_query_bbox, -1,1 )因为得不到1,取不到最大值
# 不能用rand_sign = torch.randint_like(input_query_bbox, -1,2)会取到0
torch.rand_like(input_query_bbox)
:生成与input_query_bbox
形状相同的随机浮点数张量,值范围为[0, 1)
。(rand_part + 1.0) * negative_gt_mask
:如果negative_gt_mask
为True
,则将随机值加 1.0,的取值范围变味了(1,2),表示对负样本的扰动更大。negative_gt_mask
是一个布尔掩码,用于标识负样本的位置。rand_part * (1 - negative_gt_mask)
:如果negative_gt_mask
为False
,则保持原随机值不变。rand_part *= rand_sign
:将随机值乘以rand_sign
,为其分配正负方向。
clip_:原地操作,将边界框的坐标裁剪到 [0.0, 1.0]
的范围内,确保边界框的坐标值仍然是归一化的;
最后将x_min,y_min,w_max,y_max格式转换为xywh格式,经过sigmoid反函数映射回原始值;
生成attention_mask
- 将主任务查询不能访问的范围设置为
True
,使它们不能看到去噪任务的查询信息。 - 确保去噪任务的查询只能看到自己的小组内的信息,不能访问其他组的去噪任务信息。
返回值
input_query_calss:加噪后的200个query
input_query_bbox:加噪后的200个anchor
attn_mask
dn_meta:打包的一些去噪信息