简介:
本文在论文的基础上结合代码来对Transformer进行详细的解释,根据Transformer的流程顺序对其中涉及的技术原理结合代码进行详细地阐述。同时,尽可能地去解释这些功能产生了什么作用。
Transformer的运行流程如上图所示,输入一串字符通过encoder层得到一个结果,将这个结果送到每一层的DECODER中,最后通过DECODER输出目标结果。
上图是论文《attention is all you need》中所展示的transformer结构图,本文以从左往右、从下往上的顺序进行分析。
Encoder:
class Encoder(nn.Module):
def __init__(self,
input_dim,hid_dim,n_layers, n_heads,pf_dim,dropout, device,
max_length = 100):
super().__init__()
self.device = device
self.tok_embedding = nn.Embedding(input_dim, hid_dim)
self.pos_embedding = nn.Embedding(max_length, hid_dim)
self.layers = nn.ModuleList([EncoderLayer(hid_dim, n_heads,pf_dim,
dropout,device)
for _ in range(n_layers)])
self.dropout = nn.Dropout(dropout)
self.scale = torch.sqrt(torch.FloatTensor([hid_dim])).to(device)
def forward(self, src, src_mask):
#src = [batch size, src len]
#src_mask = [batch size, src len]
batch_size = src.shape[0]
src_len = src.shape[1]
pos = torch.arange(0, src_len).unsqueeze(0).repeat(batch_size, 1).to(self.device)
#pos = [batch size, src len]
src = self.dropout((self.tok_embedding(src) * self.scale) + self.pos_embedding(pos))
#src = [batch size, src len, hid dim]
for layer in self.layers:
src = layer(src, src_mask)
#src = [batch size, src len, hid dim]
return src
输入字符串首先被向量化乘上 d m o d e l \sqrt{d_{model}} dmodel(维度),这一步骤应该是用来降低position encoding对原有向量的影响。另外,trg(Decoder的输入)和src(Encoder的输入)共享embedding层的权重,这样做可以减少运算所需要的内存。
由于德语和英语同属日耳曼语言,有很多相同的subword有着相似的含义。如果两种语言没有多少共同的subword(比如中英),使用共享词表也并不会有什么性能损失(由于词表变大,速度上可能有微小的损失),因为对于encoder和decoder而言都只有对应的语言的embedding会被激活. [1]
然后利用positional encodings给输入向量加上位置信息(代码中参考了Bert,直接加上pos_embedding),这是因为self-attention无法像RNN和CNN一样可以直接利用sequence的顺序,所以需要对向量进行一个处理,即,词向量与对应的位置向量相加,让transformer也可以利用sequence的顺序信息。在加上位置信息后dropout,dropout在transformer中多次出现,它可以抑制模型过拟合。
dropout根据一定的概率 p p p 临时隐藏一些神经网络节点(输入输出节点除外),这些被隐藏的网络节不参与这一批样本的训练,未被隐藏的节点正常更新参数。在训练结束后,重新随机隐藏部分节点。dropout通过隐藏部分节点形成一个“新”的神经网络,整个过程相当于对很多个不同的神经网络取平均。 而不同的网络产生不同的过拟合,一些互为“反向”的拟合相互抵消就可以达到整体上减少过拟合。同时,dropout程序导致两个神经元不一定每次都在一个dropout网络中出现。(这样权值的更新不再依赖于有固定关系的隐含节点的共同作用,阻止了某些特征仅仅在其它特定特征下才有效果的情况)。 迫使网络去学习更加鲁棒的特征 (这些特征在其它的神经元的随机子集中也存在)[2]。
PositionalEncoding
这一部分的代码来源于 The Annotated Transformer
class PositionalEncoding(nn.Module):
"Implement the PE function."
def __init__(self, d_model, dropout, max_len=5000):
super(