机器翻译(MT)是将一个句子 x 从一种语言( 源语言 )转换为另一种语言( 目标语言 )的句子 y 的任务。
机器翻译的大致流程就是根据输入的文本,神经网络开始学习和记忆,这个就是所谓的Encoder编码过程;然后根据自己的记忆,把文本一一翻译出来,这个就是所谓的Decoder解码过程。
机器翻译的基本流程如下:
seq2seq的用途有很多,比如机器翻译,写诗,作曲,看图写文字等等用途很广泛!该模型最早在2014年被Cho和Sutskever先后提出,前者将该模型命名为“Encoder-Decoder Model”也就是编码-解码模型,后者将其命名为“Sequence to Sequence Model”也就是序列到序列模型,两者有一些细节上的差异,但总体大致思想基本相同。seq2seq根据字面意思来看就是序列到序列,再具体点就是输入一个序列(可以是一句话,一个图片等)输出另一个序列。
训练
预测
具体结构:
Seq2Seq model 是有encoder和decoder组成的,它主要的目的是将输入的文字翻译成目标文字。其中encoder和decoder都是RNN,(可以是RNN/LSTM/或者GRU或者是双向RNN)。模型将source的文字编码成一串固定长度的context编码,之后利用这段编码,使用decoder解码出具体的输出target。这种转化任务可以适用于:翻译,语音转化,对话生成等序列到序列的任务。
下面进一步了解下seq2seq的具体流程:
这个是某个时刻t的RNN隐层单元,R,O代表里面的两个gate,下表E/D代表这个是Encoder/Decoder过程。
这个在Encoder是某个时刻输入词,在Decoder是上一个预测的词。
这个是Encoder的最后一个单元,里面的隐层存着记忆单元呢,Decoder需要根据Encoder的这个记忆单元隐层把帮助自己一个一个的预测词。所以需要单独传入每一个Decoder里面。
下面这个是Decoder的核心过程,拿一次预测举例子:
y i − 1 y_{i-1} yi−1是上一个预测词,这里是fox。 s i − 1 s_{i-1} si−1是上次预测完的记忆单元。 c i c_{i} ci是Encoder的隐藏单元。 y i y_{i} yi的计算方式为,g为一种方法(神经网络中一般为每个参数*W,最后求和输出。下面公式都类似):
$s_{i}的计算方式为:
在“编码器—解码器(seq2seq)”⼀节⾥,解码器在各个时间步依赖相同的背景变量(context vector)来获取输⼊序列信息。当编码器为循环神经⽹络时,背景变量来⾃它最终时间步的隐藏状态。将源序列输入信息以循环单位状态编码,然后将其传递给解码器以生成目标序列。然而这种结构存在着问题,尤其是RNN机制实际中存在长程梯度消失的问题,对于较长的句子,我们很难寄希望于将输入的序列转化为定长的向量而保存所有的有效信息,所以随着所需翻译句子的长度的增加,这种结构的效果会显著下降。
与此同时,解码的目标词语可能只与原输入的部分词语有关,而并不是与所有的输入有关。例如,当把“Hello world”翻译成“Bonjour le monde”时,“Hello”映射成“Bonjour”,“world”映射成“monde”。在seq2seq模型中,解码器只能隐式地从编码器的最终状态中选择相应的信息。然而,注意力机制可以将这种选择过程显式地建模。
Attention 是一种通用的带权池化方法,输入由两部分构成:询问(query)和键值对(key-value pairs)。$_∈ℝ^{_}, _∈ℝ^{_} $. Query $∈ℝ^{_} $ , attention layer得到输出与value的维度一致 $ ∈ℝ^{_} $ . 对于一个query来说,attention layer 会与每一个key计算注意力分数并进行权重的归一化,输出的向量$ o $则是value的加权求和,而每个key计算的权重与value一一对应。
为了计算输出,我们首先假设有一个函数 α \alpha α 用于计算query和key的相似性,然后可以计算所有的 attention scores a 1 , … , a n a_1, \ldots, a_n a1,…,an by
a i = α ( q , k i ) . a_i = \alpha(\mathbf q, \mathbf k_i). ai=α(q,ki).
我们使用 softmax函数 获得注意力权重:
b 1 , … , b n = softmax ( a 1 , … , a n ) . b_1, \ldots, b_n = \textrm{softmax}(a_1, \ldots, a_n). b1,…,bn=softmax(a1,…,an).
最终的输出就是value的加权求和:
o = ∑ i = 1 n b i v i . \mathbf o = \sum_{i=1}^n b_i \mathbf v_i. o=i=1∑nbivi.
不同的attetion layer的区别在于score函数的选择,两个常用的注意层 Dot-product Attention 和 Multilayer Perceptron Attention。
具体的网络分布图如下:
Attention Is All You Need提出的transformer 其实就是 seq2seq + self attention。 代码实现, 非常清晰。
seq2seq 任务指的是输入和输出都是序列的任务。例如说法语翻译成英文。
通常来说,Seq2Seq任务最常见的是使用encoder+decoder的模式,先将一个序列编码成一个上下文矩阵,在使用decoder来解码。当然,我们仅仅把context vector作为编码器到解码器的输入。
这样子往往得不到好的效果,因为我们的编码器的很多信息都无法完全编码在这个向量中,并且我们在解码的时候,对于输入的每个单词的权重是不一致的,所以在NMT任务上,还添加了attention的机制。
所以目前来说,我们可以直接先把transformer当成一个黑盒,就是transformer可以当成是一个序列转码的模型,只是它其中用了特殊的self-attention的机制。如下图所示:
为什么需要用transformer
在提到为什么需要用transformer的时候,我们需要了解,在没有transformer的时候,我们都是用什么来完成这系列的任务的呢?
其实在之前我们使用的是RNN(或者是其的单向或者双向变种LSTM/GRU等) 来作为编解码器。
RNN模块每次只能够吃进一个输入token和前一次的隐藏状态,然后得到输出。它的时序结构使得这个模型能够得到长距离的依赖关系,但是这也使得它不能够并行计算,模型效率十分低。
当然这边的的RNN可以通过CNN替换,从而达到并行的效果,可以看到下图,总共是两层的卷积层,第一层画出了两个filter,每个1D filter的size是2,到了第二层的卷积层的filter的size是3。
第一层的filter考虑的是两个字之间的关联,但是到了第二层,考虑了三个前一层输出的交互,从而考虑到了较长序列之间的关系。比如说这边序列是 , 第一层只考虑了 , … 的交互,第二层考虑了 ,而 是前一层两两交互关系的结果,所以第二层考虑了 这个序列的结果了。
但是对于CNN每次一般我们的卷积核设的长度为3/5这种较小的值,对于序列长度较长的,比如512,就需要堆叠多层的卷积层,导致模型过于冗杂。
那么,我们有没有办法提出一个新的模型,能够并行,并且能够考虑到输入序列不同token的权重?聪明的科学家们提出了一种新的模型叫做transformer。
其实就是encoder+decoder模式,只是其中的编解码器采用了self-attention的机制。
当然transformer真的就比RNN好吗?有人提出,凡事用RNN做的模型,都可以直接用self-attention替代。这个我们会在transformer的缺点中讨论。# tranformer的内部结构
transformer其实是由encoder以及decoder不是单一模块,而是由小的多个sub-encoder block和sub-decoder block组成。
与seq2seq模型相似,Transformer同样基于编码器-解码器架构,其区别主要在于以下三点:
encoder
这边的encoder由input以及多个sub-encoder blocks组成。我们将会先讲sub-encoder,再讲输入,因为输入的设计是为了弥补self-attention的缺陷的。
sub-encoder block
首先每个sub-encoder都由两个主要的部分组成(略过部分细节,之后会写),分别是self-attention layer以及ffn layer。
具体的实现机制就是:我们的输入每个词经过embedding 之后,然后经过self-attention ,根据自己的路径,经过转换得到新的输出vector,最后再经过ffn layer,得到新的输出,作为下一层sub-encoder的输入。
multi-head self-attention
在我们讨论多头注意力层之前,先来迅速理解以下自注意力(self-attention)的结构。自注意力模型是一个正规的注意力模型,序列的每一个元素对应的key,value,query是完全一致的。如图10.3.2 自注意力输出了一个与输入长度相同的表征序列,与循环神经网络相比,自注意力对每个元素输出的计算是并行的,所以我们可以高效的实现这个模块。
F i g . 10.3.2 自 注 意 力 结 构 Fig.10.3.2\ 自注意力结构 Fig.10.3.2 自注意力结构
多头注意力层包含 h h h个并行的自注意力层,每一个这种层被成为一个head。对每个头来说,在进行注意力计算之前,我们会将query、key和value用三个现行层进行映射,这 h h h个注意力头的输出将会被拼接之后输入最后一个线性层进行整合。
F i g . 10.3.3 多 头 注 意 力 Fig.10.3.3\ 多头注意力 Fig.10.3.3 多头注意力
假设query,key和value的维度分别是 d q d_q dq、 d k d_k dk和 d v d_v dv。那么对于每一个头 i = 1 , … , h i=1,\ldots,h i=1,…,h,我们可以训练相应的模型权重 W q ( i ) ∈ R p q × d q W_q^{(i)} \in \mathbb{R}^{p_q\times d_q} Wq(i)∈Rpq×dq、 W k ( i ) ∈ R p k × d k W_k^{(i)} \in \mathbb{R}^{p_k\times d_k} Wk(i)∈Rpk×dk和 W v ( i ) ∈ R p v × d v W_v^{(i)} \in \mathbb{R}^{p_v\times d_v} Wv(i)∈Rpv×dv,以得到每个头的输出:
o ( i ) = a t t e n t i o n ( W q ( i ) q , W k ( i ) k , W v ( i ) v ) o^{(i)} = attention(W_q^{(i)}q, W_k^{(i)}k, W_v^{(i)}v) o(i)=attention(Wq(i)q,Wk(i)k,Wv(i)v)
这里的attention可以是任意的attention function,比如前一节介绍的dot-product attention以及MLP attention。之后我们将所有head对应的输出拼接起来,送入最后一个线性层进行整合,这个层的权重可以表示为 W o ∈ R d 0 × h p v W_o\in \mathbb{R}^{d_0 \times hp_v} Wo∈Rd0×hpv
o = W o [ o ( 1 ) , … , o ( h ) ] o = W_o[o^{(1)}, \ldots, o^{(h)}] o=Wo[o(1),…,o(h)]
接下来我们就可以来实现多头注意力了,假设我们有h个头,隐藏层权重 h i d d e n _ s i z e = p q = p k = p v hidden\_size = p_q = p_k = p_v hidden_size=pq=pk=pv 与query,key,value的维度一致。除此之外,因为多头注意力层保持输入与输出张量的维度不变,所以输出feature的维度也设置为 d 0 = h i d d e n _ s i z e d_0 = hidden\_size d0=hidden_size。
Transformer 模块另一个非常重要的部分就是基于位置的前馈网络(FFN),它接受一个形状为(batch_size,seq_length, feature_size)的三维张量。Position-wise FFN由两个全连接层组成,他们作用在最后一维上。因为序列的每个位置的状态都会被单独地更新,所以我们称他为position-wise,这等效于一个1x1的卷积。
在self-attention层之后模型会经过FFN层。\begin{equation} FFN(x) = max(0, xW_1 + b_1 )W_2 + b_2 \end{equation} 这边的实现就是两层的Dense layer,第一层的激活函数是RELU。