🚩🚩🚩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(训练函数/损失函数)
12、TransformerDecoderLayer类
位置:models/transformer.py/TransformerDecoderLayer类
12.1 构造函数
class TransformerDecoderLayer(nn.Module):
def __init__(self, d_model, nhead, dim_feedforward=2048, dropout=0.1, activation="relu", normalize_before=False):
super().__init__()
self.self_attn = nn.MultiheadAttention(d_model, nhead, dropout=dropout)
self.multihead_attn = nn.MultiheadAttention(d_model, nhead, dropout=dropout)
self.linear1 = nn.Linear(d_model, dim_feedforward)
self.dropout = nn.Dropout(dropout)
self.linear2 = nn.Linear(dim_feedforward, d_model)
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
self.norm3 = nn.LayerNorm(d_model)
self.dropout1 = nn.Dropout(dropout)
self.dropout2 = nn.Dropout(dropout)
self.dropout3 = nn.Dropout(dropout)
self.activation = _get_activation_fn(activation)
self.normalize_before = normalize_before
- 继承PyTorch的nn.module
- 构造函数,传入6个参数:
- d_model:Transformer向量维度
- nhead:多头注意力头数
- dim_feedforward:MLP输出维度
- dropout:dropout比例
- activation:激活函数
- normalize_before:根据布尔值,选择进行归一化的地方
- 初始化
- self_attn ,多头注意力模块(PyTorch内部自带),用于解码器内部
- multihead_attn ,多头注意力模块,用于编码器与解码器之间
- linear1,MLP的第1层全连接,将维度从d_model映射到dim_feedforward
- dropout ,用于MLP第1层的dropout
- linear2 ,MLP的第2层全连接,将维度从dim_feedforward映射到d_model
- norm1 ,自注意力模块输出的层归一化
- norm2 , 交叉注意力模块输出的层归一化
- norm3 , MLP输出的层归一化
- dropout1 ,自注意力模块输出的dropout
- dropout2 ,交叉注意力模块输出的dropout
- dropout3 ,MLP输出层的dropout
- activation ,激活函数
- normalize_before ,根据布尔值,选择进行归一化的地方
12.2 前向传播
def forward(self, tgt, memory, tgt_mask: Optional[Tensor] = None, memory_mask: Optional[Tensor] = None, tgt_key_padding_mask: Optional[Tensor] = None, memory_key_padding_mask: Optional[Tensor] = None, pos: Optional[Tensor] = None, query_pos: Optional[Tensor] = None):
if self.normalize_before:
return self.forward_pre(tgt, memory, tgt_mask, memory_mask, tgt_key_padding_mask, memory_key_padding_mask, pos, query_pos)
return self.forward_post(tgt, memory, tgt_mask, memory_mask, tgt_key_padding_mask, memory_key_padding_mask, pos, query_pos)
- 前向传播函数,传入8个参数:
- tgt:目标序列
- memory:编码器输出
- tgt_mask:目标序列对应的可选的掩码
- memory_mask:编码器输出对应的可选的掩码
- tgt_key_padding_mask:目标序列对应的可选的填充掩码
- memory_key_padding_mask:编码器输出对应的可选的填充掩码
- pos:编码器输出的可选的位置编码
- query_pos:目标序列q向量的可选的位置编码
- normalize_before的值为True:
- 选择forward_pre函数执行前向传播
- 否则,选择forward_post函数执行前向传播
二者之间主要区别在于forward_pre函数的层归一化在自注意力和交叉注意力操作之前进行,forward_post函数的层归一化在自注意力和交叉注意力操作之后进行
12.3 forward_post函数
def forward_post(self, tgt, memory, tgt_mask: Optional[Tensor] = None, memory_mask: Optional[Tensor] = None, tgt_key_padding_mask: Optional[Tensor] = None, memory_key_padding_mask: Optional[Tensor] = None, pos: Optional[Tensor] = None, query_pos: Optional[Tensor] = None):
q = k = self.with_pos_embed(tgt, query_pos)
tgt2 = self.self_attn(q, k, value=tgt, attn_mask=tgt_mask,
key_padding_mask=tgt_key_padding_mask)[0]
tgt = tgt + self.dropout1(tgt2)
tgt = self.norm1(tgt)
tgt2 = self.multihead_attn(query=self.with_pos_embed(tgt, query_pos), key=self.with_pos_embed(memory, pos), value=memory, attn_mask=memory_mask, key_padding_mask=memory_key_padding_mask)[0]
tgt = tgt + self.dropout2(tgt2)
tgt = self.norm2(tgt)
tgt2 = self.linear2(self.dropout(self.activation(self.linear1(tgt))))
tgt = tgt + self.dropout3(tgt2)
tgt = self.norm3(tgt)
return tgt
def with_pos_embed(self, tensor, pos: Optional[Tensor]):
return tensor if pos is None else tensor + pos
- forward_post函数,传入和forward函数一样的8个参数:
- q,k,torch.Size([100, 2, 256]),将位置编码和目标序列相加生成q和k向量,最开始的tgt的元素全部为0
- tgt2 ,torch.Size([100, 2, 256]),将Q、K、V进行自注意力的计算得到输出tgt2 ,其中QK是相同的在tgt基础上加入了位置编码,而V和tgt完全相同
- tgt ,torch.Size([100, 2, 256]),自注意力的输出经过dropout后和原始tgt 相加,实现残差连接
- tgt ,torch.Size([100, 2, 256]),残差连接后的输出经过层归一化
- tgt2 ,torch.Size([100, 2, 256]),交叉注意力操作:
- Q:目标序列(tgt)和对应的位置编码相加得到Q向量
- K,编码器输出(memory,torch.Size([725, 2, 256]))和对应的位置编码torch.Size([725, 2, 256])相加得到K向量
- V:编码器输出(memory)
- Q、K、V经过交叉注意力后的取第一个输出为tgt2
- tgt ,torch.Size([100, 2, 256]),交叉注意力的输出经过dropout再加上上一次的层归一化的输出,实现残差连接
- tgt ,torch.Size([100, 2, 256]),经过一层层归一化
- tgt2 ,torch.Size([100, 2, 256]),此次相当于经过一个MLP,先后经过第1层全连接、激活函数、dropout、第2层全连接
- tgt ,torch.Size([100, 2, 256]),再实现一次和前面相同形式的残差连接
- tgt ,torch.Size([100, 2, 256]),经过一层层归一化
- return
with_pos_embed函数:
如果位置编码pos存在,则加上位置编码
12.4 forward_pre函数
def forward_pre(self, tgt, memory,
tgt_mask: Optional[Tensor] = None,
memory_mask: Optional[Tensor] = None,
tgt_key_padding_mask: Optional[Tensor] = None,
memory_key_padding_mask: Optional[Tensor] = None,
pos: Optional[Tensor] = None,
query_pos: Optional[Tensor] = None):
tgt2 = self.norm1(tgt)
q = k = self.with_pos_embed(tgt2, query_pos)
tgt2 = self.self_attn(q, k, value=tgt2, attn_mask=tgt_mask,
key_padding_mask=tgt_key_padding_mask)[0]
tgt = tgt + self.dropout1(tgt2)
tgt2 = self.norm2(tgt)
tgt2 = self.multihead_attn(query=self.with_pos_embed(tgt2, query_pos),
key=self.with_pos_embed(memory, pos),
value=memory, attn_mask=memory_mask,
key_padding_mask=memory_key_padding_mask)[0]
tgt = tgt + self.dropout2(tgt2)
tgt2 = self.norm3(tgt)
tgt2 = self.linear2(self.dropout(self.activation(self.linear1(tgt2))))
tgt = tgt + self.dropout3(tgt2)
return tgt
和forward_post函数基本相同,不同的地方在于层归一化的顺序不同,而且这两个函数每次也只会选择一个执行
13、TransformerDecoder类
位置:models/transformer.py/TransformerDecoder类
该类用来构建整体的解码器,主要是进行解码器层的堆叠,单个的解码器由前面的TransformerDecoderLayer类实现。
13.1 构造函数
class TransformerDecoder(nn.Module):
def __init__(self, decoder_layer, num_layers, norm=None, return_intermediate=False):
super().__init__()
self.layers = _get_clones(decoder_layer, num_layers)
self.num_layers = num_layers
self.norm = norm
self.return_intermediate = return_intermediate
- 继承PyTorch的nn.Module
- 构造函数,传入4个参数:
- decoder_layer:单个解码器层的实例
- num_layers:解码器层堆叠的数量
- norm:所有解码器层堆叠后执行的层归一化,可选
- return_intermediate:是否返回每一层解码器的输出,如果返回的每一层解码器的输出都会去做损失,这样做可以得到更好的结果
- 初始化
- layers ,使用辅助函数_get_clones结合单个解码器层的实例和解码器层堆叠的数量,生成一个包含这些克隆层的列表,并将其赋值给self.layers。这样,解码器就由多个相同配置的解码器层组成
- num_layers
- norm
- return_intermediate
13.2 前向传播
def forward(self, tgt, memory,
tgt_mask: Optional[Tensor] = None,
memory_mask: Optional[Tensor] = None,
tgt_key_padding_mask: Optional[Tensor] = None,
memory_key_padding_mask: Optional[Tensor] = None,
pos: Optional[Tensor] = None,
query_pos: Optional[Tensor] = None):
output = tgt
intermediate = []
for layer in self.layers:
output = layer(output, memory, tgt_mask=tgt_mask,
memory_mask=memory_mask,
tgt_key_padding_mask=tgt_key_padding_mask,
memory_key_padding_mask=memory_key_padding_mask,
pos=pos, query_pos=query_pos)
if self.return_intermediate:
intermediate.append(self.norm(output))
if self.norm is not None:
output = self.norm(output)
if self.return_intermediate:
intermediate.pop()
intermediate.append(output)
if self.return_intermediate:
return torch.stack(intermediate)
return output.unsqueeze(0)
- 前向传播函数,传入8参数,这些参数的含义在
12、TransformerDecoderLayer类
都已解释 - output ,torch.Size([100, 2, 256])
- intermediate ,存储每个单层解码器层的输出
- 循环遍历所有解码器层
- output ,torch.Size([100, 2, 256]),执行一次单层解码器层得到输出,每一层的输出都会被用作下一层的输入
- return_intermediate,如果为True,即启用了中间输出收集
- 将当前层的输出output经过归一化后添加到intermediate列表中
- 最后的一层解码器层的输出如果有归一化模块:
- 对最后的一层解码器层的输出执行层归一化
- 如果启用了中间输出收集
- 移除最后一个未经归一化处理的输出
- 添加经过归一化的最终输出
- 如果启用了中间输出收集
- 将intermediate列表中的所有Tensor沿着新的维度堆叠起来,形成一个新的Tensor,新的维度可以保留每一层输出的独立性
- 如果没有,返回最终的输出output并增加一层新的维度,这是为了不管是否启用了中间输出收集,最后返回的格式都保持一致
13.3 _get_clones函数
def _get_clones(module, N):
return nn.ModuleList([copy.deepcopy(module) for i in range(N)])
这个辅助函数_get_clones
创建给定模块(TransformerEncoderLayer、TransformerDecoderLayer)的N个深度拷贝,并将这些拷贝组织为一个nn.ModuleList
,nn.ModuleList
是PyTorch中的一个容器模块,用于存储一系列子模块
因此DETR总的架构为,TransformerEncoderLayer、TransformerDecoderLayer分别堆叠形成了TransformerEncoder和TransformerDecoder,这两个构成了整个的Transformer,Transformer再结合之前的backbone即项目的总体模型DETR
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(训练函数/损失函数)