DETR 详解

date
Apr 19, 2022
Last edited time
Mar 28, 2023 08:05 AM
status
Published
slug
DETR详解
tags
DL
CV
summary
总结了两篇文章,添加了一些注解
type
Post
Field
Plat

1. Story

其实这篇文章的创新点从标题(End-to-End Object Detection with Transformers)就可以看出来,主要有以下三个重点:
  1. Object detection
  1. Transformers
  1. End-to-end
其中第一点为研究的领域,第二点为所用的工具,第三点则是模型的优点所在。
事实上,文章所做的工作,就是将 transformers 运用到了 object detection 领域,取代了现在的模型需要手工设计的工作(例如 非极大值抑制 和 anchor generation),并且取得了不错的结果。在 object detection 上 DETR 准确率和运行时间上和 Faster RCNN 相当;将模型 generalize 到 panoptic segmentation任务上,DETR 表现甚至还超过了其他的 baseline.
notion image
如上图所示,文章的主要有两个关键的部分。
第一个是用 transformer 的 encoder-decoder 架构一次性生成 个 box prediction。其中 是一个事先设定的、比远远大于 image 中 object 个数的一个整数;
第二个是设计了 bipartite matching loss,基于预测的 boxes 和 ground truth boxes 的二分图匹配计算 loss 的大小,从而使得预测的 box 的位置和类别更接近于 ground truth.
notion image

2. Transformer

这一节介绍 Transformer 的结构,从下图可以看出,主要包括三个部分:encoder, decoder FFN。下面将分别介绍这三个部分。

2.1 Transformer Encoder

如下图所示,在 DETR 中,首先用 CNN backbone 处理 维的图像,得到 维的 feature map(一般 )。然后将 backbone 输出的 feature map 和 position encoding 相加,输入 Transformer Encoder 中处理,得到用于输入到 Transformer Decoder 的 image embedding。

cnn 骨架特征提取

骨架网络可以是任何一种,作者选择 resnet50,将最后一个 stage 即 stride=32 的特征图作为编码器输入。由于 resnet 仅仅作为一个小部分且已经经过了 imagenet 预训练,故和常规操作一样,会进行如下操作:
  • resnet 中所有 BN 都固定,即采用全局均值和方差
  • resnet 的 stem 和第一个 stage 不进行参数更新,即 parameter.requires_grad_(False)
  • backbone 的学习率小于 transformer,lr_backbone=1e-05, 其余为 0.0001
假设输入是 ,则 resnet50 输出是 ,2048 比较大,为了节省计算量,先采用 卷积降维为 256, 最后转化为序列格式输入到 transformer 中,输入

编码器设计和输入

编码器结构设计没有任何改变,但是输入改变了。
  1. 位置编码需要考虑 2d 空间
    1. 由于图像特征是 2d 特征,故位置嵌入向量也需要考虑 方向。前面说过编码方式可以采用 sincos,也可以设置为可学习,本文采用的依然是 sincos 模式,和前面说的一样,但是需要考虑 xy 两个方向 (前面说的序列只有 x 方向)。
      可以看出对于 的 2d 图像特征,不是类似 vision transoformer 做法简单的将其拉伸为 ,然后从 进行长度为 256 的位置编码,而是考虑了 方向同时编码,每个方向各编码 维向量,这种编码方式更符合图像特定。
      还有一个细节需要注意:原始 transformer 的 n 个编码器输入中,只有第一个编码器需要输入位置编码向量,但是 detr 里面对每个编码器都输入了同一个位置编码向量,论文中没有写为啥要如此修改。
  1. QKV 处理逻辑不同
    1. 作者设置编码器一共 6 个,并且位置编码向量仅仅加到 QK 中,V 中没有加入位置信息,这个和原始做法不一样,原始做法是 QKV 都加上了位置编码,论文中也没有写为啥要如此修改。
      其余地方就完全相同了,故代码就没必要贴了。总结下和原始 transformer 编码器不同的地方:
      • 输入编码器的位置编码需要考虑 2d 空间位置
      • 位置编码向量需要加入到每个编码器中
      • 在编码器内部位置编码仅仅和 QK 相加,V 不做任何处理
      经过 6 个编码器 forward 后,输出 shape 为
  1. 编码器部分整体运行流程
    1. 6 个编码器整体 forward 流程如下:
      notion image
      每个编码器内部运行流程如下:
 
从上面可以看出,DETR 不是一个完全由 Transformer 处理的架构,还是需要依赖于 CNN 作为 backbone 提取特征
这里 Transformer Encoder 的结构和 Transformer 原始论文中的结构相同,这里就不再赘述。唯一需要注意的地方就是将 CNN backbone 输出的 feature map 转化为能够被 Transformer Encoder 处理的序列化数据的过程。主要有以下几个步骤:
  1. 维度压缩:将 CNN backbone 输出的 维的 feature map 先用 convolution 处理,将 channels 数量从 压缩到 ,即得到 维的新 feature map;
  1. 转化为序列化数据:将空间的维度(高和宽)压缩为一个维度,即把上一步得到的 维的 feature map 通过 reshape 成 维的 feature map;
  1. 加上 position encoding: 由于 transformer 模型是顺序无关的,所以需要加上 position encoding 反映位置信息。生成的方法不难但讲起来比较啰嗦,所以我把它放在附录 (a) 中,大家如果感兴趣可以翻到文章末尾的附录看一下。

