

NLP硬核入门-Seq2Seq和Attention机制
source link: https://www.tuicool.com/articles/Ebu2ea6
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

本文需要的前序知识储备是: 循环神经网络 RNN ,词向量 WordEmbedding ,门控单元 VanillaRNN/GRU/LSTM 。
1 seq2seq
seq2seq 是 sequence to sequence 的缩写。前一个 sequence 称为编码器 encoder ,用于接收源序列 source sequence 。后一个 sequence 称为解码器 decoder ,用于输出预测的目标序列 target sequence 。
seq2seq 主要用于序列生成任务,例如:机器翻译、文本摘要、对话系统,等等。当然也可以用于文本分类等任务。
图 1.1 seq2seq
最传统的 seq2seq 流程如图 1.1 所示:
( 1 )将源序列输入 encoder 网络。
( 2 ) encoder 将源序列的信息编码成一个定长的向量 encoder vector 。
( 3 ) encoder vector 被送入 decoder 网络。
( 4 ) decoder 根据输入的向量信息,输出预测的目标序列。
seq2seq 在被提出后,马上受到了广泛的关注和应用,也暴露出一些问题。首先被关注到的,就是人们发现把一整个文本序列通过 encoder 压缩到区区一个向量里,很难通过 decoder 进行完美地没有信息缺失的解码。
此外,由于循环神经网络 RNN 的特性,源序列中越迟输入的文本,对 encoder vector 的影响也越大。换句话说, encoder vector 里会包含更多的序列尾的文本信息,而忽略序列头的文本信息。所以在很多早期的论文中,会将文本序列进行倒序后再输入 encoder ,模型测评分数也会有一个显着地提高。
为了让 decoder 能够更好地提取源序列的信息, Bahdanau 在 2014 年提出了注意力机制 Attention Mechanism , Luong 在 2015 年对 Bahdanau Attention 进行了改进。这是两个最经典的注意力机制模型。两个 Attention 模型的本质思路是一样的,下文均以 Luong Attention 模型作为范例。
2 Attention Mechanism
注意力机制的理解,可以参考 CV 领域的思想:我们在看到一幅画的时候,每个时刻总会有一个关注重点,比如说某个人、某个物品、某个动作。
所以,在 NLP 领域,我们在通过 decoder 预测输出目标序列的时候,也希望能够有一种机制,将目标序列当前 step ,和源序列某几个 step 的文本关联起来。
以翻译任务为例,将“我爱机器学习”翻译成“ I love machine learning. ”在 decoder 输出序列第一个 step ,我们希望关注输入序列中的“我”,并将“我”翻译成“ I ”;在第三个 step ,我们希望关注“机器”,并翻译成“ machine ”。
这个例子比较简单,我们就会产生一个初步的想法:是不是把源序列中的每个词语单独翻译成英文,再依次输出,就构成目标序列了呢?
但是,如果进一步思考下,我们就会发现两个问题:
( 1 ) 一词多义 :源序列里的同一个词,在输出序列里,可能根据场景的不同,会有不同的输出。例如“我”可能被翻译成“ I ”,也有可能被翻译成“ me ”。这有点类似于中文的“一词多义”,在英文里估计是叫做“一词多态”吧,我们姑且将这类由一个词可以映射成多个词的现象,广义地统称为“一词多义”。解决“一词多义”问题的一个有效途径,就是参考源序列的语境信息,也就是上下文信息,来生成词向量。
( 2 ) 序列顺序 :源序列和目标序列并不是顺序依次映射的,例如“你是谁?”翻译成“ who are you? ”,不同语言有不同的语法规则和顺序。这就需要在 decoder 输出的每一个 step ,确定当前 step 应该翻译源序列中的哪一个词。
这两个问题也就是 Attention 机制的关注重点。
图 2.1 LuongAttention
图 2.1 是一个 Luong Attention 的示意图,是作者其后续的论文里呈现的一张修订后的示意图。
还有个理解 Attention 的方式,就是参考残差网络 ResNet 。因为源序列太长了,导致早期的信息无法有效地被传递,所以需要一个额外的通道,把早期的信息送到解码器上。送的时候还不能只送一个词的信息,最好把上下文信息一起给送了。
下一节会用一个最简单的模型,介绍 Attention 机制的实现步骤,在此之前,先约定下参数符号:
h(output) : RNN 的隐藏状态,主要用于将信息输出到 RNN 模型外。
s(state) : RNN 的状态,主要用于 RNN 模型内部,模型将信息传递给下一个 step 。
a :对齐向量
c :上下文信息向量。
x :源序列。
y :目标序列。
下标 s 表示源序列,下标 t 表示目标序列。 s1 表示源序列第 1 个 step ,以此类推。
括号里的 output 和 state ,是为了方便读者将论文里的算法理论,和工业实践里 tensorflow 的 tf.nn.dynamic_rnn 函数联系起来,稍微有个印象就好。 dynamic_rnn 函数返回两个向量,第一个是 output ,也就是 encoder 所有 step 、网络最后一层输出的 h ;第二个是 state ,也就是 encoder 最后一个 step 、网络层所有层输出的 s 。
3 Attention 五部曲
3.1 执行 encoder
图 3.1 步骤一:执行 encoder
步骤一的行为是将源数据依次输入 Encoder ,执行 Encoder 。 目的在于将源序列的信息,编译成语义向量,供后续 decoder 使用。
在每个 step , endocer 会输出一个表征当前 step 语义的 output ( h )向量和一个 state ( s )向量:
( 1 ) 我们收集 每个 step 的 output ( h ),构成 output 矩阵,矩阵的维度是 [step_len,dim_rnn] ,即源数据 step 长度,乘以 rnn 单元数量。
( 2 ) 我们收集 最后一个 step 的 state ( s ),作为传入 decoder 的向量。
encoder 对于模型的贡献,在于提供了 outputs 矩阵和 state 向量。
注一:为了便于理解,我这里的 encoder 使用了单层网络,多层网络的 outputs 和 state 见上一节末尾的描述。
注二:很多论文的 h 和 s 的描述并没有一个统一的标准,经常混淆。因为早期论文的 RNN 单元,是用 VanillaRNN 或 GRU 实现的,这两个门控单元在同一个 step ,输出的 h 和 s 是一样的。但是,若通过 LSTM 实现, h 和 s 是不同的,这个需要引起注意。
注三:早期的论文中, encoder 的 state 是直接传递给 decoder ,作为 initial state 的。但是在工程应用中,也存在直接将 0 序列作为 initial state 传递给 decoder 的情况。另外,部分论文也有将 state 进行一些处理,添加一些额外的信息,再传递给 decoder 的算法。总之, encoder 和 decoder 之间传递 state 的方式比较灵活,可以根据实际情况自行选择和改进。
注四: RNN 的单元数量,即为 encoder 输出向量的维度。也就是用 dim_rnn 维度的向量,来表征源序列当前 step 文本的语义信息。对照同样表征语义信息的词向量的维度 dim_word_embd ,我建议两者的维度不宜相差过大,否则会造成浪费。
3.2 计算对齐系数 a
图 3.2 步骤二:计算对齐系数 a
步骤二解决的是第 2 节提出的“序列顺序”的问题。 在 decoder 的每个 step ,我们需要关注源序列的所有 step 和目标序列当前 step 的相关性大小,并输出相关(对齐)系数 a 。
所以,在 decoder 输出一个预测值前,都会针对 encoder 的所有 step ,计算一个 score 。这个 score 表示当前的 decoder 工作,需要从 encoder 的哪些 step 里抽取信息,以及抽取的权重大小。然后将 score 汇总向量化后,每个 decoder step 能获得一个维度为 [step_len,1] 的 score 向量。
这个 score 的计算方式有很多种,图 3.2 中列举了 Luong Attention 提及的 3 种的传统计算方式。我画的流程图中采用的是第 1 种,就是将源序列所有 step 的 output ( h )和目标序列当前 step 的 output ( h )逐个相乘,得到的值即为 score 。有些论文就是在 score 的计算方式上进行创新的。
计算出 score 后,很自然地按惯例使用 softmax 进行归一化,得到对齐向量 a ,维度也是 [step_len,1] 。
注一:很多论文的各种参数的缩写符号都不一样,有一个理清模型流程顺序的小技巧:就是去找 softmax 函数,以 softmax 为锚点。 Attention 常见的使用 softmax 的地方有两个,一个是步骤二的对齐系数 a ,另一个在步骤五将会提到,在输出预测词之前,要对概率分数进行 softmax 归一化处理。
注二:对齐系数 a 虽然只是一个过程数据,但是却蕴含很重要的信息,可用于 PointerNet 和 CopyNet 。
3.3 计算上下文语义向量 c
图 3.3 步骤三:计算上下文语义向量 c
在描述这个步骤前,我们先回顾下词向量的 CBOW 模型。在 CBOW 模型收敛后,每个词的词向量,等于它周围若干个词的词向量的均值。这其中蕴含的意思是: 表征一个词的,不是这个词本身,而是这个词的上下文(语境)。
CBOW 模型是比较简单粗暴地将上下文的词向量求平均。实际上,如果能够以一个加权平均的方式获取词向量,那幺这个词向量一定能够更准确地表达这个词在当前语境里的语义。
举个例子:“孔夫子经历了好几个春秋寒暑,终于修订完成了春秋麟史。”在这里,第一个“春秋”表示“一年”,“经历”、“寒暑”显然和它关系更密切,利用加权上下文构成词向量时,应该赋予更高的权重。第二个“春秋”表示儒家六经之一,“修订”、“麟史”关系和它更密切,同样应该赋予高权重。
在步骤三里, 我们将对齐系数 a 作为权重,对 encoder 每个 step 的 output 向量进行加权求和(对齐向量 a 点乘 outputs 矩阵),得到 decoder 当前 step 的上下文语义向量 c 。
注一: BERT 也有用到对齐系数的思想,而且更为直观漂亮。
3.4 更新 decoder 状态
图 3.4 步骤四:更新 decoder 状态
在步骤四里,需要 更新 decoder 状态,这个状态可以是 h ,也可以是 s 。 能够用于更新 h 和 s 的信息数据,可以是:前 step 的 s ,现 step 的 h ,现 step 的上下文向量 c ,以及其它一些包含有效信息的数据。
BahdanauAttention 和 Luong Attention 最大的区别就在于这个步骤,前者更新的是 s ,后者更新的是 h 。不过由于 Bahdanau 用的是前 step 的 s , Luong 用的是先 step 的 h ,所以后者在工程化实现上会简单点。
具体的更新公式的细节,在这里不作详细描述,因为不同模型可能会采用不同的更新公式,很多论文也是围绕更新公式进行创新点研究的。
需要注意的是,在这个环节,训练模式和预测模式略有差别: decoder 每个 step 都要输入一个数据,在训练模式,输入的数据是目标序列当前 step 的真实值,而不使用前 step 的 h ;在预测模式,输入的数据是前 step 的 h ,而不使用输出序列的真实值。虽然在图 3.4 中,我画了两条输入,但是要根据模型当前处于训练模式还是预测模式,选择其中的一条进行输入。
3.5 计算输出预测词
图 3.5 步骤五:计算输出预测词
这个步骤我在图里没有画全,其实很简单,同 CBOW 模型 /Skip-Gram 模型的隐藏层到输出层的那部分一样,做一个语义向量到目标词表的映射(如果 attention 用于分类模型,那就是做一个到各个分类的映射),然后再进行 softmax 就可以了。
4 其它
4.1 Local Attention 和 Global Attention
前文所提及的 Attention 都是 Global Attention ,还有一个 Local Attention ,将在这个小节作一个简单的说明。
Global Attention 就是针对源序列的所有 step ,求对齐系数 a 。而 LocalAttention 只针对源序列的部分 step ,求对齐系数 a ,这个部分 step 的长度是超参数,需要凭经验人为配置。
Local Attention 所截取的部分 step 的中心点的选取(对齐)方式,是另一个需要关注的点。论文中提及了两个对齐方式:
( 1 ) Monotonicalignment (local-m) :简单粗暴的,直接按源序列和目标序列的 step 绝对值对齐。
( 2 ) Predictivealignment (local-p) :通过模型,学习计算出截断 step 的对齐中心。
Luong 的论文里有提及, LocalAttention 的效果优于 Global Attention 。
注: CV 领域有个 Soft-Attention 和 Hard-Attention ,和这里 NLP 领域的两个 Attention 优点类似。
4.2 常见的可以替换改进的模块
1. 用于生成对齐向量 a 的分值 score 的计算方式。
2.h 和 s 的更新公式。
3. 基本 RNN 的结构,包括替换门控单元、更改 RNN 层数、单向改双向等。
参考资料
[1] Bahdanau D ,Cho K , Bengio Y . Neural Machine Translation by Jointly Learning to Align andTranslate[J]. Computer Science, 2014.
[2] Luong M T ,Pham H , Manning C D . Effective Approaches to Attention-based Neural MachineTranslation[J]. Computer Science, 2015.
[3] Andrew Ng RecurrentNeural Networks
由于微信文章 有 修改字数的限制,故附上知乎文章的链接: https://zhuanlan.zhihu.com/p/73589030
后续有更新或纠错,会在知乎文章上呈现。
本文转载自公众号: 数论遗珠,作者阮智昊
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK