End-to-End Object Detection with Transformers

End-to-End Object Detection with Transformers_第1张图片
本文参考:端到端目标检测DETR
论文:DETR
代码:代码
目录
DETR
 1.1 前言
  1.1.1 研究动机:端到端目标检测的意义
  1.1.2 简介
 1.2 相关工作
 1.3 算法
  1.3.1 目标函数
  1.3.2 模型结构
  1.3.3 代码
 1.4 实验

1.1 前言
1.1.1 研究动机:端到端目标检测的意义

  DETR(DEtection TRansformer)是2020.5发布在Arxiv上的一篇论文,可以说是近年来目标检测领域的一个里程碑式的工作。从论文题目就可以看出,DETR其最大创新点有两个:end-to-end(端到端)和 引入Transformer。
  目标检测任务,一直都是比图片分类复杂很多,因为需要预测出图片中物体的位置和类别。以往的主流的目标检测方法都不是端到端的目标检测,因为:会加入很多的先验知识,预先生成一些锚框。比如one-stage方法(YOLO系列)中的Anchor模板;two-stage(R-CNN系列)中的proposal。这些方法都不是直接预测物体,而是利用anchor/proposal去做近似,最后都会生成大大小小很多的预测框,必须在后处理时使用NMS去除这些冗余的框。
  正是因为需要很多的人工干预、先验知识(Anchor)还有NMS,所以整个检测框架非常复杂,难调参难优化,并且部署困难(NMS需要的算子普通的库不一定支持,即不是所有硬件都支持)。所以说,一个端到端的目标检测是大家一直以来梦寐以求的。
1.1.2 简介
1.DETR如何做到end-to-end

  DETR利用Transformer这种全局建模的能力,直接把目标检测视为集合预测问题(即给定一张图像,预测图像中感兴趣物体的集合)。然后使用可学习的object query替代了生成anchor的机制;使用了新的目标函数,并利用二分图匹配的方式,强制模型对每个物体只生成一个预测框,从而替代了NMS这一步。
  DETR把之前不可学习的东西(anchor、NMS)变成可学的东西,删掉了这些依赖先验知识的部分,从而得到了一个简单有效的端到端的网络。所以DETR不需要费尽心思的设计anchor,不需要NMS后处理,也就没有那么多超参需要调,也不需要复杂的算子。
  除了端到端这一点,DETR使用了 Transformer Encoder-Decoder 的架构。相比于原始的 Transformer,DETR是并行预测的(in parallel),即所有预测框是一起出框的。
2.简单架构
End-to-End Object Detection with Transformers_第2张图片
整个模型前向流程如上,训练分四个步骤:
(1)使用CNN网络提取图片特征
(2)全局建模:图片特征拉成一维,输入Transformer Encoder中进行全局建模,进一步通过自注意力学习全局特征。 之所以使用Transformer Encoder,是因为Transformer中的自注意力机制,使得图片中的每个点(特征)都能和图片中所有其他特征做交互了,这样模型就能大致知道哪块区域是一个物体,哪块区域又是另一个物体,从而能够尽量保证每个物体只出一个预测框。所以说这种全局特征非常有利于移除冗余的框。
(3)通过Transformer Decoder 生成N个预测框set of box prediction(默认取N=100,也就是一张图固定生成100个预测框)。
(4)计算二分图匹配损失(bipartite matching loss),选出最优预测框,然后计算最优框的损失。 计算N个预测框与所有GT box(真实框)的matching
loss,然后通过二分图匹配算法来选出与每个物体最匹配的预测框。比如上图中有两个物体,那么最后只有两个框和它们是最匹配的,归为前景;剩下98个都被标记为背景(no object)。最后和之前的目标检测算法一样,计算这两个框的分类损失和回归损失。
  推理时,前三步是一样的。通过decoder生成N个预测框后,设置一个置信度阈值进行过滤,得到最终的预测框。(比如设阈值=0.7,表示只输出置信度大于0.7的预测框,剩下都当做背景框)。
  总的来说,Transformer Encoder全局建模,用于区分物体;Transformer Decoder用于描绘物体边界,将物体位置补充的更完整。
