04 感知:3D目标检测 注意力机制

1. DETR 2020(DEtection TRansformer)End-to-End Object Detection with Transformers

Best Ref: 理解DETR

可以将DETR视为一个从图像序列到一个集合序列的转换过程。该集合实际上就是一个可学习的位置编码(文章中也称为object queries或者output positional encoding,代码中叫作query_embed)。

1. pos_embed (位置编码)

transformer位置编码:

{PE(pos,2i)=sin(pos/100002i/dmodel)PE(pos,2i+1)=cos(pos/100002i/dmodel)

pos是词向量在序列中的位置,i是channel的索引。

DETR中位置编码:为二维特征在图像x和y方向分别编码,每个维度编码长度为特征向量长度一半。这样两个维度编码特征拼接后,构成和图像特征长度相等的向量。

class PositionEmbeddingSine(nn.Module):
"""
This is a more standard version of the position embedding, very similar to the one
used by the Attention is all you need paper, generalized to work on images.
"""
def __init__(self, num_pos_feats=64, temperature=10000, normalize=False, scale=None):
super().__init__()
self.num_pos_feats = num_pos_feats
self.temperature = temperature
self.normalize = normalize
if scale is not None and normalize is False:
raise ValueError("normalize should be True if scale is passed")
if scale is None:
scale = 2 * math.pi
self.scale = scale
def forward(self, tensor_list: NestedTensor):
x = tensor_list.tensors
mask = tensor_list.mask
assert mask is not None
not_mask = ~mask
y_embed = not_mask.cumsum(1, dtype=torch.float32)
x_embed = not_mask.cumsum(2, dtype=torch.float32)
if self.normalize:
eps = 1e-6
y_embed = y_embed / (y_embed[:, -1:, :] + eps) * self.scale
x_embed = x_embed / (x_embed[:, :, -1:] + eps) * self.scale
dim_t = torch.arange(self.num_pos_feats, dtype=torch.float32, device=x.device)
dim_t = self.temperature ** (2 * (dim_t // 2) / self.num_pos_feats)
pos_x = x_embed[:, :, :, None] / dim_t
pos_y = y_embed[:, :, :, None] / dim_t
pos_x = torch.stack((pos_x[:, :, :, 0::2].sin(), pos_x[:, :, :, 1::2].cos()), dim=4).flatten(3)
pos_y = torch.stack((pos_y[:, :, :, 0::2].sin(), pos_y[:, :, :, 1::2].cos()), dim=4).flatten(3)
pos = torch.cat((pos_y, pos_x), dim=3).permute(0, 3, 1, 2)
return pos

2. object queries (query_embed)

这里query_embed的作用表现的和位置编码类似

# embedding_layer = nn.Embedding(num_embeddings=3, embedding_dim=2)
self.query_embed = nn.Embedding(num_queries, hidden_dim)

object queries在经过decoder的计算以后,会输出一个形状为T,N,C的数组,其中T是object queries的序列长度,即100,N是batch size,C是特征channel。
最后通过一个Linear层输出class预测,通过一个多层感知机结构输出box预测:

'''
torch.nn.Linear(in_features, out_features, bias=True)
- 输入形状:(batch_size, in_features)
- (batch_size, out_features)
'''
# MLP (also called FFN)
class MLP(nn.Module):
""" Very simple multi-layer perceptron (also called FFN)"""
def __init__(self, input_dim, hidden_dim, output_dim, num_layers):
super().__init__()
self.num_layers = num_layers
h = [hidden_dim] * (num_layers - 1)
self.layers = nn.ModuleList(nn.Linear(n, k) for n, k in zip([input_dim] + h, h + [output_dim]))
def forward(self, x):
for i, layer in enumerate(self.layers):
x = F.relu(layer(x)) if i < self.num_layers - 1 else layer(x)
return x
self.class_embed = nn.Linear(hidden_dim, num_classes + 1)
self.bbox_embed = MLP(hidden_dim, hidden_dim, 4, 3)
#forward
hs = self.transformer(self.input_proj(src), mask, self.query_embed.weight, pos[-1])[0]
outputs_class = self.class_embed(hs)
outputs_coord = self.bbox_embed(hs).sigmoid()
out = {'pred_logits': outputs_class[-1], 'pred_boxes': outputs_coord[-1]}

3. 为什么DETR不需要NMS处理

假如有N个目标,那么100个object predictions中就会有N个能够匹配到这N个ground truth,其他的都会和“no object”匹配成功,这些predictions的类别label就会被分配为num_classes,即表示该prediction是背景。这样的设计是很不错的,理论上每个object query都有唯一匹配的目标,不会存在重叠,所以DETR不需要nms进行后处理。


补充:

1. nn.Embedding

- nn.Embedding 是一个预定义的层,专门用于将 离散的整数索引(比如单词ID、类别ID)转换为 稠密向量。
- 它内部维护一个可学习的“向量表”(类似字典),通过查表的方式将索引映射成向量。
- 可学习:向量表中的每个向量都是 nn.Parameter,会被优化器更新。
- 专为离散设计:适合处理文本、类别等离散数据。
- nn.Embedding:就像一本字典,每个单词对应一页解释(向量),这本字典的内容会在训练中不断优化,让解释更准确。

2. 匈牙利匹配算法(带权二分图最大匹配 KM算法)

1. 匈牙利匹配算法

交替路和增广路是用来解决新配对的时候发生冲突的问题。

交替路:就是依次经过非匹配边、匹配边的路

  • (非匹配边) (匹配边) (非匹配边):B--------------a----------------A-----------------c
  • B和c都是没有被匹配过的点,而它又是这条交替路的起点和终点。这条交替路就是增广路。

现在我们要做一个取反操作,怎么取呢,就是将上面这条增广路的匹配边变成不匹配边,不匹配边变成匹配边。

  • (匹配边) (非匹配边) (匹配边):B--------------a----------------A-----------------c

增广路的核心特点就是“起点终点都是非匹配点”,这样就导致非匹配边比匹配边多了一条。增广路建立连接时,必须建立在两者有意向的基础上。这样我们取反,也就是交换匹配和非匹配边的身份。我们就多得到了一条匹配边。这个取反的过程,就是把原本匹配上的两个人拆散,给第三个人腾位置。

参考:Raf 1

2. KM算法

参考:Ref 2

2. DAB-DETR (收敛慢因为: 没有提供位置先验的 learnable queries)

3. DN-DETR (收敛慢因为:匈牙利匹配的离散性和模型训练的随机性,导致了 query 对 gt 的匹配变成了一个动态的、不稳定的过程)

4. Deformable DETR 2021()

Code URL: https://github.com/fundamentalvision/Deformable-DETR
改进:

  1. 减少训练时长:将全局attention转为局部attention
  2. 提升小物体检测准确率:采用多尺度特征图
    原始图片上一个目标物体太小,它在压缩后的特征图上可能就几乎找不到了,因此我们很难将其检测出来。所以一般情况下,小物体我们需要更大尺寸的特征图。想法:让不同尺寸的特征图都参与训练,提高一个物体被检测出来的可能。

1. Deformable attention计算

# ! 此部分代码不完整,完整代码解析查看参考资料:Ref 4.
class MSDeformAttn(nn.Module):
def __init__(self, d_model=256, n_levels=4, n_heads=8, n_points=4):
"""
Multi-Scale Deformable Attention Module
:param d_model hidden dimension
:param n_levels number of feature levels (特征图的数量)
:param n_heads number of attention heads
:param n_points number of sampling points per attention head per feature level
"""
super().__init__()
if d_model % n_heads != 0:
raise ValueError('d_model must be divisible by n_heads, but got {} and {}'.format(d_model, n_heads))
_d_per_head = d_model // n_heads
# you'd better set _d_per_head to a power of 2 which is more efficient in our CUDA implementation
if not _is_power_of_2(_d_per_head):
warnings.warn("You'd better set d_model in MSDeformAttn to make the dimension of each attention head a power of 2 "
"which is more efficient in our CUDA implementation.")

2. Reference points/ off set 如何产生?

3. Corase2Fine如何实现?

Reference

  1. 简单理解增广路与匈牙利算法:https://zhuanlan.zhihu.com/p/208596378
  2. 从匈牙利算法到KM算法:ttps://zhuanlan.zhihu.com/p/214072424
  3. 理解DETR:https://zhuanlan.zhihu.com/p/348060767
  4. 再读deformable detr:https://zhuanlan.zhihu.com/p/700776674
posted @   ldfm  阅读(17)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示