基于 EasyCV 复现 DETR 和 DAB-DETR,Object Query 的正确打开方式

Posted 阿里云大数据AI技术

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于 EasyCV 复现 DETR 和 DAB-DETR,Object Query 的正确打开方式相关的知识,希望对你有一定的参考价值。

DETR 是最近几年最新的目标检测框架,第一个真正意义上的端到端检测算法,省去了繁琐的 RPN、anchor 和 NMS 等操作,直接输入图片输出检测框。DETR 的成功主要归功于 Transformer 强大的建模能力,还有匈牙利匹配算法解决了如何通过学习的方式 one-to-one 的匹配检测框和目标框。

虽然 DETR 可以达到跟 Mask R-CNN 相当的精度,但是训练 500 个 epoch、收敛速度慢,小目标精度低的问题都饱受诟病。后续一系列的工作都围绕着这几个问题展开,其中最精彩的要属 Deformable DETR,也是如今检测的刷榜必备,Deformable DETR 的贡献不单单只是将 Deformable Conv 推广到了 Transformer 上,更重要的是提供了很多训练好 DETR 检测框架的技巧,比如模仿 Mask R-CNN 框架的 two-stage 做法,如何将 query embed 拆分成 content 和 reference points 两部分组成,如何将 DETR 拓展到多尺度训练,还有通过 look forward once 进行 boxes 预测等技巧,在 Deformable DETR 之后,大家似乎找到了如何打开 DETR 框架的正确方式。其中对 object query 代表什么含义,以及如何更好的利用 object query 做检测,产生了许多有价值的工作,比如 Anchor DETR、Conditional DETR 等等,其中 DAB-DETR 做的尤为彻底。DAB-DETR 将 object query 看成是 content 和 reference points 两个部分,其中 reference points 显示的表示成 xywh 四维向量,然后通过 decoder 预测 xywh 的残差对检测框迭代更新,另外还通过 xywh 向量引入位置注意力,帮助 DETR 加快收敛速度,本文将基于 EasyCV 复现的 DETR 和 DAB-DETR 算法详细介绍一下如何正确的使用 object query 来提升 DETR 检测框架的性能。

DETR

DETR 使用 set loss function 作为监督信号来进行端到端训练,然后同时预测所有目标,其中 set loss function 使用 bipartite matching 算法将 pred 目标和 gt 目标匹配起来。直接将目标检测任务看成 set prediction 问题,使训练过程变的简洁,并且避免了 anchor、NMS 等复杂处理。

DETR 主要贡献有两个部分:architecture 和 set prediction loss。

1.Architecture

基于

DETR 先用 CNN 将输入图像 embedding 成一个二维表征,然后将二维表征转换成一维表征并结合 positional encoding 一起送入 encoder,decoder 将少量固定数量的已学习的 object queries(可以理解为 positional embeddings)和 encoder 的输出作为输入。最后将 decoder 得到的每个 output embdding 传递到一个共享的前馈网络(FFN),该网络可以预测一个检测结果(包括类和边框)或着“没有目标”的类。

1.1 Transformer

基于

1.1.1 Encoder

将 Backbone 输出的 feature map 转换成一维表征,得到 特征图,然后结合 positional encoding 作为 Encoder 的输入。每个 Encoder 都由 Multi-Head Self-Attention 和 FFN 组成。和 Transformer Encoder 不同的是,因为 Encoder 具有位置不变性,DETR 将 positional encoding 添加到每一个 Multi-Head Self-Attention 中,来保证目标检测的位置敏感性。

# 一层encoder代码如下
class TransformerEncoderLayer(nn.Module):

def __init__(self,
d_model,
nhead,
dim_feedforward=2048,
dropout=0.1,
activation=relu,
normalize_before=False):
super().__init__()
self.self_attn = nn.MultiheadAttention(d_model, nhead, dropout=dropout)
# Implementation of Feedforward model
self.linear1 = nn.Linear(d_model, dim_feedforward)
self.dropout = nn.Dropout(dropout)
self.linear2 = nn.Linear(dim_feedforward, d_model)

self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
self.dropout1 = nn.Dropout(dropout)
self.dropout2 = nn.Dropout(dropout)

self.activation = _get_activation_fn(activation)
self.normalize_before = normalize_before

def with_pos_embed(self, tensor, pos: Optional[Tensor]):
return tensor if pos is None else tensor + pos

def forward(self,
src,
src_mask: Optional[Tensor] = None,
src_key_padding_mask: Optional[Tensor] = None,
pos: Optional[Tensor] = None):
q = k = self.with_pos_embed(src, pos)
src2 = self.self_attn(
q,
k,
value=src,
attn_mask=src_mask,
key_padding_mask=src_key_padding_mask)[0]
src = src + self.dropout1(src2)
src = self.norm1(src)
src2 = self.linear2(self.dropout(self.activation(self.linear1(src))))
src = src + self.dropout2(src2)
src = self.norm2(src)
return src
1.1.2 Decoder