3.性能
在摘要中,作者卖了一下DETR的优点:
(1)简单性:不仅框架简单,可以进行端到端的检测,而且只要硬件支持CNN和Transformer就一定可以支持DETR。在COCO数据集上的性能,和一个 训练好的Faster R-CNN baseline是差不多的,无论从内存、速度还是精度来说。
(2)迁移性好:DETR框架可以很容易的拓展到其它任务上,比如在全景分割上的效果就很好(加个分割头就行)。

局限性:
(1)DETR对大物体检测效果不错,但是对小物体的检测效果不好(见实验4.1)。前者归功于transformer可以进行全局建模,这样无论多大的物体都可以检测,而不像anchor based方法检测大物体时会受限于anchor的尺寸。后者是因为作者只是使用了一个简单的结构,很多目标检测的针对性设计还没有使用,比如多尺度特征、针对性的检测头。
(2)训练太慢。
为了达到好的效果,作者在COCO上训练了500epoch,而一般模型训练几十个epoch就行了。
改进
  半年后提出的Deformable-DETR, 融入了多尺度特征,成功解决小物体检测效果不好的问题,还解决了训练慢的问题。
  另外DETR不仅是一个目标检测方法,还是一个拓展性很强的框架。其设计理论,就是适用于更多复杂的任务,使其更加的简单,甚至是使用一个框架解决所有问题。后续确实有一系列基于它的改进工作,比如Omni-DETR, up-DETR, PnP-DETR, SMAC-DETR, DAB-DETR, SAM-DETR, DN-DETR, OW-DETR, OV-DETR等等,将DETR应用在了目标追踪、视频领域的姿态预测、语义分割等多个视觉任务上。
1.2 相关工作
这一块介绍了三部分:
(1)介绍之前的集合预测工作
(2)如何使用Parallel Decoding让transformer可以并行预测
(3)目标检测
  集合预测:以前也有集合预测这一类的方法,也做了二分图匹配,也可以做到每个物体只得到一个预测框,而不需要NMS。但是这些方法性能低,要不就是为了提高性能加了很多人工干预,显得复杂。
  encoder-decoder:以前也有用encoder-decoder做检测,但都是17年以前的工作,用的是RNN的结构,效果和性能都不好(RNN自回归,效率慢)。
  所以对比以前的工作发现,能让DETR工作的好最主要的原因就是使用了Transformer。比如上面两点,都是backbone学的特征不够好,才需要使用很多人工干预,或者说模型效果性能都不好。 所以说DETR的成功,还是Transformer的成功。
1.3 算法
1.3.1 目标函数

  DETR 模型每次输出固定个数(N=100)的预测框,如何判断哪个预测框匹配哪个GT box呢?这就涉及到二分图匹配算法。
  在 scipy 库中,已经封装好了匈牙利算法,只需要将成本矩阵(cost matrix)输入进去就能够得到最优的排列。在 DETR 的官方代码中,也是调用的这个函数进行匹配(from scipy.optimize import linear_sum_assignment)。
  从N个预测框中,选出与M个GT Box最匹配的预测框,也可以转化为二分图匹配问题,这里需要填入矩阵的“成本”,就是每个预测框和GT Box的损失。对于目标检测问题,损失就是分类损失和边框损失组成。即:
End-to-End Object Detection with Transformers_第3张图片
但是在DETR 中,损失函数有两点小改动:
(1)去掉分类损失中的log。对于前一项分类损失,通常目标检测方法计算损失时是需要加 log的,但是 DETR 中为了保证两部分损失的数值区间接近,便于优化,选择了去掉 了log;
(2)回归损失为L1 loss+GIOU。对于后一项回归损失,通常方法只计算一个 L1 loss(预测框和真实框坐标的L1 损失)。但是L1 loss和预测框的大小有关,框越大损失越大。 DETR 中,用 Transformer 提取的全局特征对大物体比较友好,经常出一些大框,这样就不利于优化,因此作者这里还添加了一个 GIoU Loss。
1.3.2 模型结构
  作者在这部分给出了模型更详细的框架,如下图所示
End-to-End Object Detection with Transformers_第4张图片
  下面参考官网的一个demo,以输入尺寸3×800×1066为例进行前向过程:
(1)CNN提取特征([800,1066,3]→[25,34,256])
  backbone为ResNet-50,最后一个stage输出特征图为25×34×2048(32倍下采样),然后用1×1的卷积将通道数降为256;
