【DETR 论文精读【论文精读】-哔哩哔哩】 https://b23.tv/Iy9k4O2
【DETR源码解读4-哔哩哔哩】 https://b23.tv/Qp1uH5v
简介:
时间:2020
会议:ECCV
作者:Nicolas Carion, Francisco Massa, Gabriel Synnaeve, Nicolas Usunier, Alexander Kirillov和Sergey Zagoruyko,来自Paris Dauphine University和Facebook AI
摘要:
将目标检测看作一个集合预测的问题
任务:给定一张图片,预测一组框,每个框需要得到坐标信息和包含的物体类别信息,将框可以视为集合,不同图片所对应的框不同,则所对应的集合就不同
去除:NMS、生成anchor
创新:
①提出一个目标函数,通过二分图匹配的方式强制模型输出一组独一无二的预测(去除冗余框,每个物体的理想状态下就会生成一个框)
②使用Transformer 的encoder、decoder架构:
(1)在Transformer解码器中提出learned object query,它可以和全局图像信息结合,通过不停做注意力操作,让模型直接输出一组预测框
(2)并行出框
导言:
背景:
大部分目标检测器采用间接方式预测,或用回归和分类替代目标检测问题
性能受限于后处理(NMS)操作,处理大量冗余框
流程:
①用卷积神经网络提取特征
②将特征拉直,送入Transformer的encoder、decoder,进一步学习全局信息,将每一个点的信息与全局做交互
③生成框输出,通过object query限制框的个数
④使用二分图匹配计算Loss,计算预测框和GT Box的matching loss决定预测框中对应GT Box的框;选择后计算分类损失、Bounding Box Loss;没有匹配上的框被标记为背景
相关工作:
两阶段目标检测:proposal
单阶段目标检测:anchor、center
基于集合:
关系型网络、Learnable NMS利用类似于自注意力的方法去处理物体间联系
无需后处理
性能低,使用了人工干预
基于Encoder Decoder:
得到更全局的信息
并行输出目标框,时效性增强
Encoder:
代码结构与图对应 :
(0): TransformerEncoderLayer(
(self_attn): MultiheadAttention(
(out_proj): NonDynamicallyQuantizableLinear(in_features=256, out_features=256, bias=True)
)
(linear1): Linear(in_features=256, out_features=2048, bias=True)
(dropout): Dropout(p=0.1, inplace=False)
(linear2): Linear(in_features=2048, out_features=256, bias=True)
(norm1): LayerNorm((256,), eps=1e-05, elementwise_affine=True)
(norm2): LayerNorm((256,), eps=1e-05, elementwise_affine=True)
(dropout1): Dropout(p=0.1, inplace=False)
(dropout2): Dropout(p=0.1, inplace=False)
)
# 图像特征和位置编码相加得到了q、k
q = k = self.with_pos_embed(src, pos)
# value还是特征
src2 = self.self_attn(q, k, value=src, attn_mask=src_mask,
key_padding_mask=src_key_padding_mask)[0]
改进后的Decoder:
# 将encoder出来的4个值传入decoder进行处理
hs = self.decoder(tgt, memory, memory_key_padding_mask=mask,
pos=pos_embed, query_pos=query_embed)
代码结构与图对应:
(0): TransformerDecoderLayer(
(self_attn): MultiheadAttention(
(out_proj): NonDynamicallyQuantizableLinear(in_features=256, out_features=256, bias=True)
)
(multihead_attn): MultiheadAttention(
(out_proj): NonDynamicallyQuantizableLinear(in_features=256, out_features=256, bias=True)
)
(linear1): Linear(in_features=256, out_features=2048, bias=True)
(dropout): Dropout(p=0.1, inplace=False)
(linear2): Linear(in_features=2048, out_features=256, bias=True)
(norm1): LayerNorm((256,), eps=1e-05, elementwise_affine=True)
(norm2): LayerNorm((256,), eps=1e-05, elementwise_affine=True)
(norm3): LayerNorm((256,), eps=1e-05, elementwise_affine=True)
(dropout1): Dropout(p=0.1, inplace=False)
(dropout2): Dropout(p=0.1, inplace=False)
(dropout3): Dropout(p=0.1, inplace=False)
)
解码器部分的第一部分用到的是Multi-Head Self-Attention ,而第二部分用到的则是Multi-Head Attention
第一个MHSA:
q = k = self.with_pos_embed(tgt, query_pos)
# tgt当作第一个value进行self-aattention
# 计算每个目标表示与其他目标表示之间的注意力分数
tgt2 = self.self_attn(q, k, value=tgt, attn_mask=tgt_mask,
key_padding_mask=tgt_key_padding_mask)[0]
第二个MHA:
# 通过多头自注意力层计算目标表示和图像特征表示之间的注意力分数
tgt2 = self.multihead_attn(
# q:第一个attention的输出+位置编码
query=self.with_pos_embed(tgt, query_pos),
# k:encoder输出+position embedding(空间位置编码)
key=self.with_pos_embed(memory, pos),
# v:encoder输出(memory)
value=memory,
attn_mask=memory_mask,
key_padding_mask=memory_key_padding_mask)[0]
原因:
Queries 内部先进行协同工作(通过 MHSA)
然后再与图像特征交互提取有用的信息(通过 MHA)
DETR:
class DETR(nn.Module):
def __init__(self, num_classes, hidden_dim, nheads, num_encoder_layers, num_decoder_layers, num_queries):
super(DETR, self).__init__()
# Encoder
self.transformer = TransformerEncoder(d_model=hidden_dim, nhead=nheads, num_encoder_layers=num_encoder_layers)
# Decoder
self.transformer_decoder = TransformerDecoder(d_model=hidden_dim, nhead=nheads, num_decoder_layers=num_decoder_layers)
# Object queries
self.query_embed = nn.Embedding(num_queries, hidden_dim)
# Prediction heads
self.class_embed = nn.Linear(hidden_dim, num_classes + 1) # +1 for background
self.bbox_embed = MLP(hidden_dim, hidden_dim * 4, 4, 3)
def forward(self, images):
# 假设images已经通过CNN处理成[batch_size, num_channels, H, W]
# 这里略过CNN部分,直接模拟CNN输出特征
src = ... # [batch_size, src_seq_len, hidden_dim],其中src_seq_len是特征图的序列长度
# 目标查询
hs = self.query_embed.weight.unsqueeze(0).repeat(images.shape[0], 1, 1) # [batch_size, num_queries, hidden_dim]
# Encoder
memory = self.transformer(src)
# Decoder
tgt = torch.zeros_like(hs) # 初始化为零的目标查询
outputs = self.transformer_decoder(tgt, memory, tgt_mask=None, memory_key_padding_mask=None)
# 输出预测
outputs_class = self.class_embed(outputs)
outputs_coord = self.bbox_embed(outputs).sigmoid() # 假设使用sigmoid来限制坐标范围
return outputs_class, outputs_coord
基于集合的目标函数:
使用cost matrix:类似于三个工人分配三个工作,使每个工人被分配其最擅长的工作
使用scipy包中提供的linear-sum-assignment函数,将cost matrix作为函数输入
a、b、c看作100个框,x、y、z看作GT框
损失函数:
包含分类Loss和出框Loss
为了使两个Loss在相近的取值空间,将log去除,得到更好的效果
在Bounding Box中,使用L_1 Loss可能会产生问题,于是加入了genralized IoU Loss(与框大小无关)
细节:为了让模型收敛更快,训练的更稳定,在Decoder后加入很多auxiliary loss(额外的目标函数);