深度学习之目标检测(十一)DETR详解
继 Transformer 应用于图像分类后,本章学习 Transformer 应用于图像目标检测的开山之作 – DEtection TRansformer,其大大简化了目标检测的框架,更直观。
1. 前言
DETR 是 Facebook 团队于 2020 年提出的基于 Transformer 的端到端目标检测,没有非极大值抑制 NMS 后处理步骤、没有 anchor 等先验知识和约束,整个由网络实现端到端的目标检测实现,大大简化了目标检测的 pipeline。其原始论文为 《End-to-End Object Detection with Transformers》。结果在 COCO 数据集上效果与 Faster RCNN 相当,在大目标上效果比 Faster RCNN 好,且可以很容易地将 DETR 迁移到其他任务例如全景分割。
事实上,在 DETR 之前,就有几个类似思想的工作。首先和15年的人体检测的一篇工作的思路非常相像了。然后是 ECCV2018 workshop 的一篇用 RNN + 匈牙利算法做 instance segmentation 的文章 《Recurrent Neural Networks for Semantic Instance Segmentation》,提出了一个编码器-解码器架构,它为图像中的每个对象顺序生成成对的二进制掩码和分类标签。通过连接所有 ConvLSTM 层的侧输出并应用每通道最大池化操作以获得隐藏表示,该表示将作为两个全连接层的输入,预测分类标签和停止概率,并且一个一个得到分割掩码。通过匈牙利算法来进行预测结果和 ground-truth 之间的一一匹配用于计算损失。
Transformer 比 RNN 更好,因为做到了并行化,不需要受到之前结果的影响。那么把目标检测任务也变成一个 Set Prediction 任务,即一口气预测一个集合,而不是按照 RNN 一样一个一个预测,这样做是不是会更好呢?为此,DETR 诞生了。所以说我认为 DETR 最大的贡献,在于它提出了目标检测的新范式(新理解)—Set Prediction。论文的主要改进和实验结果总结如下图所示:
2. DETR 框架
DETR 分为四个部分,首先是一个 CNN 的 backbone,Transformer 的 Encoder,Transformer 的 Decoder,最后的预测层 FFN。
2.1 CNN Backbone
CNN 的特征提取部分没有什么可以说的,在 2020 年时候,还没有 Swin 这样的可以针对不同分辨率图像输入的 Transformer Backbone。目标检测的图一般比较大,那么直接上 Transformer 计算上吃不消,所以先用 CNN 进行特征提取并缩减尺寸,再使用 Transformer 是常规操作(或者说无奈之举)。
原始 DETR 使用 Imagenet 预训练好的 Resnet,这一部分就极其多变了,可以上 Swin 等等了。那么通常 Backbone 的输出通道为 2048,图像高和宽都变为了 1/32。
2.2 Transformer Encoder
经过 Backbone 后,将输出特征图 reshape 为 $C \times HW$,因为 $C = 2048$ 是每个 token 的维度,还是比较大,所以先经过一个 $1 \times 1$ 的卷积进行降维,然后再输入 Transformer Encoder 会更好。此时自注意力机制在特征图上进行全局分析,因为最后一个特征图对于大物体比较友好,那么在上面进行 Self-Attention 会便于网络更好的提取不同位置不同大物体之间的相互关系的联系,比如有桌子的地方可能有杯子,有草坪的地方有树,有一个鸟的地方可能还有一个鸟等等。所以 DETR 在大目标上效果比 Faster RCNN 好就比较容易理解到了。然后位置编码是被每一个 Multi-Head Self-Attention 前都加入了的,这个就比较狠了。为了体现图像在 x 和 y 维度上的信息,作者的代码里分别计算了两个维度的 Positional Encoding,然后 Cat 到一起。整个 Transformer Encoder 和之前的没什么不同。
2.3 Transformer Decoder
Transformer Decoder 也有几个地方需要着重强调。首先就是如何考虑同时进行一个集合预测?之前讲分类的时候都是给一个 class token,因为只进行一类预测。那么现在同时进行不知道多少类怎么办呢?因为目标预测框和输入 token 是一一对应的,所以最简单的做法就是给超多的查询 token,超过图像中会出现的目标的个数(在过去也是先生成 2000 个框再说)。所以在 DETR 中,作者选择了固定的 N = 100 个 token 作为输入,只能最多同时检测 100 个物体。据闻,这种操作可能跟 COCO 评测的时候取 top 100 的框有关。输入 100 个 decoder query slots (Object Query),并行解码N个object,对应的 Transformer decoder 也就会输出 100 个经过注意力和映射之后的 token,然后将它们同时喂给一个 FFN 就能得到 100 个框的位置和类别分数(因为是多分类,所以类别个数是 K + 1,1 指的是背景类别)。
固定预测个数更为简单,定长的输出有利于显存对齐,但是 N = 100 会不会冗杂呢?作者的实验表明,当图像内目标个数在 50 左右的时候,网络就已经区域饱和了,之后就会出现目标丢失。当图像内目标在一百个左右时,其实网络只能检测出来三四十个,这比图像中只有 50 个实例被检测到的情况还要少。作者认为出现这样反常的原因还是因为检测结果与训练分布相差甚远,是训练集中没有那么多多目标图片所造成的。
为了提升 AP,作者也坦然说到对应推理时出现的一些预测为背景的,用第二高分的类别覆盖这些槽的预测,使用相应的置信度。但是具体是怎么选的,比如背景概率在0.7以下使用还是怎么,就从论文中不可知了…
` At inference time, some slots predict empty class. To optimize for AP, we override the prediction of these slots with the second highest scoring class, using the corresponding confidence. `
与 ViT 他们不同的另外一点是,DETR 的 Decoder 也加了 Positional Encoding。这个思想其实也很自然。当作图像分类是,其实 class token 就一个,对应整个图片,那么自然无需 positional encoding,自己把整个图都占全了。但是在做目标检测时,可能会希望不同的 Object Query 是不是对应图像中不同的位置会好一些。那么按照这个思想,Object Query 自然就是 positional encodings,也就是我就是要查询这里的物体,你预测出来的就是对应的如果有物体的话就是它的类别和位置。
怎么加,在哪里加 positional encodings? Transformer Decoder 做得比 Encoder 还要狠,不仅 encoder 用的那个 position encodings,也要给每层的 key 加上;Decoder 每一层的 query 还是加了 positional encodings (Object Query) 的。
还有一点值得注意的是:Decoder 每一层的输出结果也经过参数共享的最后的那个 FFN 进行预测并计算loss,实现 深监督。
作者给出了可视化结果,对应于 100 个 Object Query 中的 20 个 Object Query 在 COCO2017 验证集中预测得到的目标的中心点位置分布。绿色表示小物体,红色表示水平的大物体,蓝色表示竖直的大物体。可见不同的 Object Query 确实实现了想要去查询不同地方出现的小物体,比如左下,右边,右上等等。但是对于大物体而言,大家检测出来的定位是相近的。
2.4 FFN
最后的 FFN 是由具有 ReLU 激活函数且具有隐藏层的 3 层线性层计算的,或者说就是 $1 \times 1$ 卷积。FFN 预测框标准化中心坐标,高度和宽度,然后使用 softmax 函数激活获得预测类标签。
最终网络的大致推理过程如下图所示:
3. 二分图匹配和损失函数
DETR 预测了一组固定大小的 N = 100 个边界框,这比图像中感兴趣的对象的实际数量大得多。怎么样来计算损失呢?或者说预测出来的框我们怎么知道对应哪一个 ground-truth 的框呢?
为了解决这个问题,第一步是将 ground-truth 也扩展成 N = 100 个检测框。使用了一个额外的特殊类标签 $\phi$ 来表示在未检测到任何对象,或者认为是背景类别。这样预测和真实都是两个100 个元素的集合了。这时候采用匈牙利算法进行二分图匹配,即对预测集合和真实集合的元素进行一一对应,使得匹配损失最小。 \(\hat{\sigma}=\underset{\sigma \in \mathfrak{S}_{N}}{\arg \min } \sum_{i}^{N} \mathcal{L}_{\operatorname{match}}\left(y_{i}, \hat{y}_{\sigma(i)}\right) \\ \mathcal{L}_{\operatorname{match}}\left(y_{i}, \hat{y}_{\sigma(i)}\right) = -\mathbb{1}_{\left\{c_{i} \neq \varnothing\right\}} \hat{p}_{\sigma(i)}\left(c_{i}\right)+\mathbb{1}_{\left\{c_{i} \neq \varnothing\right\}} \mathcal{L}_{\text {box }}\left(b_{i}, \hat{b}_{\sigma(i)}\right)\) 我们来看看 ground truth $y_i$ 和预测出来的第 $\delta(i)$ 个结果之间的匹配损失。首先是对于那些不是背景的,获得其对应的预测是目标类别的概率,然后用框损失减去预测类别概率。这也就是说不仅框要近,类别也要基本一致,是最好的。经过匈牙利算法之后,我们就得到了 ground truth 和预测目标框之间的一一对应关系。然后就可以计算损失函数了。
损失函数和匹配损失不同之处在于,损失函数需要是正值,所以使用了 log-probability。对于 $c_i = \phi$ 的类别损失,将分类损失除了 10,降低其作用,因为正负样本不均衡。这种思想和 Faster R-CNN 等一致。目标边界框回归损失则是 IOU 损失和 L1 损失的加权和,其中 IOU 损失对于 Scale 不敏感,L1 损失对于 Scale 敏感。事实上 DETR 用的是 GIoU 损失。 \(\mathcal{L}_{\text {Hungarian }}(y, \hat{y})=\sum_{i=1}^{N}\left[-\log \hat{p}_{\hat{\sigma}(i)}\left(c_{i}\right)+\mathbb{1}_{\left\{c_{i} \neq \varnothing\right\}} \mathcal{L}_{\text {box }}\left(b_{i}, \hat{b}_{\hat{\sigma}}(i)\right)\right] \\ \mathcal{L}_{\text {box }}\left(b_{i}, \hat{b}_{\hat{\sigma}}(i)\right) = \lambda_{\text {iou }} \mathcal{L}_{\text {iou }}\left(b_{i}, \hat{b}_{\sigma(i)}\right)+\lambda_{\text {L1 }}\left\|b_{i}-\hat{b}_{\sigma(i)}\right\|_{1}\)
依然延续论文实验部分不做过多详解,主要学习核心思想的观点,故实验部分就不做过多赘述,大家可以详见论文的描述。
4. 代码
代码见 Facebook 官方实现最好不过了,代码链接点击 此处。强烈推荐搭建看看“搞笑”博主手把手代码运行教学。
5. 总结
之前还有幸接触外国博主的一种理解,说 Transformer 的 Self-Attention 对于目标检测友好的观点,个人觉得非常好。他说:Backbone 输出的特征图经过 $1 \times 1$ 卷积后进行降维,得到的是 $d \times H \times W$,被 reshape 成 $d \times HW$ 作为 Transformer Block 的输入。在 Encoder 阶段,会计算 $HW \times HW$ 的 Attention Matrix,那么其实 Attention Matrix 上每一个值,其实就是考虑了 Backbone 输出的特征图空间上的两个点,因为 token 数量和特征图空间像素个数一样,那么这两个点,其实就已经构建出来了一个 box(左上角和右下角)。从这个角度来看,神经网络在基于 Attention Matrix 进行思考时,其实也可以从某种意义上就是在对一个个 bounding box 进行思考,这对于目标检测任务似乎是非常利好的。
DETR 对大物体更好是因为在 Backbone 最后输出特征图上进行 Self-Attention。怎么结合多尺度信息例如 FPN 等提升小物体性能呢?怎么使得大小物体之间相互作用相互关联呢?DETR 直接预测多个目标框不需要 NMS,如果多个目标框在测试时出现重叠怎么办呢?
如今 Set Prediction 方式也逐渐成为目标检测的一种主流范式。当然 DETR 算法本身存在各种各样的问题,基于 DETR 模型的各种改进版纷纷出现,包括最近新出来的 Anchor + DETR 等等,留待之后继续学习。