(2)Transformer encoder 计算自注意力([25,34,256]→[850,256]
  将上一步的特征拉直为850×256,并加上同样维度的位置编码(Transformer本身没有位置信息),然后输入的Transformer encoder进行自注意力计算,最终输出维度还是850×256;
(3)Transformer decoder解码,生成预测框
  decoder输入除了encoder部分最终输出的图像特征,还有前面提到的learned object query,其维度为100×256。在解码时,learned object query和全局图像特征不停地做across attention,最终输出100×256的自注意力结果。
  这里的object query即相当于之前的anchor/proposal,是一个硬性条件,告诉模型最后只得到100个输出。然后用这100个输出接FFN得到分类损失和回归损失。
(4)使用检测头输出预测框
  检测头就是目标检测中常用的全连接层(FFN),输出100个预测框(xcenter, ycenter, w, h )和对应的类别。
(5)使用二分图匹配方式输出最终的预测框,然后计算预测框和真实框的损失,梯度回传,更新网络。

除此之外还有部分细节:
(1)Transformer-encode/decoder都有6层
(2)除第一层外,每层Transformer encoder里都会先计算object query的self-attention,主要是为了移除冗余框。这些query交互之后,大概就知道每个query会出哪种框,互相之间不会再重复(见实验)。
(3)decoder加了auxiliary loss,即每层decoder输出的100×256维的结果,都加了FFN得到输出,然后去计算loss,这样模型收敛更快。(每层FFN共享参数)
1.3.3 伪代码

import torch
from torch import nn
from torchvision.models import resnet50

class DETR(nn.Module):
    def __init__(self, num_classes, hidden_dim, nheads,
        num_encoder_layers, num_decoder_layers):
        super().__init__()
        # We take only convolutional layers from ResNet-50 model
        self.backbone = nn.Sequential(*list(resnet50(pretrained=True).children())[:-2])
        self.conv = nn.Conv2d(2048, hidden_dim, 1) # 1×1卷积层将2048维特征降到256维
        self.transformer = nn.Transformer(hidden_dim, nheads, num_encoder_layers, num_decoder_layers)
        self.linear_class = nn.Linear(hidden_dim, num_classes + 1) # 类别FFN
        self.linear_bbox = nn.Linear(hidden_dim, 4)                # 回归FFN
        self.query_pos = nn.Parameter(torch.rand(100, hidden_dim)) # object query
        # 下面两个是位置编码
        self.row_embed = nn.Parameter(torch.rand(50, hidden_dim // 2))
        self.col_embed = nn.Parameter(torch.rand(50, hidden_dim // 2))

    def forward(self, inputs):
        x = self.backbone(inputs)
        h = self.conv(x)
        H, W = h.shape[-2:]
        pos = torch.cat([self.col_embed[:W].unsqueeze(0).repeat(H, 1, 1),
       					 self.row_embed[:H].unsqueeze(1).repeat(1, W, 1),
       					 ], dim=-1).flatten(0, 1).unsqueeze(1) # 位置编码
       					 
        h = self.transformer(pos + h.flatten(2).permute(2, 0, 1),self.query_pos.unsqueeze(1))
        return self.linear_class(h), self.linear_bbox(h).sigmoid()


detr = DETR(num_classes=91, hidden_dim=256, nheads=8, num_encoder_layers=6, num_decoder_layers=6)
detr.eval()
inputs = torch.randn(1, 3, 800, 1200)
logits, bboxes = detr(inputs)

1.4 实验
1.4.1 对比 Faster RCNN

End-to-End Object Detection with Transformers_第5张图片
(1)最上面一部分是 Detectron2 实现的 Faster RCNN ,但是本文中作者使用了很多trick
(2)中间部分是作者使用了GIoU loss、更强的数据增强策略、更长的训练时间来把上面三个模型重新训练了一次,这样更显公平。重新训练的模型以+表示,参数量等这些是一样的,但是普偏提了两个点
(3)下面部分是DETR模型,可以看到参数量、GFLOPS更小,但是推理更慢。模型比 Faster RCNN 精度高一点,主要是大物体检测提升6个点AP,小物体相比降低了4个点左右
(4)参数量、计算量和推理速度之间并没有必然的关系
(5)transformer encoder/decoder层数消融试验,结果是层数越多效果越好,但是考虑到计算量,作者最后选择6层。

原文链接:https://blog.csdn.net/qq_56591814/article/details/127701119

你可能感兴趣的:(pytorch,目标检测,目标检测,深度学习,计算机视觉)