🚩🚩🚩Transformer实战-系列教程总目录
有任何问题欢迎在下面留言
本篇文章的代码运行界面均在Pycharm中进行
本篇文章配套的代码资源已经上传
点我下载源码
DETR 算法解读
DETR 源码解读1(项目配置/CocoDetection类/ConvertCocoPolysToMask类)
DETR 源码解读2(DETR类)
DETR 源码解读3(位置编码:Joiner类/PositionEmbeddingSine类)
DETR 源码解读4(BackboneBase类/Backbone类)
DETR 源码解读5(Transformer类)
DETR 源码解读6(编码器:TransformerEncoder类/TransformerEncoderLayer类)
DETR 源码解读7(解码器:TransformerDecoder类/TransformerDecoderLayer类)
DETR 源码解读8(训练函数/损失函数)
5、Joiner类
位置:models/backbone.py/Joiner类
class Joiner(nn.Sequential):
def __init__(self, backbone, position_embedding):
super().__init__(backbone, position_embedding)
def forward(self, tensor_list: NestedTensor):
xs = self[0](tensor_list)
out: List[NestedTensor] = []
pos = []
for name, x in xs.items():
out.append(x)
pos.append(self[1](x).to(x.tensors.dtype))
return out, pos
- 定义Joiner类,继承自PyTorch的
nn.Sequential
- 构造函数
- 初始化
- 前向传播函数,接收一个类型为
NestedTensor
的参数tensor_list
- xs,
self[0]
指的是nn.Sequential
容器中的第一个模块,根据__init__
方法中的定义,这是backbone
模块。所以,self[0](tensor_list)
表示对输入的tensor_list
执行backbone
模块的前向传播 - 初始化一个空列表
out
,用于存储backbone
输出的特征 - pos = []
**:初始化一个空列表
pos,用于存储由
position_embedding`生成的位置编码 - 遍历
xs
- 将当前层的输出
x
添加到out
列表中 self[1]
指的是nn.Sequential
容器中的第二个模块,根据初始化时的顺序,这是position_embedding
模块。因此,self[1](x)
表示对backbone
的输出x
执行position_embedding
模块的前向传播- out、pos
6、PositionEmbeddingSine类
位置:models/position_encoding.py/PositionEmbeddingSine类
DETR提供了两种位置编码方式,一种是和Transformer一样的正余弦,另一种是可学习的位置编码方式,下面是和Transformer一样的正余弦
P
E
p
o
s
,
2
i
=
s
i
n
(
p
o
s
/
1000
0
2
i
/
d
m
o
d
e
l
)
PE_{pos,2i} = sin(pos/10000^{2i/d_{model}})
PEpos,2i=sin(pos/100002i/dmodel)
P
E
p
o
s
,
2
i
+
1
=
c
o
s
(
p
o
s
/
1000
0
2
i
/
d
m
o
d
e
l
)
PE_{pos,2i+1} = cos(pos/10000^{2i/d_{model}})
PEpos,2i+1=cos(pos/100002i/dmodel)
6.1 构造函数
class PositionEmbeddingSine(nn.Module):
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
- 继承nn.Module的类
- 构造函数
- 初始化
- num_pos_feats ,每个位置特征数量的一般
- temperature ,缩放因子,用于调整位置编码的频率
- normalize ,是否对位置坐标进行归一化
- scale,在归一化位置坐标时使用的额外缩放因子
- 验证scale和normalize参数的兼容性
- 设置scale的默认值为2 * math.pi,如果未提供
6.2 前向传播
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
- 前向传播函数
- x,从tensor_list提取张量,torch.Size([2, 2048, 25, 29]),2是batch,2048是一个像素点的特征向量维度,后面是特征图长宽
- mask ,从tensor_list提取掩码,torch.Size([2, 25, 29]),这里面存储的全是bool值,如果是True表示的是一个实际的特征,如果是False表示的是padding出来的
- 确认mask 存在
- 通过对掩码取反获取非掩码区域,这里非掩码区域指的是图像的有效部分
- y_embed,行方向上的累积和,用于生成位置编码,torch.Size([2, 25, 29]),mask为True的就加1,mask为false的就不用加了
- x_embed,列方向上的累积和,用于生成位置编码,torch.Size([2, 25, 29])
- 如果启用了归一化
- 一个很小的数,防止出现除以0
- 对y_embed和
- x_embed进行归一化并应用缩放因子self.scale,self.scale表示缩放0到2π的角度中,方便进行正余弦的计算
- dim_t ,torch.Size([128]),生成一个维度张量dim_t,表示位置编码每个位置都要生成128维的向量
- dim_t ,torch.Size([128]),根据temperature和num_pos_feats调整其值
- pos_x ,对x_embed应用缩放,torch.Size([2, 25, 29, 128])
- pos_y ,对y_embed应用缩放,准备生成正弦和余弦编码,torch.Size([2, 25, 29, 128])
- pos_x ,分别计算所有偶数位置的正弦值和所有奇数位置的余弦值,将正弦值和余弦值沿着新的维度堆叠起来,将堆叠后的维度(正弦和余弦值对)展平,torch.Size([2, 25, 29, 128])
- pos_y ,torch.Size([2, 25, 29, 128])
- pos ,将pos_y和pos_x在第3维上进行拼接,形成完整的位置编码,torch.Size([2, 256, 25, 29])
- 返回pos
通过这种方式,PositionEmbeddingSine类为每个像素位置生成了一个独特的编码,这个编码通过正弦和余弦函数的交替使用捕获了空间位置信息。正弦和余弦函数的周期性和连续性特点使得这种编码非常适合表示位置关系,有助于提高模型对图像空间信息的理解和处理能力
DETR 算法解读
DETR 源码解读1(项目配置/CocoDetection类/ConvertCocoPolysToMask类)
DETR 源码解读2(DETR类)
DETR 源码解读3(位置编码:Joiner类/PositionEmbeddingSine类)
DETR 源码解读4(BackboneBase类/Backbone类)
DETR 源码解读5(Transformer类)
DETR 源码解读6(编码器:TransformerEncoder类/TransformerEncoderLayer类)
DETR 源码解读7(解码器:TransformerDecoder类/TransformerDecoderLayer类)
DETR 源码解读8(训练函数/损失函数)