文章目录
- 提出背景
- 总体结构
- Encoder
- Decoder
- 细节再探
- 源码阅读
- 主函数部分
- 主模型定义
- 参考资料
提出背景
改进了RNN的训练慢的缺点,利用self-attention机制实现并行计算。并且Transformer可以增加到非常深的深度,充分发掘DNN模型的特性,提升模型准确率。同时使用注意力机制更容易关注序列的全局特征,解决了RNN不善处理长距离依赖的问题
本质上是一个seq2seq的模型,因此可用于机器翻译,语音辨识,语音合成等问题
总体结构
整体结构由编码器与解码器组成,一个编码器中可有若干个编码层
注意,Encoder中的参数各不相同
Encoder
编码器的作用在于,输入一个向量,输出一个等长的向量
要点:
- 使用多头注意力机制,关注全局输入向量的资讯
- 使用了残差及layer normalization,避免出现梯度消失问题
- 使用前向神经网络,每个位置的单词对应的前馈神经网络都完全一样
Self Attention
query,key,value全由输入向量自身计算得到的一种注意力机制。每一个输入向量分别对应了查询、键、值权重矩阵,用于生成查询、键、值向量,权重矩阵为训练参数
Decoder
输入有两部分
- 编码器最后一层的输出作为key和value
- 同时解码器自己的输入经过多头自注意力的计算作为query
输出为对应的结果序列
结构与编码器类似,唯一的不同在于Masked Multi-Head Attention使用了掩码机制,不再关注全局的资讯,而是只考虑了当前位置以及当前位置之前的信息,存在时序性
细节再探
-
Teacher forcing:将真实标签作为decoder的输入,依次预测结果,相当于把正确答案给解码器
-
掩码作用:考虑到Teacher forcing已经将正确答案输入到模型,因此在每次预测一个token时,需要将其及其之后的token先掩盖,保证训练任务和预测任务在执行自回归时,是同样的形式
- dk是query和key向量的维度
在计算注意力权重时除以dk,避免了随着dk的增大,即维度增大时,计算出的权重出现过大(softmax后的值非0即1)的情况,从而保留了数据原始的分布,可以使得梯度回传时更加平稳
- 使用layer normalize而非batch normalize:ln是针对一个样本内部做归一化;bn针对的是各个样本的特征做归一化,在文本长度变化较大时,这种归一化方式会造成均值方差抖动明显
- 多头self attention意在让根据词向量得出的query, key, value向量,进入不同的线性层,分别进行自注意力计算,捕捉全局信息,再将所有结果concat,最后再进入一个线性层得到结果
- 在decoder中计算自注意力时,query来自带有掩码的自注意力计算得出,而key和value来自encoder最后的输出,目的是让Decoder端的单词(token)给予Encoder端对应的单词(token)“更多的关注(attention weight)”
- 自注意力是基于全局信息进行计算,而忽略了时序信息,位置编码PE解决了这一问题。PE的长度与embedding相同,使二者可以直接相加。
计算方式是根据token的文本中的位置,PE向量中偶数位使用sin计算,奇数位使用cos计算。由于三角函数公式不受序列长度的限制,也就是可以对比所遇到序列的更长的序列进行表示
源码阅读
主函数部分
确定了模型的输入部分,由两部分组成encoder的输入,decoder的输入
将token转换成id后以tensor形式传入模型
P代表填充码,S代表起始,E代表结束。对文本进行了id映射
模型参数初始化,选用交叉熵损失函数,Adam自适应学习率优化器
主模型定义
class Transformer(nn.Module):
def __init__(self):
super(Transformer, self).__init__()
self.encoder = Encoder()
self.decoder = Decoder()
self.projection = nn.Linear(d_model, tgt_vocab_size, bias=False)
def forward(self, enc_inputs, dec_inputs):
enc_outputs, enc_self_attns = self.encoder(enc_inputs)
dec_outputs, dec_self_attns, dec_enc_attns = self.decoder(dec_inputs, enc_inputs, enc_outputs)
dec_logits = self.projection(dec_outputs) # dec_logits : [batch_size x src_vocab_size x tgt_vocab_size]
return dec_logits.view(-1, dec_logits.size(-1)), enc_self_attns, dec_self_attns, dec_enc_attns
由编码器,解码器及全连接层组成
编码器输入 为1*5
的张量,输出为1*5*512
的张量(批量大小为1,文本长度为5,embedding向量维度为512),及注意力权重参数6*8*5*5
(6代表编码器中编码层的数量;8代表8头注意力,计算了8次,有8个子空间;5*5
代表文本内部的相关性计算的结果)
解码器输入为编码器的输出及解码层输入文本
输出为注意力计算得出的结果(1*5*512
),及解码器掩码自注意力权重参数及交互注意力权重参数
将解码器的输出作为全连接层的输入,通过全连接层将512维向量压缩成7维,对应翻译文本的长度(5*7
)
参考资料
- 李沐Transformer论文逐段精读【论文精读】
- 李宏毅Transformer
- Transformer从零详细解读(可能是你见过最通俗易懂的讲解)