一、机器翻译及相关技术
机器翻译(MT):将一段文本从一种语言自动翻译为另一种语言,用神经网络解决这个问题通常称为神经网络翻译(NMT)。
主要特征:输出是单词序列而不是单个单词。输出序列的长度可能与源序列的长度不可。
数据预处理:
将数据集清洗、转化为神经网络的输入minbatch
空格处理编码。数据清洗。
大小写修改
分词source:
字符串----单词组成的列表
单词表里的特殊符号:
句子开始符、句子结束符、未知单词
建立字典
token单词组成的列表----单词id组成的列表
tokenization 用于把字符型式的句子转化为单词组成的列表
counter:词频统计
得到数据生成器
载入数据集
Encoder-Decoder
encoder:输入到隐藏状态(语义编码)
decoder:隐藏状态(语义编码)到输出
应用:机器翻译、对话机器人、语音识别任务。
Sequence to Sequence模型
训练时decode每个单元输出得到的单词作为下一个单元的输入单词。
预测时decoder单元输出为句子结束符时跳出循环。
每个batch训练时encoder和decoder都有固定长度的输入。
集束搜索(Beam Search)
集束搜索结合了greedy search和维特比算法
集束搜索使用beam size参数来限制再每一步保留下来的可能性词的数量。
集束搜索是一种贪心算法。
二、注意力机制与Seq2seq模型
在“编码器—解码器(seq2seq)”⼀节⾥,解码器在各个时间步依赖相同的背景变量(context vector)来获取输⼊序列信息。当编码器为循环神经⽹络时,背景变量来⾃它最终时间步的隐藏状态。将源序列输入信息以循环单位状态编码,然后将其传递给解码器以生成目标序列。然而这种结构存在着问题,尤其是RNN机制实际中存在长程梯度消失的问题,对于较长的句子,我们很难寄希望于将输入的序列转化为定长的向量而保存所有的有效信息,所以随着所需翻译句子的长度的增加,这种结构的效果会显著下降。
与此同时,解码的目标词语可能只与原输入的部分词语有关,而并不是与所有的输入有关。例如,当把“Hello world”翻译成“Bonjour le monde”时,“Hello”映射成“Bonjour”,“world”映射成“monde”。在seq2seq模型中,解码器只能隐式地从编码器的最终状态中选择相应的信息。然而,注意力机制可以将这种选择过程显式地建模。
Seq2seq模型
本节中将注意机制添加到sequence to sequence 模型中,以显式地使用权重聚合states。下图展示encoding 和decoding的模型结构,在时间步为t的时候。此刻attention layer保存着encodering看到的所有信息——即encoding的每一步输出。在decoding阶段,解码器的t时刻的隐藏状态被当作query,encoder的每个时间步的hidden states作为key和value进行attention聚合. Attetion model的输出当作成上下文信息context vector,并与解码器输入Dt拼接起来一起送到解码器:
点积注意力
代码操作:
1 # Save to the d2l package. 2 class DotProductAttention(nn.Module): 3 def __init__(self, dropout, **kwargs): 4 super(DotProductAttention, self).__init__(**kwargs) 5 self.dropout = nn.Dropout(dropout) 6 7 # query: (batch_size, #queries, d) 8 # key: (batch_size, #kv_pairs, d) 9 # value: (batch_size, #kv_pairs, dim_v) 10 # valid_length: either (batch_size, ) or (batch_size, xx) 11 def forward(self, query, key, value, valid_length=None): 12 d = query.shape[-1] 13 # set transpose_b=True to swap the last two dimensions of key 14 15 scores = torch.bmm(query, key.transpose(1,2)) / math.sqrt(d) 16 attention_weights = self.dropout(masked_softmax(scores, valid_length)) 17 print("attention_weight ",attention_weights) 18 return torch.bmm(attention_weights, value)
三、Transformer
在之前的章节中,我们已经介绍了主流的神经网络架构如卷积神经网络(CNNs)和循环神经网络(RNNs)。让我们进行一些回顾:
- CNNs 易于并行化,却不适合捕捉变长序列内的依赖关系。
- RNNs 适合捕捉长距离变长序列的依赖,但是却难以实现并行化处理序列。
为了整合CNN和RNN的优势,[Vaswani et al., 2017] 创新性地使用注意力机制设计了Transformer模型。该模型利用attention机制实现了并行化捕捉序列依赖,并且同时处理序列的每个位置的tokens,上述优势使得Transformer模型在性能优异的同时大大减少了训练时间。
图10.3.1展示了Transformer模型的架构,与9.7节的seq2seq模型相似,Transformer同样基于编码器-解码器架构,其区别主要在于以下三点:
- Transformer blocks:将seq2seq模型重的循环网络替换为了Transformer Blocks,该模块包含一个多头注意力层(Multi-head Attention Layers)以及两个position-wise feed-forward networks(FFN)。对于解码器来说,另一个多头注意力层被用于接受编码器的隐藏状态。
- Add and norm:多头注意力层和前馈网络的输出被送到两个“add and norm”层进行处理,该层包含残差结构以及层归一化。
- Position encoding:由于自注意力层并没有区分元素的顺序,所以一个位置编码层被用于向序列元素里添加位置信息。
多头注意力层
在我们讨论多头注意力层之前,先来迅速理解以下自注意力(self-attention)的结构。自注意力模型是一个正规的注意力模型,序列的每一个元素对应的key,value,query是完全一致的。如图10.3.2 自注意力输出了一个与输入长度相同的表征序列,与循环神经网络相比,自注意力对每个元素输出的计算是并行的,所以我们可以高效的实现这个模块。
多头注意力层包含h个并行的自注意力层,每一个这种层被成为一个head。对每个头来说,在进行注意力计算之前,我们会将query、key和value用三个现行层进行映射,这h个注意力头的输出将会被拼接之后输入最后一个线性层进行整合。
基于位置的前馈网络
Transformer 模块另一个非常重要的部分就是基于位置的前馈网络(FFN),它接受一个形状为(batch_size,seq_length, feature_size)的三维张量。Position-wise FFN由两个全连接层组成,他们作用在最后一维上。因为序列的每个位置的状态都会被单独地更新,所以我们称他为position-wise,这等效于一个1x1的卷积。
下面我们来实现PositionWiseFFN:
1 # Save to the d2l package. 2 class PositionWiseFFN(nn.Module): 3 def __init__(self, input_size, ffn_hidden_size, hidden_size_out, **kwargs): 4 super(PositionWiseFFN, self).__init__(**kwargs) 5 self.ffn_1 = nn.Linear(input_size, ffn_hidden_size) 6 self.ffn_2 = nn.Linear(ffn_hidden_size, hidden_size_out) 7 8 9 def forward(self, X): 10 return self.ffn_2(F.relu(self.ffn_1(X)))
与多头注意力层相似,FFN层同样只会对最后一维的大小进行改变;除此之外,对于两个完全相同的输入,FFN层的输出也将相等
Add and Norm
除了上面两个模块之外,Transformer还有一个重要的相加归一化层,它可以平滑地整合输入和其他层的输出,因此我们在每个多头注意力层和FFN层后面都添加一个含残差连接的Layer Norm层。这里 Layer Norm 与7.5小节的Batch Norm很相似,唯一的区别在于Batch Norm是对于batch size这个维度进行计算均值和方差的,而Layer Norm则是对最后一维进行计算。层归一化可以防止层内的数值变化过大,从而有利于加快训练速度并且提高泛化性能。