DERT目标检测—End-to-End Object Detection with Transformers
DERT:使用Transformer的端到端目标检测
论文题目:End-to-End Object Detection with Transformers
官方代码:https://github.com/facebookresearch/detr
论文题目中包括的一个创新点End to End(端到端的方法)简单的理解就是没有使用到NMS等后处理操作来处理生成的多个重复的框
简单的端到端的目标检测系统
引言与概述
We present a new method that views object detection as a
direct set prediction problem.
将目标检测任务直接看成是集合预测的问题。
- 提出了一个全局的二分图匹配的损失函数:a set-based global loss that forces unique predictions via bipartite matching
- 结合了Transformer结构,在解码器的部分进行并行的出框。
在引言的部分论文中简单概括了之前的目标检测所用到的一些方法。
- 双阶段目标检测的算法: FasterRcnn
- 单阶段目标检测的算法:Yolo
- 基于中心点进行生成的算法:CenterNet
在没有深入的学习目标检测网络具体的细节之前。对这一个过程进行一.个直观的信息描述,
-
首先经过一个CNN网络提取一部分的特征得到对应的特征图,并将得到的特征进行拉直处理。准备送入之后的Transformer结构中去。
-
将拉直之后的token送入编码器的结构部分,(endcode去进一步学习全局的特征信息。为我们decode出预测框的部分做铺垫。)使用endcode可以认为是将图片中的每一个点和其他的点之间就有交互信息了。就可以知道大概那一块是那个物体。
-
对同一个物体就出一个检测框的结果。通过query和我们的特征就可以确定要出多少检测框(论文中固定出框数为100)
-
最后一步就是我得出的这100个框,如何和我的Ground Truth框之间做一个关联匹配问题呢
计算loss
没有匹配的框则会标记为没有物体。
目标检测相关工作
Most modern object detection methods make predictions relative to some ini-tial guesses. Two-stage detectors [37,5] predict boxes w.r.t. proposals, whereas single-stage methods make predictions w.r.t. anchors [23] or a grid of possible object centers [53,46]. Recent work [52] demonstrate that the final performance of these systems heavily depends on the exact way these initial guesses are set.
在之前的目标检测的相关工作中作者就提到了,之前相关的目标检测的工作,取决于我们的先验猜测,双阶段的候选框proposals,单阶段的anchors于 centernet的中心点检测取决于,中心点选取的位置。
从而提出了一种新的方法,基于集合的方式来做这个目标检测的任务。
目标函数
DETR infers a fixed-size set of N predictions, in a single pass through the decoder, where N is set to be significantly larger than the typical number of objects in an image N=100
二分图匹配问题+匈牙利算法。找到一个唯一解使得最后可以完成最后的一个分配。(代价矩阵的构建就可以看成是,将100个预测框和Ground Truth框之间进行二分图匹配)
σ ^ = arg min σ ∈ S N ∑ i N L match ( y i , y ^ σ ( i ) ) \hat{\sigma}=\underset{\sigma \in \mathfrak{S}_{N}}{\arg \min } \sum_{i}^{N} \mathcal{L}_{\text {match }}\left(y_{i}, \hat{y}_{\sigma(i)}\right) σ^=σ∈SNargmini∑NLmatch (yi,y^σ(i))
最后匹配完成之后就可以和之后的目标检测差不多的损失函数。
L Hungarian ( y , y ^ ) = ∑ i = 1 N [ − log p ^ σ ^ ( i ) ( c i ) + 1 { c i ≠ ∅ } L box ( b i , b ^ σ ^ ( i ) ) ] \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] LHungarian (y,y^)=i=1∑N[−logp^σ^(i)(ci)+1{ci=∅}Lbox (bi,b^σ^(i))]
简单的看也就是分类损失加上回归损失的表达形式。
这一个更为详细的图里面就引出了另外的一个十分重要的概念object Queries(是一个可学习的参数)在经过学习之后就可以确定出哪些查询会对应哪些目标,从而避免重复的操作。
连接一个分类头完成最终的结果的一个预测。论文中给出的简化的版本代码编写。
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)
self.transformer = nn.Transformer(hidden_dim, nheads,
num_encoder_layers, num_decoder_layers)
self.linear_class = nn.Linear(hidden_dim, num_classes + 1)
self.linear_bbox = nn.Linear(hidden_dim, 4)
self.query_pos = nn.Parameter(torch.rand(100, hidden_dim))
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)
网络模型结构
主干网络与预处理的部分
根据论文官方的代码对模型的结构进行说明:
-
输入时一个800 x 1066的三通道图片,将其输入到主干网络提取器ResNet50中进行特征的提取 得到的特征图大小是 25x34(下采样了32倍) 将通道数拓展为2048。
-
将得到的特征图经过一个1x1的卷积层输入的通道数是2048 输出的通道数是 256得到了**[ 25 34 256]的结构**
-
将最后的两个维度进行一个展平的操作步骤得到了 [850 ,256]的结构
其中的850就是我们后面使用的Transformer中token的个数,256即为特征向量的长度。
Transformer结构部分
论文中也给出了一个改进之后的Transformer结构。结构之前的Transfomer结构给出类比的结果。
在标准的Transformer中位置编码只作用在输入的位置处,并且只作用一次。而在DERT的Transformer中位置编码是在每一个编码器,和解码器的部分都需要操作一次的。
学习这个网络模型的难点就在需要注意,模型之间的连线来确定好各个Q K V是通过哪些变量的计算来生成的(结合源码)
解读细节
Encoder完成的任务得到各个目标的注意力结果,准备好特征,等解码器来选
一组参考点的编码器自注意力。 编码器能够分离各个实例。 使用基线 DETR 模型对验证集图像进行预测。
在论文的结构图中解码器的部分也需要注意一些实现的细节步骤。
解码器部分虽然用到了两个自注意力机制,但是第一部分用到的是多头自注意力机制,而第二部分用到的则是,多头注意力机制
原因第二部分的Q来自下面一层的一个输出,而K和V的部分则来自编码器的输出,因此不能称为自注意力机制。
解码器的第一部分:个人感觉可以简单的理解为通过我们初始化的Object Queries查询向量
这一个可以学习的参数来整合查询的过程,告诉第二部分,每一个查询向量应该对应那一部分的区域信息。
将它作为一个查询向量Q输入到下一层和编码器中包括的信息(如上图所示)进行整合 最后进行分类和回归得到最后的结果。得到的长度为100
损失函数
- 从100个预测框中,找出和真实标注框所匹配的N个框(图中对应的是两个框),也就是说我们在训练集样本中标注了几个框,就需要在那100个得到的预测框中筛选出几个框(N)来进行匹配
我们需要做的任务就是向代价矩阵中进行填值使得匹配的结果最为合适
−
1
{
c
i
≠
∅
}
p
^
σ
(
i
)
(
c
i
)
+
1
{
c
i
≠
∅
}
L
box
(
b
i
,
b
^
σ
(
i
)
)
.
-\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) .
−1{ci=∅}p^σ(i)(ci)+1{ci=∅}Lbox (bi,b^σ(i)).
我们首先看公式的前半部分:即为对应的类别损失:Class Cost
− 1 { c i ≠ ∅ } p ^ σ ( i ) ( c i ) -\mathbb{1}_{\left\{c_{i} \neq \varnothing\right\}} \hat{p}_{\sigma(i)}\left(c_{i}\right) −1{ci=∅}p^σ(i)(ci)
-
首先要提取出GT中的坐标框对应的类别信息(第一张图有两个框,第二张图中有四个框。值为类别编号)
-
对应两个图片给出的200个预测框的值(2N)我们将其进行拼接,计算出包含真实类别的概率值。
在计算的时候Cost class这个张量需要加符号用来计算损失函数的值
- 第二部分我们对应的是边界框回归的一个损失。
1 { c i ≠ ∅ } L box ( b i , b ^ σ ( i ) ) \mathbb{1}_{\left\{c_{i} \neq \varnothing\right\}} \mathcal{L}_{\text {box }}\left(b_{i}, \hat{b}_{\sigma(i)}\right) 1{ci=∅}Lbox (bi,b^σ(i))
论文中关于回归损失函数的描述信息为:
Bounding box loss. The second part of the matching cost and the Hungarian
loss is Lbox(·) that scores the bounding boxes. Unlike many detectors that do box
predictions as a ∆ w.r.t. some initial guesses, we make box predictions directly.
While such approach simplify the implementation it poses an issue with relative
scaling of the loss. The most commonly-used 1 loss will have different scales for
small and large boxes even if their relative errors are similar. To mitigate this
issue we use a linear combination of the 1 loss and the generalized IoU loss [38]
Liou(·, ·) that is scale-invariant. Overall, our box loss is Lbox(bi, ˆbσ(i)) defined as
λiouLiou(bi, ˆbσ(i)) + λL1||bi − ˆbσ(i)||1 where λiou, λL1 ∈ R are hyperparameters.
These two losses are normalized by the number of objects inside the batch.
总结一下:也就如果和之前一样使用常规的L1损失来作为回归损失,可能会导致,大小检测框的相对计算一致。因此在这个基础上引出了GIOU损失与L1损失相结合的最终回归损失部分。
λ iou L iou ( b i , b ^ σ ( i ) ) + λ L 1 ∥ b i − b ^ σ ( i ) ∥ 1 \lambda_{\text {iou }} \mathcal{L}_{\text {iou }}\left(b_{i}, \hat{b}_{\sigma(i)}\right)+\lambda_{\mathrm{L} 1}\left\|b_{i}-\hat{b}_{\sigma(i)}\right\|_{1} λiou Liou (bi,b^σ(i))+λL1 bi−b^σ(i) 1
match操作
− 1 { c i ≠ ∅ } p ^ σ ( i ) ( c i ) + 1 { c i ≠ ∅ } L box ( b i , b ^ σ ( i ) ) -\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) −1{ci=∅}p^σ(i)(ci)+1{ci=∅}Lbox (bi,b^σ(i))
L box ( ⋅ ) = λ iou L iou ( b i , b ^ σ ( i ) ) + λ L1 ∥ b i − b ^ σ ( i ) ∥ 1 \mathcal{L}_{\text {box }}(\cdot)=\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} Lbox (⋅)=λiou Liou (bi,b^σ(i))+λL1 bi−b^σ(i) 1
-
我们对应代码部分实际的计算步骤就是:cost = -cost_class + 5 × cost_bbor - 2 × cost_GIoUs
-
把计算得到的结果填写入矩阵之中,就可以得到两个图片总的代价矩阵,我们在使用split操作将其分开得到两个代价矩阵的结果。
(分别进行匈牙利匹配)
计算损失并反向传播
在这个地方论文中提出了一个新的损失函数。—匈牙利损失函数。使用筛选出的预测框与真实标注框计算损失。
L Hungarian ( y , y ^ ) = ∑ i = 1 N [ − log p ^ σ ^ ( i ) ( c i ) + 1 { c i ≠ ∅ } L box ( b i , b ^ σ ^ ( i ) ) ] \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] LHungarian (y,y^)=i=1∑N[−logp^σ^(i)(ci)+1{ci=∅}Lbox (bi,b^σ^(i))]
和之前代价矩阵计算所用的那个函数其实差不多(类别损失+坐标损失)。区别主要在于一下几点。
- 这里在计算类别损失的时候我们是使用N也就是100个预测框来参与运算。而不是只计算标注类别的损失。
- 加了 log也就是使用交叉熵损失函数(计算平均值)
- 中间层的输出也是参与了损失计算的。(主网络损失+网络中间层的损失)
用预测结果与真实的结果计算交叉熵损失(所有的框 92代表背景)
在回归损失中,公式也给出了只使用真实的标注框不含背景。
1 { c i ≠ ∅ } L box ( b i , b ^ σ ^ ( i ) ) \mathbb{1}_{\left\{c_{i} \neq \varnothing\right\}} \mathcal{L}_{\text {box }}\left(b_{i}, \hat{b}_{\hat{\sigma}}(i)\right) 1{ci=∅}Lbox (bi,b^σ^(i))
最后就可以得到最终的结果了:结合反向传播对整个网络进行训练和优化
L Hungarian ( y , y ^ ) = ∑ i = 1 N [ − log p ^ σ ^ ( i ) ( c i ) + 1 { c i ≠ ∅ } L box ( b i , b ^ σ ^ ( i ) ) ] \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] LHungarian (y,y^)=i=1∑N[−logp^σ^(i)(ci)+1{ci=∅}Lbox (bi,b^σ^(i))]