DETR论文笔记

DETR是一种基于Transformer架构的目标检测框架,通过二分图匹配实现预测与真实框的唯一对应,简化检测流程并避免后处理。该模型使用Encoder-Decoder结构,结合全局损失,能并行输出预测结果,且可应用于全景分割任务。
摘要由CSDN通过智能技术生成

DETR论文阅读

1. 贡献概述

​ 文章提出一种基于Transformer架构的目标检测框架,并表现出了和Faster RCNN相当的性能。利用Encoder-Decoder结构简化了检测流程,并在此基础上提出一种集合的全局损失,通过二部匹配实现唯一预测。它的流程为:给定Quary学习对象集,DETR推理学习对象与全局的上下文关系,并行输出最终预测集。其贡献可以描述为:

  • 提出了一套简洁的基于Transformer架构的框架
  • 并行预测解码方式
  • 提出一种损失集合,用二部匹配的方式实现预测集到真实框之间的匹配
  • 可迁移到全景分割任务

二分图匹配(bipartite matching):图的顶点可以分为两个集合,每条边应对的顶点分别属于这两个集合,G=[V,E],结点集合V可以分割为互不相交的子集(V1,V2)。匹配则用的匈牙利算法寻找最多边数,从而实现最大匹配。
在这里插入图片描述

2. 相关工作

直接集预测:

​ 为了避免接近的框的重复预测,采用NMS来抑制这些框,Transformer的直接集合预测是无后处理的。前人的方法采用RNN或者Linear实现恒定大小的集合预测,成本很高,对于RNN这些损失函数应保持预测排序不变。匈牙利算法被用于解决预测和真值之间的二部匹配问题。保证了每种元素都有一个唯一的匹配。

Transformer:

​ 这种基于注意力机制的网络,能够得到全局输入的序列信息。Transformer最早被用于代替序列预测,之后用于Seq2Seq任务,以及文本生成、以及语音领域、机器翻译、词表学习和语音识别。

bipartite matching:
  • 使用卷积或者全卷积结合NMS
  • 使用非唯一匹配(non-unique assignment)在真值和预测值之间,并使用了NMS
  • 利用可学习的NMS方法与关系网络以及注意力显式地构建不同预测之间的关系

3. 网络结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LV6SWmQT-1684417021803)(C:\Users\yufeixie\AppData\Roaming\Typora\typora-user-images\image-20230518161343957.png)]

  • 目标检测集预测损失

​ 通过decoder预测一组固定大小的N个预测,其中N应明显大于图像中的典型对象数量。二分图匹配实现在预测框与真实框之间的匹配,最低的搜索成本可以建模为:
σ ^ = a r g m i n ∑ i N L m a t c h ( y i , y σ ( i ) ^ ) \hat{\sigma} = argmin \sum_{i}^{N} L_{match}(y_i,\hat{y_{\sigma(i)}}) σ^=argminiNLmatch(yi,yσ(i)^)
在计算成本时,类别和框的预测都被考虑其中,其中每个真实框的表示可以为:(其中 c i c_i ci为类别, b i b_i bi为中心坐标以及相对于原图的相对尺寸wh)
y i = ( c i , b i ) y_i = (c_i,b_i) yi=(ci,bi)
最终的 L m a t c h L_{match} Lmatch可以表示为:
L m a t c h ( y i , y σ ( i ) ^ ) = − l c i ≠ ⊘ p σ i ^ ( C i ) + l c i ≠ ⊘ L b o x ( b i , b σ ( i ) ^ ) L_{match}(y_i,\hat{y_{\sigma(i)}}) = -l_{c_{i} \neq \oslash} \hat{p_{\sigma_{i}}}(C_i) + l_{c_{i} \neq \oslash}L_{box}(b_i,\hat{b_{\sigma(i)}}) Lmatch(yi,yσ(i)^)=lci=pσi^(Ci)+lci=Lbox(bi,bσ(i)^)

  • CNN backbone+ Encoder-Decoder Transformer + FFN

CNN backbone: 用于降低输入分辨率,将[3,h,w] --> [2048,h/32,w/32]

Encoder-Decoder Transformer: 首先通过一个1* 1卷积进行降维成[d,h,w],之后再将后面两个维度合并成为[d,w * h],通过位置编码完成最终的输入,每个Encoder包含多层,每一层有多头注意力机制。在Decoder阶段主要使用可学习的object query位置编码,并且可以并行进行Encoder,在这些嵌入层上使用自注意力和编码器-解码器注意力,该模型使用它们之间的成对关系对所有对象进行全局推理,同时能够将整个图像用作上下文。

Encoder-Decoder:与普通的Transfomer结构不同,这里的二维位置编码即加入到了Encoder Input中,又加入到了Decoder的两个多头注意力中,并且Object queries也加入到了两层的注意力输入口。在实验中表明,Encoder不加任务Spatial PE也仅仅下降1.3AP。DETR在计算attention的时候没有使用masked attention,因为将特征图展开成一维以后,所有像素都可能是互相关联的,因此没必要规定mask。而src_key_padding_mask是用来将zero_pad的部分给去掉。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ADBea21s-1684417021803)(C:\Users\yufeixie\AppData\Roaming\Typora\typora-user-images\image-20230518203056808.png)]