2.2 Transformer Decoder

Decoder结构

Transformer Decoder 结构如下图所示,结构基本上和原始的 Transformer 相同。可以看到 transformer 主要有两个输入:
notion image
  1. image embedding (由 Transformer Encoder 输出) 与 position encoding 之和;
  1. object queries
    1. 其中 image embedding 与 position encoding 上文讲过,不再赘述。这里主要讲一下 object queries.
      Object queries 有 个(其中 是一个事先设定的、比远远大于 image 中 object 个数的一个整数),输入 Transformer Decoder (using multi-headed self- and encoder-decoder attention mechanisms.) 后分别得到 个 decoder output embedding,经过 FFN(后面会讲)处理后就得到了 个预测的 boxes 和这些 boxes 的类别。具体实现上,object queries 是 个 learnable embedding,训练刚开始时可以随机初始化。在训练过程中,因为需要生成不同的 boxes,object queries 会被迫使变得不同来反映位置信息,所以也可以称为 leant positional encoding (注意和 encoder 中讲的 position encoding 区分,不是一个东西)。
此外,decoder部分还可以将位置编码作为输入 编码器环节采用的 sincos 位置编码向量也可以考虑引入,且该位置编码向量输入到每个解码器的第二个 Multi-Head Attention 中,后面有是否需要该位置编码的对比实验。
此外,和原始的 Transformer不同的是,DETR 的 Transformer Decoder 是一次性处理全部的 object queries,即一次性输出全部的 predictions;而不像原始的 Transformer 是 auto-regressive 的,从左到右一个词一个词地输出。 为了说明如何实现该功能,我们需要先回忆下原始 transformer 的顺序解码过程:输入 BOS_WORD,解码器输出 i;输入前面已经解码的 BOS_WORD 和 i,解码器输出 am…,输入已经解码的 BOS_WORD、i、am、a 和 student,解码器输出解码结束标志位 EOS_WORD, 每次解码都会利用前面已经解码输出的所有单词嵌入信息。现在就是一次解码,故只需要初始化时候输入一个全 0 的查询向量 A,类似于 BOS_WORD 作用,然后第一个解码器接受该输入 A,解码输出向量作为下一个解码器输入,不断推理即可,最后一层解码输出即为我们需要的输出,不需要在第二个解码器输入时候考虑 BOS_WORD 和第一个解码器输出。

Decoder的主要流程

n 个解码器整体流程如下:
内部每个解码器运行流程为:
解码器最终输出 shape 是 是指 个解码器的输出。

2.3 FFN(Feed-forward network)

notion image
FFN 在 DETR 中的位置如上图所示。从更细节的结构图(下图)看,其实有两种 FFN:一种预测 bounding box 的中心位置、高和宽,第二种预测 class 标签。下图是更细节的 DETR Transformer 结构,大家感兴趣可以仔细看一下。
notion image
作者实验发现,如果对解码器的每个输出都加入辅助的分类和回归 loss,可以提升性能,故作者除了对最后一个编码层的输出进行 Loss 监督外,还对其余 5 个编码器采用了同样的 loss 监督,只不过权重设置低一点而已。

3. Loss

上面我们介绍了如何用 Transformer 生成 个 prediction boxes(其中 是一个事先设定好的远远大于 image objects 个数的整数)。这里我们将介绍如何得到这 个 prediction boxes 和 image objects 的最优二分图匹配,并通过计算配对的 loss 来评价 prediction boxes 生成的好坏。

3.1 得到 prediction boxes 和 image objects 的最优二分图匹配

