DETR原理解读
原理解读

DETR任务是Object detection,用到的工具是transformers,特点是End-to-end。而目标检测的任务是要预测一系列的Bounding Box的坐标及Label,当下大多数检测器通过定义一些proposal或者anchor,将问题构建成一个分类及回归问题来间接完成这个任务。DETR的工作则是将transfoemers运用到目标检测领域,取代了现在模型需要手工设计的工作,并且取得了不错的结果。 DETR是第一个使用端到端的方法解决检测问题,解决的方法是检测问题视为一个set prediction 问题,如下图所示:

网络的主要组成:CNN和Transformer。

DETR工作两个关键部分:

  • 用transformer的encoder-decoder架构一次性生成N个box prediction。(N是一个事先设定的、远远大于image中object个数的一个整数)
  • 设计了bipartite matching loss,基于预测的box和ground truthboxes的二分图匹配计算loss大小,从而使得预测的box位置和类别更接近于ground truth。

DETR整体结构可以分为4个部分:(如下图所示)

  • backbone

  • encoder

  • decoder

  • FFN

  1. backbone: CNN backbone处理 维的图像,把它转换为维的feature map。

  2. encoder: encoder的输入是 维的feature map,接下来一次进行下面过程:

    • 通道数压缩: 通过1*1卷积处理,将channels数量从C压缩到d,即得到 维的新feature map。
    • 转化为序列化数据: 将空间的维度(高和宽)压缩为一个维度,即把 维的新feature map通过reshape成(HW,B,256)维的feature map。
    • 位置编码: 在得到 维的feature map后,正式输入encoder之前,需要进行__Positional Encoding__。因为在__self-attention中需要有表示位置的信息,但是transformer encoder这个结构本身无法体现出位置信息__。所以我们需要对 维的feature map做positional encoding。

    原版本Transformer与Vision Transformer中Positional Encoding表达式为: $$ PE_{(pos,2i)} = sin(pos / 10000^{2i/d}),

    PE_{(pos,2i+1)} = cos(pos / 10000^{2i/d}) $$ 其中,d就是d*HW维的feature map的第一维,。表示token在sequence中的位置,sequence的长度是HW,例如第一个token的pos=0。

    2i和2i+1表示了Positional Encoding的维度,i的取值范围为[0,...,d/2)。所以当pos为1时,Positional Encoding可以写为: 式子中,d=256。

    __不同点1:因为Transformer原版中只需要考虑x方向的位置编码,而DETR需要考虑xy方向的位置编码(图像特征是2-D特征),考虑xy方向进行同时编码__Positional Enconding的输出张量:(B,d,H,W),d = 256,其中d代表位置编码长度,H,W代表张量位置。意思为,这个特征图上的任意一个点(H1,W1)有个位置编码,这个编码的长度为256,其中,前128维代表H1的位置编码,后128维代表W1的位置编码。

    任意一个位置 的Positional Encoding,通过公式(3)(4)可以得到128维向量,代表 的位置编码,通过带入带入公式(5)(6)可以得到一个128维向量,代表的位置编码,将这两个128维的向量拼接起来,可以得到一个256维的向量,代表的位置编码。

    计算所有位置的编码后就可以得到(256,H,W)的张量,代表这个batch的位置编码。编码矩阵的维度是(B,256,H,W),也把其序列化为维度为(HW,B,256)维的张量。准备与__(HW,B,256)维的feature map相加输入Encoder__。

    不同点2:原版Transformer只在Encoder之前使用了Positional Encoding,并且只在输入上进行Positional Encoding,再把输入经过transformation matrix变为Query,Key和Value这几个张量。但DETR在Encoder的每一个Multi-head Self-attention之前都使用了Positional Encoding,且只对Query和Key使用,即:只把维度维(HW,B,256)维的位置编码与维度为(HW,B,256)维的Query和Key相加,而不与Value相加

    下图为DETR的transformer的结构详解:

    除了Positonal Encoding设置不一样之外,Encoder其他结构一致。每个Encoder Layer包含一个multi-head self-attention的module和一个前馈网络。

    Encoder最终输出的是(HW,b,256)维的编码矩阵Embedding,并将其输入Decoder。

    与原始transformer编码器不同之处:

    • 输入编码器的位置需要考虑2-D空间位置。

    • 位置编码向量需要加入每个Encoder Layer中。

    • 在编码器内部位置编码仅作用于Query和Key,即只与Query和Key相加,Value不做处理。

  3. decoder:

    DETR的Decoder与原版Transformer的也不太一样:原版的decoder最后一个框output probability,代表一次只产生一个单词的softmax,并由此得到这个单词的预测结果。即:predicts the output sequence one element at a time

    不同的是,DETR的transformer decoder是一次性处理全部的object queries,即一次性输出全部的predictions,即:decodes the N objects in parallel at each decoder layer

    DETR的Decoder主要有两个输入:

    1. Transformer Encoder 输入的Embedding与position encoding之和。
    2. Object queries。

    其中,Embedding即使上文提到的(HW,b,256)的编码矩阵。

    __Object queries__是一个维度维(100,b,256)的张量,数据类型是nn.Embedding,该张量可学习。__Object queries__矩阵内部通过学习建模了100个物体之间的全局关系(例如:房间里桌子旁一般放椅子),推理时可以利用该全局注意力更好的进行解码预测输出。

    Decoder的输入初始被初始为维度为(100,b,256)维的全部元素为0的张量,和__Object queries__相加后一起充当__multi-head self-attention的Query和Key。multi-head self-attention的Value为Decoder的输入(全0张量)__。

    而到了Decoder的multi-head attention时,它的Key和Value来自Encoder的输出张量,维度为(hw,b,256), 其中Key值还进行位置编码。Query值一部分来自第一个Add and Norm的输出,维度为(100,b,256)的张量,另一部分来自Object queries,充当可学习的位置编码。所以,multi-head attention的Key和Value的维度为(hw,b,256),而Query的维度为(100,b,256)。

    每个Decoder的输出维度为(1,b,100,256),送入后面的前馈网络。

    故而:Object queries充当的其实是位置编码的作用,只不过它是可学习的位置编码。所以,归纳得:

    损失函数部分解读:

    Decoder输出维度(b,100,256)的张量,送到2个前馈网络FFN得到class和Bounding Box。他们会得到N=100个预测目标,包括类别和Bounding Box(100远大于图中目标总数)。计算loss时回归分支仅计算物体位置,背景集合忽略。所以DETR输出张量的维度为__分类分支(b,100,class+1)__和__回归分支(b,100,4)__。其中,4是指每个预测目标归一化的。归一化就是出一图片宽高进行归一化。

    question: 预测框和真值如何一一对应---------如何知道第47个预测框对应图片里的狗的?

    DETR: 目标检测任务就是输出无序集合,如何将GT Bounding Box计算loss?

    一幅图,若第i个物体的真值表达为 ,其中,表示它的class,表示它的Bounding Box。定义 为网络输出的N个预测值。

    由匈牙利算法,可以找到每个真指对应的预测值: 对于某一个真值,假设已经找到了这个真值对应的预测值,这里表示所有可能的排列,代表__从真值索引到预测值索引的所有的映射__,然后用最小化的距离。 意思为:假设当前从真值索引到预测值索引的所有映射为,对于图片中的每个真值ℹ,先找到对应的预测值,再看分类网络的结果,取反作为的第1部分。再计算回归网络的结果与直值的Bounding Box的差异,即作为的第2部分。

    所以,可使最小的排列就是我们要找的排列,即:对于每个真值ℹ来说,就是这个真值所对应的预测值的索引

    接下来,使用上步得到的排列,计算匈牙利损失: 其中,具体为: 常用的L1 loss对于大小Bounding Box会有不同的标度,即使它们的相对误差相似。为缓解该问题,这里使用L1 loss和广义loU损耗的线性组合,它是比列不变的。

    DETR的End-to-End的原理概括

    • DETR如何训练?

      训练集李的任意一张图片,假设第1张图片,通过模型产生100个预测框Predict Bounding Box,假设这张图片有3个GT Bounding Box,它们分别是Cat,Monkey,Pig。

      问题是:如何知道这100个预测框哪个对应Cat,哪个对应Monkey,哪个对应Pig?

      首先建立一个(100,3)的矩阵,矩阵元素即为公式(7)所得结果。举个例子:比如左上角(1,1)号元素的含义是:第1个预测框对应Cat(label = 1)的情况下的值。我们用__scipy.optimize__这个库中的__linear_sum_assignment函数找到最优匹配,这个过程称之为:"匈牙利算法(Hungarian Algorithm)"__。

      假设__linear_sum_assignment__结果是:第16个预测框对应Cat,第39个预测框对应Monkey,第88个预测框对应Pig。接下来,会将第16、39、88个预测框挑出来安装公式(9)计算这个图片的Loss。最后,将所有的图片都按照这个模式去训练模型。

    • 训练完如何用?

      训练完,你的模型学习到了一种能力,即:模型产生的100个预测框,它指导某个预测框该对应什么Object,例如,模型学习到:第1个预测框对应Cat(label=1),第2个预测框对应Dog(label=11),第3个预测框对应Mouse(label=32),第4-100个预测框对应......

    • 为什么训练完后,模型学习到了一种能力,即:模型产生的100个预测框,它指导某个预测框该对应什么Object?

      前文提到Object queries,它是一个维度为(100,b,256)维的张量,初始化元素维全0。实现方式是__nn.Embedding(num_queries,hidden_dim)__,这里num_queries=100,hidden_dim=256,它是可以训练的。这里的b指batch size,对于但章图片而言,假设Object queries是一个维度为(100,256)维的张量。在训练完模型后,这个张量也训练完成了,那__此时的Object queries代表什么呢?

      可以把此时的__Object queries看成100个格子,每个格子都是256维的向量__。训练完成后,这100个格子里__注入了不同Object的位置信息和类别信息__。例如:第一个格子里的这256维的向量代表着Cat这种Object的位置信息,这种信息是通过训练,考虑所有图片的某个位置附近的Cat编码特征,属于和位置有关的全局Car统计信息。

      测试时,倘若图片中有Cat,Monkey,Pig三种物体,该图片会输入到编码器中进行特征编码,假设特征没有丢失,Decoder的__Key__和 __Value__就是编码器输出的编码向量,而__Query__就是Object queries,就是我们的100个格子。

      Query可以看作代表不同Object的信息,而Key和Value可以看作代表图像的全局信息

      通过注意力模块,将__Query__和__Key__计算,然后加权__Value__得到解码器输出。对于第1个格子的__Query__会和__Key__中的所有向量进行计算,目的是查找某个位置附近有没有Cat,如果有那么该特征就会加权输出,若没有,输出信息就不会有Cat。

      整个过程计算完成后就可以把编码向量中的Cat,Monkey,Pig的编码嵌入信息提取出来,容纳后后面接上FFN进行分类和回归就比较容易,因为特征已经对齐了。

      总而言之,Object queries在训练过程中,对于N个格子会压缩入对应的位置和类别相关统计信息,在测试阶段就可以利用__Query区和某个图像的编码特征Key,Value__计算,若图片中刚好有Query想找的特征,比如Cat,则这个特征就能提取出来,最后通过2个FFN进行分类和回归。Object queries作用非常类似Faster R-CNN中的anchor,这个anchor是可学习的,由于维度比较高,故可以表征的东西丰富,训练时间相应也会越长。

