一、文章介绍
detr是Facebook AI Research在2020年所提出的一种新型视觉物体检测方法,通过在全卷积神经网络中使用transformer编码器-解码器结构,将物体检测任务从先前的两阶段目标检测方法转变为一阶段的end-to-end方法,可以在保持预测准确性的同时提高检测效率。本文将介绍detr的核心思想、模型结构及其训练方法,以及与常见的目标检测方法的比较。
二、一阶段end-to-end物体检测
我们知道,传统目标检测方法一般都是两阶段的。首先,使用区域提取算法生成包含物体的候选窗口,然后将这些窗口送入分类器和回归器中进行分类和位置估计。这种方法一般效果很好,但是速度慢且复杂。而detr使用了一阶段的end-to-end方法,直接在图像上预测物体的位置和类别。这样一来,第一阶段的计算可以被跳过,检测速度大大提高。
三、Transformer结构的应用
detr中的核心思想是使用transformer打包特征图,然后使用一个解码器输出检测结果。利用transformer的自注意力机制,detr能自动提取特征图中的重要特征,使空间信息得到了更好的利用。同时,transformer使得编码器-解码器框架可以应用于物体检测任务。
四、模型结构
detr的模型结构是一个全卷积神经网络,由一个编码器和一个解码器组成。输入是一张图像,经过卷积神经网络进行特征提取,然后传入transformer编码器中进行特征图打包,最终由解码器输出检测结果,即每个目标包含类别和边界框。detr使用5个编码器层和6个解码器层,每个解码器层都将encoder输出和自己的前一个输出输入transformer中。
class DETR(nn.Module):
def __init__(self, backbone, num_classes, num_queries):
super().__init__()
self.num_queries = num_queries
self.query_embed = nn.Embedding(num_queries, self.hidden_dim)
self.transformer = nn.Transformer(
d_model=self.hidden_dim,
nhead=self.nhead,
num_encoder_layers=self.num_encoder_layers,
num_decoder_layers=self.num_decoder_layers,
dim_feedforward=self.dim_feedforward,
dropout=self.transformer_dropout,
activation=self.activation,
normalize_before=self.normalize_before,
)
self.input_proj = nn.Conv2d(backbone.num_channels, self.hidden_dim, kernel_size=1)
self.backbone = backbone
self.class_embed = nn.Linear(self.hidden_dim, num_classes + 1)
self.bbox_embed = MLP(hidden_dim, hidden_dim, 4, 3)
五、训练方法
detr的训练是一个端到端的过程,梯度由所有步骤共同计算。Cityscapes和COCO数据集是在训练过程中使用的常见数据集,在COCO数据集上可以达到较好的表现。为了提高模型的准确性,detr使用了多任务学习,并使用了一个匈牙利算法为每个预测框匹配真实目标或背景类别。为了提高训练效率,作者使用了分布式训练并使用了warmup策略逐渐增加学习率。
六、实验结果与对比
实验结果表明,detr在COCO数据集上的表现相当出色。mAP@50增加了4.2%(从42.0%到46.2%),并且在只有一个物体的图像中的表现也特别好。同时,与Faster R-CNN等目标检测方法相比,detr有着更好的检测速度。另外,detr也被证明在其他形式的目标检测中具有灵活性。
七、总结
detr等一阶段end-to-end物体检测方法的出现将彻底改变目标检测的方式。detr不仅提高了检测速度,还在准确率方面达到了很好的性能。但是,detr也存在一些限制,例如对小物体的检测效果不佳等。但是,这个方向仍然具有很大的潜力和研究价值。
完整代码
# 定义模型
class DETR(nn.Module):
def __init__(self, backbone, num_classes, num_queries):
super().__init__()
self.num_queries = num_queries
self.query_embed = nn.Embedding(num_queries, self.hidden_dim)
self.transformer = nn.Transformer(
d_model=self.hidden_dim,
nhead=self.nhead,
num_encoder_layers=self.num_encoder_layers,
num_decoder_layers=self.num_decoder_layers,
dim_feedforward=self.dim_feedforward,
dropout=self.transformer_dropout,
activation=self.activation,
normalize_before=self.normalize_before,
)
self.input_proj = nn.Conv2d(backbone.num_channels, self.hidden_dim, kernel_size=1)
self.backbone = backbone
self.class_embed = nn.Linear(self.hidden_dim, num_classes + 1)
self.bbox_embed = MLP(hidden_dim, hidden_dim, 4, 3)
def forward(self, x):
hs = self.backbone(x)
hs = self.input_proj(hs)
bs, c, h, w = hs.shape
hs = hs.flatten(2).permute(2, 0, 1)
query_embed = self.query_embed.weight.unsqueeze(1).repeat(1, bs, 1)
memory = self.transformer_encoder(hs)
hs = self.transformer_decoder(query_embed, memory)
return hs.transpose(0, 1), self.class_embed(hs), self.bbox_embed(hs).sigmoid