
Transformer原理输入 词嵌入 位置编码词嵌入部分位置编码为什么要加入位置编码注意力机制不像RNN那样逐步处理序列是并行计算的这使得它无法关注位置信息。pos是位置索引i是维度索引d_model是模型的维度。除数是为了使得加长周期区分奇偶也是为了加长周期避免因为周期太短而出现不同的位置却有相同的位置编码。位置编码矩阵的生成遵循一定的数学公式该公式保证了每个位置编码都是独特的并且在不同维度上具有周期性变化。具体来说位置编码矩阵的每一行对应序列中的一个位置每一列对应特征维度中的一个维度。def PositionCode(self, max_len, word2vec_size): position torch.arange(0, max_len, dtypetorch.float32).unsqueeze(1) #torch.arange(0, max_len, dtypetorch.float32)这行代码生成一个从0到max_len-1的序列步长为1数据类型为float32。 #.unsqueeze(1)这个操作会在第1维增加一个维度使position变成一个形状为(max_len, 1)的量。 I torch.tensor([[i if i % 2 0 else (i - 1) for i in range(word2vec_size)]], dtypetorch.float32) #生成的结果是一个包含偶数的列表偶数列保持不变奇数列被向下调到最近的偶数。 #torch.tensor(..., dtypetorch.float32)将生成的列表转换为一个PyTorch张量数据类型为float32。 pe position / torch.pow(10000, I / word2vec_size) pe[:, 0::2] torch.sin(pe[:, 0::2]) pe[:, 1::2] torch.cos(pe[:, 1::2]) return pe多头注意力机制def mask(self,x_index): mask (x_index torch.zeros_like(x_index)).bool() # 找到需要遮掩的元素位置值为 True attention_mask mask.unsqueeze(1).unsqueeze(1) attention_mask attention_mask.float() # 获得词向量填充符掩码 return attention_mask def MultiAttention(self, x_embedding, x_index): q, k, v self.WQ(x_embedding), self.WK(x_embedding), self.WV(x_embedding) #分头 h_q q.reshape(q.shape[0], head_num, q.shape[1], self.head_dim) h_k k.reshape(k.shape[0], head_num, k.shape[1], self.head_dim) h_v v.reshape(v.shape[0], head_num, v.shape[1], self.head_dim) attention torch.matmul(h_q, h_k.transpose(-2, -1))/ math.sqrt(self.head_dim) #[batch_size,head_num,max_len,max_len] attention_mask self.mask(x_index) #[batch_size,1,1,max_len] attention attention_mask * -1e10 self.attention torch.softmax(attention, dim -1) att_massage torch.matmul(self.attention, h_v) #[batch_size,head_num,max_len,head_dim] #print(att_massage.shape) att_massage att_massage.permute(0, 2, 1, 3).contiguous().view(batch_size, max_len, word2vec_size) #print(att_massage.shape) att_output self.linear1(att_massage) #print(att_output.shape) return att_output前向传播def feed_forword(self, attention): #每一层都有一层前向传播可以让每一层都清楚输入在该层被处理时的逻辑 output self.fc2(self.relu(self.fc1(attention))) #print(output.shape) return outputencoder层def encoder_layer(self, x_embedding, x_index): attention self.MultiAttention(x_embedding, x_index) #[16, 250, 8, 16] x_massage self.norm1(x_embedding self.dropout(attention)) ff_out self.feed_forword(x_massage) x self.norm2(x_massage self.dropout(ff_out)) return x整个模块def all_transformer(self, x_index, x_vector, layer_num): x_embdeding x_vector self.pe #[16, 250, 128] enc_output x_embdeding #[16, 250, 128] for i in range(layer_num): enc_output self.encoder_layer(enc_output, x_index) scores torch.mean(enc_output, dim1) #在max_len维度上取平均值平均池化使得维度变成了[batch_size,d_model] scores self.linear2(scores) #[16,2] return scores完整代码import torch import torch.nn as nn import math from self_params import * class transformer(nn.Module): def __init__(self, max_len, word2vec_size, head_num, d_ff, dropout_rate, class_num): super(transformer, self).__init__() self.pe self.PositionCode(max_len, word2vec_size) self.head_dim word2vec_size // head_num self.WQ nn.Linear(self.head_dim * head_num, self.head_dim * head_num, biasFalse) self.WK nn.Linear(self.head_dim * head_num, self.head_dim * head_num, biasFalse) self.WV nn.Linear(self.head_dim * head_num, self.head_dim * head_num, biasFalse) self.fc1 nn.Linear(word2vec_size, d_ff) # 输入维度输出维度将神经网络输入维度映射到d_ff self.fc2 nn.Linear(d_ff, word2vec_size) self.relu nn.ReLU() self.norm1 nn.LayerNorm(word2vec_size) self.norm2 nn.LayerNorm(word2vec_size) self.dropout nn.Dropout(dropout_rate) self.linear1 nn.Linear(word2vec_size, word2vec_size) self.linear2 nn.Linear(word2vec_size, class_num, biasTrue) def PositionCode(self, max_len, word2vec_size): position torch.arange(0, max_len, dtypetorch.float32).unsqueeze(1) I torch.tensor([[i if i % 2 0 else (i - 1) for i in range(word2vec_size)]], dtypetorch.float32) pe position / torch.pow(10000, I / word2vec_size) pe[:, 0::2] torch.sin(pe[:, 0::2]) pe[:, 1::2] torch.cos(pe[:, 1::2]) return pe def mask(self,x_index): mask (x_index torch.zeros_like(x_index)).bool() # 找到需要遮掩的元素位置值为 True attention_mask mask.unsqueeze(1).unsqueeze(1) attention_mask attention_mask.float() # 获得词向量填充符掩码 return attention_mask def MultiAttention(self, x_embedding, x_index): q, k, v self.WQ(x_embedding), self.WK(x_embedding), self.WV(x_embedding) #分头 h_q q.reshape(q.shape[0], head_num, q.shape[1], self.head_dim) h_k k.reshape(k.shape[0], head_num, k.shape[1], self.head_dim) h_v v.reshape(v.shape[0], head_num, v.shape[1], self.head_dim) attention torch.matmul(h_q, h_k.transpose(-2, -1))/ math.sqrt(self.head_dim) #[batch_size,head_num,max_len,max_len] attention_mask self.mask(x_index) #[batch_size,1,1,max_len] attention attention_mask * -1e10 self.attention torch.softmax(attention, dim -1) att_massage torch.matmul(self.attention, h_v) #[batch_size,head_num,max_len,head_dim] #print(att_massage.shape) att_massage att_massage.permute(0, 2, 1, 3).contiguous().view(batch_size, max_len, word2vec_size) #print(att_massage.shape) att_output self.linear1(att_massage) #print(att_output.shape) return att_output def feed_forword(self, attention): #每一层都有一层前向传播可以让每一层都清楚输入在该层被处理时的逻辑 output self.fc2(self.relu(self.fc1(attention))) #print(output.shape) return output def encoder_layer(self, x_embedding, x_index): attention self.MultiAttention(x_embedding, x_index) #[16, 250, 8, 16] x_massage self.norm1(x_embedding self.dropout(attention)) ff_out self.feed_forword(x_massage) x self.norm2(x_massage self.dropout(ff_out)) return x def all_transformer(self, x_index, x_vector, layer_num): x_embdeding x_vector self.pe #[16, 250, 128] enc_output x_embdeding #[16, 250, 128] for i in range(layer_num): enc_output self.encoder_layer(enc_output, x_index) scores torch.mean(enc_output, dim1) #在max_len维度上取平均值平均池化使得维度变成了[batch_size,d_model] scores self.linear2(scores) #[16,2] return scores前向传播为什么使用LN而不使用BN输入序列矩阵形状[batch_size,seq_len,d_model]RN纵向[Batch_size,seq_len]需要较大的Batch_size才能合理评估训练数据的均值和方差导致内存可能会不够用。同一个batch_size中序列有长有短。 为了使用BN而对每个样例补齐0使得较长的序列词的含义相对减小造成抖动误差。对于使用场景来说BN在MLP多层感知机——人工神经网络和CNN上使用的效果都比较好在RNN这种动态文本模型上使用的比较差。 BN是对每个特征在batch_size上求的均值和方差应用到NLP任务相当于是在对默认了在同一个位置的单词对应的是同一种特征。LN横向[seq_len,d_model]对Batch_size的每一个样本做归一化。LN针对的是文本的长度整条序列的文本。相较于LSTM或者RNN等网络来说Transformer有什么优势