🚩🚩🚩Transformer实战-系列教程总目录
有任何问题欢迎在下面留言
本篇文章的代码运行界面均在Pycharm中进行
本篇文章配套的代码资源已经上传
6、Block类------构造函数
class Block(nn.Module):
def __init__(self, config, vis):
super(Block, self).__init__()
self.hidden_size = config.hidden_size
self.attention_norm = LayerNorm(config.hidden_size, eps=1e-6)
self.ffn_norm = LayerNorm(config.hidden_size, eps=1e-6)
self.ffn = Mlp(config)
self.attn = Attention(config, vis)
- 隐藏特征,768维向量
- LayerNorm,层归一化
- 定义一个mlp:两次全连接+relu+Dropout
- 定义一个Attention类的实例
7、Attention类------构造函数
class Attention(nn.Module):
def __init__(self, config, vis):
super(Attention, self).__init__()
self.vis = vis
self.num_attention_heads = config.transformer["num_heads"]
self.attention_head_size = int(config.hidden_size / self.num_attention_heads)
self.all_head_size = self.num_attention_heads * self.attention_head_size
self.query = Linear(config.hidden_size, self.all_head_size)
self.key = Linear(config.hidden_size, self.all_head_size)
self.value = Linear(config.hidden_size, self.all_head_size)
self.out = Linear(config.hidden_size, config.hidden_size)
self.attn_dropout = Dropout(config.transformer["attention_dropout_rate"])
self.proj_dropout = Dropout(config.transformer["attention_dropout_rate"])
self.softmax = Softmax(dim=-1)
- num_attention_heads ,多头注意力机制,配置参数中定义了要多少头
- attention_head_size ,每头注意力处理多少维向量,隐层特征是768维/12头=64维向量
- all_head_size 应该要等于config.hidden_size
- 全连接层生成Q、K、V
- 全连接层、dropout、softmax
8、Vision Transformer类------前向传播
class VisionTransformer(nn.Module):
def forward(self, x, labels=None):
x, attn_weights = self.transformer(x)
# print(x.shape)
logits = self.head(x[:, 0])
# print(logits.shape)
if labels is not None:
loss_fct = CrossEntropyLoss()
loss = loss_fct(logits.view(-1, self.num_classes), labels.view(-1))
return loss
else:
return logits, attn_weights
前向传播函数打上断点,开启debug模式,查看数据维度变化:
输入x=[16,3,224,224],依次为batch_size,通道数,图像长和宽
经过self.transformer()后
x=[16,197,768],依次为batch_size,197=196+1其中196为序列长度、1为分类标记,自定义的向量维度
logits = [16,10],依次为batch_size,10分类的分数
9、Transformer类------前向传播
class Transformer(nn.Module):
def __init__(self, config, img_size, vis):
super(Transformer, self).__init__()
self.embeddings = Embeddings(config, img_size=img_size)
self.encoder = Encoder(config, vis)
def forward(self, input_ids):
embedding_output = self.embeddings(input_ids)
encoded, attn_weights = self.encoder(embedding_output)
return encoded, attn_weights
前向传播函数打上断点,开启debug模式,查看数据维度变化:
- input_ids.shape = torch.Size([16, 3, 224, 224])
- embedding_output.shape = torch.Size([16, 197, 768])
- encoded.shape = torch.Size([16, 197, 768])
Embeddings输入是彩色图,输出每个位置得到768维向量
Encoder输入与输出都是768维向量
10、Embeddings------前向传播
class Embeddings(nn.Module):
def forward(self, x):
B = x.shape[0]
cls_tokens = self.cls_token.expand(B, -1, -1)
if self.hybrid:
x = self.hybrid_model(x)
x = self.patch_embeddings(x)
x = x.flatten(2)
x = x.transpose(-1, -2)
x = torch.cat((cls_tokens, x), dim=1)
embeddings = x + self.position_embeddings
embeddings = self.dropout(embeddings)
return embeddings
- 输入图像:x.shape= torch.Size([16, 3, 224, 224])
- B为batch_size=16
- cls_tokens.shape= torch.Size([16, 1, 768]),
cls_token
:这是一个特殊的“分类(class)标记”,其设计灵感来源于BERT模型中的[CLS]
标记。在Vision Transformer中,这个cls_token
被添加到图像块(patch)嵌入的序列的前面,并且在整个Transformer模型的处理过程中一直携带着。模型的最终目标是使用这个cls_token
的表示(经过Transformer模型的多层处理后的输出)来进行分类任务。换句话说,cls_token
在模型的最后一层的输出被用作图像分类或其他下游任务的基础。这个就是197=196+1的1的由来 - x.shape = self.patch_embeddings(x).shape = torch.Size([16, 768, 14, 14]),
patch_embeddings
:这是将输入图像分割成多个图像块(patches),然后将每个图像块转换成模型可以处理的嵌入向量的过程。在Vision Transformer中,输入图像首先被划分为多个固定大小的小块,每个小块接着通过一个卷积层(在这个代码中是Conv2d
层)转换成一个嵌入向量。这个卷积层的输出通道数等于模型的隐藏层大小(config.hidden_size
),这样每个图像块就被映射到了一个高维空间,以便后续由Transformer处理。patch_embeddings
实质上是对图像进行了一种“词嵌入”操作,将图像的原始像素值转换为模型可以理解的语义向量 - x.flatten(2).torch.Size([16, 768, 196]),展开为一个序列,现在是self-Attention中标准的输入格式
- x.transpose(-1, -2).torch.Size([16, 196, 768]),转换维度
- torch.cat((cls_tokens, x), dim=1).torch.Size([16, 197, 768]),x和cls_tokens拼接
- (x + self.position_embeddings).torch.Size([16, 197, 768]),加上位置编码,维度保存不变,self.position_embeddings位置编码是为每一个小块都增加了一个初始化的全零的可学习的位置嵌入张量,
- self.dropout(embeddings).torch.Size([16, 197, 768]),加上Dropout