首先大家可能有个疑问:假设对于一张图来说,ground truth boxes 的个数(即图中 object 的个数)为 ,由于 是一个事先设定好的远远大于 image objects 个数的整数,所以 ,即生成的 prediction boxes 的数量会远远大于 ground truth boxes 数量。这样怎么做匹配?为了解决这个问题,作者人为构造了一个新的物体类别 (表示没有物体)并加入 image objects 中,上面所说到的多出来的 个 prediction embedding 就会和 类别配对。这样就可以将 prediction boxes 和 image objects 的配对看作两个等容量的集合的二分图匹配了。
只要定义好每对 prediction box 和 image object 匹配的 cost,我们就能用匈牙利算法
什么是匈牙利算法
快速地找到使总 cost 最小的二分图匹配方案。
每对 prediction box 和 image object 匹配的 cost 定义如下:
其中, 为第 个 image object 的 class 标签, 为与第 个 object 配对的 prediction box 的 index; 是一个函数,当 时为 1,否则为 0; 表示 Transformer 预测的第 个 prediction box 类别为 的概率; 分别为第 个 image object 的 box 和第 个 prediction box 的位置; 计算的是 ground truth box 和 prediction box 之间的差距(下面详细介绍)。
上式总结起来就是,当配对的 image object 为 时,我们人为规定️配对的 cost;当配对的 image object 为真实的物体(即不为 )时,如果预测的 prediction box 类别和 image object 类别相同的概率越大,或者两者的 box 差距越小,配对的 cost 越小。 的计算公式如下:
.其中 为两个 box 中心坐标的 L1 距离。如果只用 L1 距离,当 box 大小不同时,即使 L1 距离相同,差距也会不一样。例如,两个 3x3 的 box 相距 3,和两个 30x30 的 box 相距 3 的差距是完全不同的。为了解决这个问题,作者还加入了与 box 大小无关的 ,通过 IoU 计算 loss。
这样,我们就完全定义好了每对 prediction box 和 image object 配对时的 cost。再利用匈牙利算法即可得到二分图最优匹配。
这一部分是利用 得到最优二分图匹配, 而不是作为真正优化的 loss。

3.2 根据最优二分图匹配计算 set prediction loss

上面我们得到了 prediction boxes 和 image objects 之间的最优匹配。这里我们基于这个最优匹配,来计算 set prediction loss,即评价 Transformer 生成这些 prediction boxes 的效果好坏。
Set prediction loss 计算公式如下:
其中 为最佳匹配,将第 个 image object 匹配到第 个 prediction box。这个式子各个部分的含义与 计算公式完全一致,这里就不再赘述。唯一需要注意的是,这里用的是 classification probability 的对数形式,而 中直接用的线性形式;且这里考虑了被匹配到 object 的 ,而 则直接定义为 0. 用 做反向传播即可优化 Transformer。

附录

a. position encoding 的生成方式

文中的position encoding采用原始Transformer论文的固定position encoding,即对于每个  维向量,用不同频率的sin函数对高(  )这个维度生成  维的position encoding,用不同频率cos函数对宽(  )这个维度生成  维的position encoding,然后将两个  维的position encoding concat成  维的position encoding。

b. object queries 的推断过程

论文附录中有一段 Inference 阶段 DETR 的 PyTorch 代码:
notion image
当然上面是简化代码,和实际代码不一样。具体流程是:
  • 图片输入到 resnet50 中进行特征提取, 输出
  • 通过 卷积降维,变成
  • 利用 函数计算位置编码
  • 将图像特征和位置编码向量相加,作为编码器输入,输出编码后的向量, 不变
  • 初始化全 0 的 的输出嵌入向量,结合位置编码向量和 query_embed,进行解码输出,解码器输出 shape 为,后面的解码器接受该输出,然后再次结合置编码向量和 query_embed 进行输出,不断前向
  • 将最后一个解码器输出输入到分类和回归 head 中,得到 100 个无序集合
  • 对 100 个无序集合进行后处理,主要是提取前景类别和对应的 bbox 坐标,乘上 即可得到最终坐标, 后处理代码如下:
既然训练时候对 6 个解码器输出都进行了 loss 监督,那么在测试时候也可以考虑将 6 个解码器的分类和回归分支输出结果进行 nms 合并,稍微有点性能提升。

2.2.4 小结

detr 整体做法非常简单,基本上没有改动原始 transformer 结构,其显著优点是:不需要设置啥先验,超参也比较少,训练和部署代码相比 faster rcnn 算法简单很多,理解上也比较简单。但是其缺点是:改了编解码器的输入,在论文中也没有解释为啥要如此设计,而且很多操作都是实验对比才确定的,比较迷。算法层面训练 epoch 次数远远大于 faster rcnn(300epoch),在同等 epoch 下明显性能不如 faster rcnn,而且训练占用内存也大于 faster rcnn
整体而言,虽然效果不错,但是整个做法还是显得比较原始,很多地方感觉是尝试后得到的做法,没有很好的解释性,而且最大问题是训练 epoch 非常大和内存占用比较多,对应的就是收敛慢,期待后续作品。

参考

  1. Carion N, Massa F, Synnaeve G, et al. End-to-End Object Detection with Transformers[J]. arXiv preprint arXiv:2005.12872, 2020.
  1. Zhu X, Su W, Lu L, et al. Deformable DETR: Deformable Transformers for End-to-End Object Detection[J]. arXiv preprint arXiv:2010.04159, 2020.
  1. Kirillov A, He K, Girshick R, et al. Panoptic segmentation[C]//Proceedings of the IEEE conference on computer vision and pattern recognition. 2019: 9404-9413.
  1. bcdefVaswani A, Shazeer N, Parmar N, et al. Attention is all you need[C]//Advances in neural information processing systems. 2017: 5998-6008.
  1. Parmar N, Vaswani A, Uszkoreit J, et al. Image transformer[J]. arXiv preprint arXiv:1802.05751, 2018.

© Lazurite 2021 - 2024