因为 Decoder 也具有位置不变性,Decoder 的个 object query(可以理解为学习不同 object 的 positional embedding)必须是不同,以便生成不同 object 的 embedding,并且同时把它们添加到每一个 Multi-Head Attention 中。个 object queries 通过 Decoder 转换成一个 output embedding,然后 output embedding 通过 FFN 独立解码出个预测结果,包含 box 和 class。对输入 embedding 同时使用 Self-Attention 和 Encoder-Decoder Attention,模型可以利用目标的相互关系来进行全局推理。和 Transformer Decoder 不同的是,DETR 的每个 Decoder 并行输出个对象,Transformer Decoder 使用的是自回归模型,串行输出个对象,每次只能预测一个输出序列的一个元素。

# 一层decoder代码如下
class TransformerDecoderLayer(nn.Module):

def __init__(self,
d_model,
nhead,
dim_feedforward=2048,
dropout=0.1,
activation=relu,
normalize_before=False):
super().__init__()
self.self_attn = nn.MultiheadAttention(d_model, nhead, dropout=dropout)
self.multihead_attn = nn.MultiheadAttention(
d_model, nhead, dropout=dropout)
# Implementation of Feedforward model
self.linear1 = nn.Linear(d_model, dim_feedforward)
self.dropout = nn.Dropout(dropout)
self.linear2 = nn.Linear(dim_feedforward, d_model)

self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
self.norm3 = nn.LayerNorm(d_model)
self.dropout1 = nn.Dropout(dropout)
self.dropout2 = nn.Dropout(dropout)
self.dropout3 = nn.Dropout(dropout)

self.activation = _get_activation_fn(activation)
self.normalize_before = normalize_before

def with_pos_embed(self, tensor, pos: Optional[Tensor]):
return tensor if pos is None else tensor + pos

def forward(self,
tgt,
memory,
tgt_mask: Optional[Tensor] = None,
memory_mask: Optional[Tensor] = None,
tgt_key_padding_mask: Optional[Tensor] = None,
memory_key_padding_mask: Optional[Tensor] = None,
pos: Optional[Tensor] = None,
query_pos: Optional[Tensor] = None):
q = k = self.with_pos_embed(tgt, query_pos)
tgt2 = self.self_attn(
q,
k,
value=tgt,
attn_mask=tgt_mask,
key_padding_mask=tgt_key_padding_mask)[0]
tgt = tgt + self.dropout1(tgt2)
tgt = self.norm1(tgt)
tgt2 = self.multihead_attn(
query=self.with_pos_embed(tgt, query_pos),
key=self.with_pos_embed(memory, pos),
value=memory,
attn_mask=memory_mask,
key_padding_mask=memory_key_padding_mask)[0]
tgt = tgt + self.dropout2(tgt2)
tgt = self.norm2(tgt)
tgt2 = self.linear2(self.dropout(self.activation(self.linear1(tgt))))
tgt = tgt + self.dropout3(tgt2)
tgt = self.norm3(tgt)
return tgt
1.1.3 FFNFFN

由 3 层 perceptron 和一层 linear projection 组成。FFN 预测出 box 的归一化中心坐标、长、宽和 class。DETR 预测的是固定数量的N个 box 的集合,并且N通常比实际目标数要大的(其中 DETR 默认设置为 100 个,而 DAB-DETR 设置为 300 个),并且使用一个额外的空类来表示预测得到的 box 不存在目标。

class MLP(nn.Module):
""" Very simple multi-layer perceptron (also called FFN)"""

def __init__(self, input_dim, hidden_dim, output_dim, num_layers):
super().__init__()
self.num_layers = num_layers
h = [hidden_dim] * (num_layers - 1)
self.layers = nn.ModuleList(
nn.Linear(n, k) for n, k in zip([input_dim] + h, h + [output_dim]))

def forward(self, x):
for i, layer in enumerate(self.layers):
x = F.relu(layer(x)) if i < self.num_layers - 1 else layer(x)
return x

2.Set prediction loss

DETR 模型训练的主要困难是如何根据 gt 衡量预测结果(类别、位置、数量)。DETR 提出的 loss 函数可以产生 pred 和 gt 的最优双边匹配(确定 pred 和 gt 的一对一关系),然后优化 loss。将表示为 gt 的集合, 表示为个预测结果的集合。假设大于图片目标数,可以认为是用空类(无目标)填充的大小为N的集合。搜索两个集合个元素基于的不同排列顺序,使得 loss 尽可能的小的排列顺序即为二分图最大匹配(Bipartite Matching),公式如下:

基于

其中基于表示pred和gt关于元素MAE自监督算法介绍和基于EasyCV的复现

MAE 自监督算法介绍和基于 EasyCV 的复现

百度飞桨顶会论文复现营DETR解读笔记

百度飞桨顶会论文复现营DETR解读笔记

百度飞桨顶会论文复现营DETR解读笔记

EasyCV带你复现更好更快的自监督算法-FastConvMAE