0. 简单理解
在原论文Attention is all you need当中所提出的Transformer架构将过去的RNN/CNN 架构全部替换为Attention结构,提高了在NLP领域对于全文的理解能力和并行度,并且在其它领域该架构也有广泛的应用,本文作者为深度学习领域小白,尽可能用通俗的语言来描述这一架构,如果文中有错误或者理解不到位的地方,敬请读者指出
这里的并行度是指之前的RNN模型,当前时刻的输出依赖于前一个时刻的输出,并不能进行并行计算,这里的Transformer模型可以一次抓取整个句子的信息,主要运算时矩阵乘,所以有很强的并行性
1. 总体架构

1.1 Encoder
Transformer的总体架构如下图所示,左侧部分为编码器encoder,它负责将我们的翻译目标token(最小语义块)拆解成一个个的key和value, 类似于变形金刚当中的拆解. 它的输入为翻译目标的句子,由于不同的token长度不同,所以我们需要进行padding操作,但是padding的部分我们不用关心,所以需要进行掩码操作,这个掩码其实是一个上三角矩阵,将我们不需要关心的位置置为True即可.
它有两个子层,一个是多头自注意力层,另外一个是前向层.Transfomer当中的Encoder由N个这样的结构堆叠而成(原文中为6).
Encoder层的输出为key 和 value,提供给Decoder层使用
1.2 Decoder
右侧部分为Decoder层,它由三个子层堆叠而成,分别是带掩码的自注意层、多头注意力层以及一个全连接层(dropout = 0.1).
需要注意的是,在第二个子层当中,Multi-head Attention的K、V来自Encoder的输出,这部分不理解可以跳转到文末的附录当中查看

2. 实现代码
2.1 数据预处理
Transfomer当中的输入输出都是将句子中的token用映射来表示, 输入/输出 所有的token会用一个词典来记录,我们的inputs就是记录句子的单词在词典当中对应的下标即可
sentence = [
# enc_input dec_input dec_output
['ich mochte ein bier P','S i want a beer .', 'i want a beer . E'],
['ich mochte ein cola P','S i want a coke .', 'i want a coke . E'],
]
# 词典,padding用0来表示
# 源词典
src_vocab = {
'P':0, 'ich':1,'mochte':2,'ein':3,'bier':4,'cola':5}
src_vocab_size = len(src_vocab) # 6
# 目标词典(包含特殊符)
tgt_vocab = {
'P':0,'i':1,'want':2,'a':3,'beer':4,'coke':5,'S':6,'E':7,'.':8}
# 反向映射词典,idx ——> word
idx2word = {
v:k for k,v in tgt_vocab.items()}
tgt_vocab_size = len(tgt_vocab) # 9
src_len = 5 # 输入序列enc_input的最长序列长度,其实就是最长的那句话的token数
tgt_len = 6 # 输出序列dec_input/dec_output的最长序列长度
# 这个函数把原始输入序列转换成token表示
def make_data(sentence):
enc_inputs, dec_inputs, dec_outputs = [],[],[]
for i in range(len(sentence)):
enc_input = [src_vocab[word] for word in sentence[i][0].split()]
dec_input = [tgt_vocab[word] for word in sentence[i][1].split()]
dec_output = [tgt_vocab[word] for word in sentence[i][2].split()]
#这里的enc_inputs、dec_inputs存放的都是词典的序列号 在给出的sentence当中已经对句子进行了填充 相同位置(encode decode)都是相同长度
enc_inputs.append(enc_input)
dec_inputs.append(dec_input)
dec_outputs.append(dec_output)
# LongTensor是专用于存储整型的,Tensor则可以存浮点、整数、bool等多种类型
return torch.LongTensor(enc_inputs),torch.LongTensor(dec_inputs),torch.LongTensor(dec_outputs)
enc_inputs, dec_inputs, dec_outputs = make_data(sentence)
# 使用Dataset加载数据
class MyDataSet(Data.Dataset):
def __init__(self,enc_inputs, dec_inputs, dec_outputs):
super(MyDataSet,self).__init__()
self.enc_inputs = enc_inputs
self.dec_inputs = dec_inputs
self.dec_outputs = dec_outputs
#构造一个DataSet的常用步骤 实现__len__和__getitem__两个函数
def __len__(self):
# 我们前面的enc_inputs.shape = [2,5],所以这个返回的是2
return self.enc_inputs.shape[0]
# 根据idx返回的是一组 enc_input, dec_input, dec_output
def __getitem__(self, idx):
return self.enc_inputs[idx], self.dec_inputs[idx], self.dec_outputs[idx]
# 构建DataLoader
loader = Data.DataLoader(dataset=MyDataSet(enc_inputs,dec_inputs, dec_outputs),batch_size=2,shuffle=True)
2.2 模型参数
# 用来表示一个词的向量长度 //词的原始长度
d_model = 512
# FFN的隐藏层神经元个数
d_ff = 2048
# 分头后的q、k、v词向量长度,依照原文我们都设为64
# 原文:queries and kes of dimention d_k,and values of dimension d_v .所以q和k的长度都用d_k来表示
d_k = d_v = 64
# Encoder Layer 和 Decoder Layer的个数
n_layers = 6
# 多头注意力中head的个数,原文:we employ h = 8 parallel attention layers, or heads
n_heads = 8
2.2 Positional Encoding 位置编码
#输入的embedding 构造一个5000个token * d_model 的矩阵 取句子到的位置上的就行
class PositionalEncoding(nn.Module):
def __init__(self, d_model, dropout=0.1, max

本文为深度学习小白视角,介绍Transformer架构。它将RNN/CNN替换为Attention结构,提高NLP领域理解能力与并行度。文中阐述总体架构,包括Encoder和Decoder,给出实现代码,涉及数据预处理、模型参数等,还对Q、K、V及模型理解作了解释。
最低0.47元/天 解锁文章
8360

被折叠的 条评论
为什么被折叠?