Experiments:

1.性能对比:

2.编码器层数对比:

实验发现,编码器层数越多越好,最后选择6层。

下图为最后一个Encoder Layer的attention可视化,Encoder已经分离了instances,简化了Decoder的对象提取和定位。

3.解码器层数对比:

可以发现,性能随着解码器层数的增加而提升。下图为Decoder Layer的attention可视化:

类似于可视化编码器注意力,作者用不同颜色给每个预测对象的注意力图着色。

Final: Question:

Q: 如果他的N个object query在训练完之后,每个都有了自己对应的目标,比如cat,但是如果测试图中,有好多同一目标该怎么办?这时候的输出(N,class+1)与(N,4)该怎么输出多个同一目标?

A: N个object query在训练完之后,每个都有了自己对应的目标"只是一种便于理解的方式,实际上也可能是多个query都能找到同样类型的object。比如第49,65个query都可以对应Cat。但anyway,还是query个数多一点比较好,像Deformable DETR就用了N=300。

Q:残差链接用途

A:残差链接减小了梯度消失的影响,加入残差链接,就能保证层次很深的模型不会出现梯度消失的现象。

Q:encoder的输出如何作为decoder的输入

A:根据transformer的整体架构图可以看出,decoder的第二层是一个多头注意力(Multi-Head Attention)。既然是多头注意力了,那么一定会涉及到Q、K、V三个矩阵。从上图中还可以看出,K、V矩阵是由encoder部分的输出作为decoder的输入的。刚才提到,encoder最后一层的输出是Zn。那么如何把Zn这一个矩阵变成K、V两个矩阵呢?很简单,和注意力机制内部一样,初始化一个新的​权重矩阵,用Zn去乘这两个矩阵就可以了。总结一下,假如该transformer的编码部分有6层encoder,每层encoder有8个“头”,那么编码部分一共初始化了多少个W^{Q}W^{K}W^{V}权重矩阵?

W^{K}W^{V}都是6×8+1个,W^{Q}有6×8个。

DETR模型弊端

如果用10*10的特征图表示一张图片,即一张图片划分成100个Patch,那么就有100个特征向量,每一个特征向量要和所有的特征向量计算注意力机制,所以计算一次注意力机制要100*100=10000次。

如果用100*100的特征图表示一张图片,即一张图片划分成10000个Patch,那么就有10000个特征向量,每一个特征向量要和所有的特征向量计算注意力机制,所以计算一次注意力机制要10000*100000=1亿次。由此可见,每一个Patch的边长缩小10倍,计算量要增加一万倍。因为识别小物体有恰恰需要划分更小的Patch,因此DETR的小物体识别能力有限。