**全景分割头:**添加了一个掩码头,它为每个预测框预测一个二进制掩码,它将每个对象的转换器解码器的输出作为输入,并计算该嵌入的多头注意力分数在编码器的输出上,以一个小的分辨率为每个对象生成 M 个注意力热图。为了做出最终预测并增加分辨率,使用了类似 FPN 的架构。

在这里插入图片描述

4. 研究细节

  • Encoder 每个decoder层都会进行预测,文章直接评价每一层预测的精度,随着层数增加性能越来越好。凭借基于集合的损失,DETR不再需要NMS操作。第一层中加入的NMS对于AP提升较大,后面逐渐减小。decoder的可视化大象结果图中,显示decoder中的注意力更倾向于注意细节和局部信息,比如大象的四肢或者脚。
  • FFN在transformers中可以认为是1x1的卷积层,使encoder更像attention增广的卷积网络
  • 两种位置编码方式:空间位置编码和输出位置编码即目标queries。输出位置编码是必须的,不可或缺的

5. 代码笔记

class DETR(nn.Module):
    """ This is the DETR module that performs object detection """
    def __init__(self, backbone, transformer, num_classes, num_queries, aux_loss=False):
        """ Initializes the model.
        Parameters:
            backbone: torch module of the backbone to be used. See backbone.py
            transformer: torch module of the transformer architecture. See transformer.py
            num_classes: number of object classes
            num_queries: number of object queries, ie detection slot. This is the maximal number of objects
                         DETR can detect in a single image. For COCO, we recommend 100 queries.
            aux_loss: True if auxiliary decoding losses (loss at each decoder layer) are to be used.
        """
        super().__init__()
        self.num_queries = num_queries
        self.transformer = transformer
        hidden_dim = transformer.d_model
        # 分类
        self.class_embed = nn.Linear(hidden_dim, num_classes + 1)
        # 回归
        self.bbox_embed = MLP(hidden_dim, hidden_dim, 4, 3)
        # self.query_embed 类似于传统目标检测里面的anchor 这里设置了100个  [100,256]
        # nn.Embedding 等价于 nn.Parameter
        self.query_embed = nn.Embedding(num_queries, hidden_dim)
        self.input_proj = nn.Conv2d(backbone.num_channels, hidden_dim, kernel_size=1)
        self.backbone = backbone
        self.aux_loss = aux_loss   # True

    def forward(self, samples: NestedTensor):
        """ The forward expects a NestedTensor, which consists of:
               - samples.tensor: batched images, of shape [batch_size x 3 x H x W]
               - samples.mask: a binary mask of shape [batch_size x H x W], containing 1 on padded pixels

            It returns a dict with the following elements:
               - "pred_logits": the classification logits (including no-object) for all queries.
                                Shape= [batch_size x num_queries x (num_classes + 1)]
               - "pred_boxes": The normalized boxes coordinates for all queries, represented as
                               (center_x, center_y, height, width). These values are normalized in [0, 1],
                               relative to the size of each individual image (disregarding possible padding).
                               See PostProcess for information on how to retrieve the unnormalized bounding box.
               - "aux_outputs": Optional, only returned when auxilary losses are activated. It is a list of
                                dictionnaries containing the two above keys for each decoder layer.
        """
        if isinstance(samples, (list, torch.Tensor)):
            samples = nested_tensor_from_tensor_list(samples)
        # out: list{0: tensor=[bs,2048,19,26] + mask=[bs,19,26]}  经过backbone resnet50 block5输出的结果
        # pos: list{0: [bs,256,19,26]}  位置编码
        features, pos = self.backbone(samples)

        # src: Tensor [bs,2048,19,26]
        # mask: Tensor [bs,19,26]
        src, mask = features[-1].decompose()
        assert mask is not None

        # 数据输入transformer进行前向传播
        # self.input_proj(src) [bs,2048,19,26]->[bs,256,19,26]
        # mask: False的区域是不需要进行注意力计算的
        # self.query_embed.weight  类似于传统目标检测里面的anchor 这里设置了100个
        # pos[-1]  位置编码  [bs, 256, 19, 26]
        # hs: [6, bs, 100, 256]
        hs = self.transformer(self.input_proj(src), mask, self.query_embed.weight, pos[-1])[0]

        # 分类 [6个decoder, bs, 100, 256] -> [6, bs, 100, 92(类别)]
        outputs_class = self.class_embed(hs)
        # 回归 [6个decoder, bs, 100, 256] -> [6, bs, 100, 4]
        outputs_coord = self.bbox_embed(hs).sigmoid()
        out = {'pred_logits': outputs_class[-1], 'pred_boxes': outputs_coord[-1]}
        if self.aux_loss:   # True
            out['aux_outputs'] = self._set_aux_loss(outputs_class, outputs_coord)
        # dict: 3
        # 0 pred_logits 分类头输出[bs, 100, 92(类别数)]
        # 1 pred_boxes 回归头输出[bs, 100, 4]
        # 3 aux_outputs list: 5  前5个decoder层输出 5个pred_logits[bs, 100, 92(类别数)] 和 5个pred_boxes[bs, 100, 4]
        return out

    @torch.jit.unused
    def _set_aux_loss(self, outputs_class, outputs_coord):
        # this is a workaround to make torchscript happy, as torchscript
        # doesn't support dictionary with non-homogeneous values, such
        # as a dict having both a Tensor and a list.
        return [{'pred_logits': a, 'pred_boxes': b}
                for a, b in zip(outputs_class[:-1], outputs_coord[:-1])]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值