NLP预训练模型【3】 – seq2seq模型与RNN、LSTM等基础编解码器

引自:@mantchs
GitHub:https://github.com/NLP-LOVE/ML-NLP

1. 什么是seq2seq?

在⾃然语⾔处理的很多应⽤中,输⼊和输出都可以是不定⻓序列。以机器翻译为例,输⼊可以是⼀段不定⻓的英语⽂本序列,输出可以是⼀段不定⻓的法语⽂本序列,例如:

英语输⼊:“They”、“are”、“watching”、“.”
法语输出:“Ils”、“regardent”、“.”

当输⼊和输出都是不定⻓序列时,可以使⽤编码器—解码器(encoder-decoder)或者seq2seq模型(seq2seq属于encoder-decoder结构的一种)。encoder-decoder结构的基本思想就是利用两个RNN,一个RNN作为encoder,另一个RNN作为decoder。encoder负责将输入序列压缩成指定长度的向量,这个向量就可以看成是输入序列的定长表征(通常被成为背景向量),这个过程称为编码。如下图,获取语义向量最简单的方式就是直接将最后一个输入的隐状态作为语义向量c。当然,也可以对向量 cc 做变换得到新的语义向量,还可以将输入序列的所有隐含状态做变换得到语义向量。编码完成后再用一个RNN对 cc 的结果进行解码,简而言之就是将 cc 作为初始状态的隐变量输入到解码网络。

img

编码器-解码器网络示意图

seq2seq模型最早是在2013年由cho等人提出的一种RNN模型[1],主要的应用目的就是机器翻译。除了上面这篇文章之外,奠定seq2seq理论的还有谷歌的论文[2]。seq2seq不仅可以用于机器翻译,还能够用于很多NLP任务,如下图的聊天机器人系统。

img

2. 基础的编码器

编码器的作⽤是把⼀个不定⻓的输⼊序列变换成⼀个定⻓的背景变量 $ e $在该背景变量中编码输⼊序列信息。常⽤的编码器是LSTM循环神经⽹络。在encoder中 ,每个单词都被映射成一个 dd 维的词向量 wRdw\subset\mathbb{R^d} ,在下图所示的例子中,输入“ how are you ” 将被转化成 $[w_{0},w_{1},w_{2}]\subset \mathbb{R}^{d\times 3} $ ,经过LSTM后,可以得到每一个词对应的隐状态 $[e_{0},e_{1},e_{2}] $ ,和背景变量e。

通常情况下,构造背景变量 ee 的方法会比较复杂,进而发展出了很多形式的编码器。例如更一般的,考虑批量⼤小为1的时序数据样本。假设输⼊序列是 $ x_1, . . . , x_T $,例如 $ x_i $ 是输⼊句⼦中的第 ii 个词。在时间步 tt ,循环神经⽹络将输⼊ $ x_t $ 的特征向量 $ x_t $ 和上个时间步的隐变量 $ e_{t-1} $ 变换为当前时间步的隐变量 $ e_t $ 。可以⽤函数 $ f $ 表达循环神经⽹络隐藏层的变换:

et=f(xt,et1)e_t=f(x_t,e_{t-1})

接下来,编码器通过⾃定义函数 qq 将各个时间步的隐变量变换为背景变量:

e=q(e1,...,eT) e=q(e_1,...,e_T)

一种最简单的方式是将最后一个时间步的隐变量作为编码器的背景变量,即 $ e=q(e_1, . . . ,e_T) = e_T $ 。

以上描述的编码器是⼀个单向的循环神经⽹络,每个时间步的隐变量只取决于该时间步及之前的输⼊⼦序列。也可以使⽤双向循环神经⽹络构造编码器。在这种情况下,编码器每个时间步的隐变量同时取决于该时间步之前和之后的⼦序列(包括当前时间步的输⼊),并编码了整个序列的信息。

3. 基础的解码器

由于编码器输出的背景变量 ee 编码了整个输⼊序列 x1,...,xTx_1, . . . , x_T 的信息。因此,对于输出序列 $ [y_1, y_2, . . . , y_T] $ 中的每个时间步 $ t′ $ ,解码器输出 $ y_{t′} $ 的条件概率应当基于之前的子序列 $ [y_1,…,y_{t’-1} ]$ 和背景变量 $ e $ ,即:$ P(y_{t’}|y_1,…,y_{t’-1},e) $ 。

实现上述条件概率计算的最直接想法是引入另⼀个循环神经⽹络作为解码器,在输出序列的时间步 $ t′ $ ,将上⼀时间步的输出 yt1y_{t'-1} 以及背景变量 ee 作为输⼊,同时引入上⼀时间步的隐变量 $ h_{t’-1}$ 一起变换为当前时间步的隐变量 $ h_{t′}$ 。因此,可以⽤函数 gg 表达解码器隐藏层的变换:$ s_{t’}=g(y_{t’-1},e,h_{t’-1}) $ 。

下图为以背景变量 ee 为输入的解码器,以特殊字符 wsosw_{sos}作为起时字符,特殊字符 weosw_{eos}作为终止字符得到目标序列。

image-20210331182514466

(1)当时间步等0时:

h0=LSTM(e,wsos )s0=g(h0)p0=softmax(s0)i0=argmax(p0)eEncoder输出的句子向量wsos:特殊词,代表起时位置,作为当前时间步骤的输入h0:当前时间步骤的隐状态。h0Rhh为隐层的维度s0:词表中,每个词的得分。s0Rvv为词表的大小g:函数(其实就是矩阵,wb)RhRvp0s0经过softmax归一化后得到在词表上的概率分布,p0Rvv为词表的大小i0p0中最大概率词的索引,int\begin{array}{l} h_{0}=L S T M\left(e, w_{\text {sos }}\right) \\\\ s_{0}=g\left(h_{0}\right) \\\\ p_{0}=\operatorname{softmax}\left(s_{0}\right) \\\\ i_{0}=\operatorname{argmax}\left(p_{0}\right) \\\\ \\\\ e:Encoder输出的句子向量 \\\\ w_{sos}:特殊词,代表起时位置,作为当前时间步骤的输入\\\\ h_0:当前时间步骤的隐状态。h_0\subset \mathbb{R}^{h},h为隐层的维度\\\\ s_0:词表中,每个词的得分。s_0\subset \mathbb{R}^{v},v为词表的大小\\\\ g:函数(其实就是矩阵,w 和 b),\mathbb{R}^{h} \mapsto \mathbb{R}^{v}\\\\ p_0:s_0经过softmax归一化后得到在词表上的概率分布,p_0 \subset \mathbb{R}^{v},v为词表的大小\\\\ i_0:p_0中最大概率词的索引,int值\\\\ \end{array}

(2)当时间步等于1时:

h1=LSTM(h0,wi0)s1=g(h1)p1=softmax(s1)i1=argmax(p1)\begin{array}{l} h_{1}=L S T M\left(h_{0}, w_{i_{0}}\right) \\\\ s_{1}=g\left(h_{1}\right) \\\\ p_{1}=\operatorname{softmax}\left(s_{1}\right) \\\\ i_{1}=\operatorname{argmax}\left(p_{1}\right) \\\\ \end{array}

​ 与时间步等于0不同的是,LSTM的输入从 ee 变成上一个时间步的隐状态 h0h_0 ,即 eh0e\rightarrow h_{0}

词也变成从 wsosw_{sos} 变成了上一个时间步预测的词 wi0w_{i0} ,wsoswi0w_{sos}\rightarrow w_{i_{0}}

(3)当时间步大于1时:

重复(2)一直到预测出了特殊字符 $ $ 后,停止。

上述过程其实等价于做了一个变换:P[yty1,,yt1,x0,,xn]P[yty1,...,yt1,e]\mathbb{P}\left[y_{t'} \mid y_{1}, \cdots, y_{t'-1}, x_{0}, \cdots, x_{n}\right] \mapsto \mathbb{P}\left[y_{t} \mid y_1,...,y_{t'-1}, e\right]

当然,有了解码器的隐变量 hh ,可以定义更为复杂的模型来计算 $ P(y_{t’}|y_1,y_2,…,y_{t’-1},e)$ ,例如:基于当前时间步的解码器隐变量 $ h_{t′} $ 、上⼀时间步的输出 $h_{t’-1} $ 及背景变量 $ e $ 来计算当前时间步输出 $y_{t′} $ 的概率分布。

4. seq2seq模型的训练

依据上述“编码器-解码器”的模型解释,seq2seq的训练在本质上是找 $ LSTM和g的最佳参数 $ ,该参数使得样本输出序列 [y1,...,yT][y_1,...,y_{T'}] 出现的联合概率(解码器每个时间步输出的条件概率之积)最大。

根据最⼤似然估计,可以最⼤化输出序列基于输⼊序列的条件概率:

P(y1,...,yt1x1,...,xT)=t=1TP(yty1,...,yt1,x1,...,xT)=t=1TP(yty1,...,yt1,e)P(y_1,...,y_{t'-1}|x_1,...,x_T) =\prod \limits_{t'=1}^{T'}P(y_{t'}|y_1,...,y_{t'-1},x_1,...,x_T)\\\\ =\prod \limits_{t'=1}^{T'}P(y_{t'}|y_1,...,y_{t'-1},e)

目标函数为:

logP(y1,...,yt1x1,...,xT)=t=1TlogP(yty1,...,yt1,e)-log{P(y_1,...,y_{t'-1}|x_1,...,x_T)}=-\sum\limits_{t'=1}^{T'}log{P(y_{t'}|y_1,...,y_{t'-1},e)}

在模型训练中,所有输出序列损失的均值通常作为需要最小化的损失函数。在上图所描述的模型预测中,需要将解码器在上⼀个时间步的输出作为当前时间步的输⼊。与此不同,在训练中也可以将标签序列(训练集的真实输出序列)在上⼀个时间步的标签作为解码器在当前时间步的输⼊。这叫作强制教学(teacher forcing)。

5. seq2seq模型的预测

以上介绍了如何训练输⼊和输出均为不定⻓序列的编码器—解码器。本节介绍如何使⽤“编码器—解码器”来预测不定⻓的序列。

在准备训练数据集时,通常会在样本的输⼊序列和输出序列后面分别附上⼀个特殊符号“<eos>”表⽰序列的终⽌。在接下来的讨论中也将沿⽤上⼀节的全部数学符号。为了便于讨论,假设解码器的输出是⼀段⽂本序列。设输出⽂本词典 YY(包含特殊符号“<eos>”)的⼤小为 Y|Y| ,输出序列的最⼤⻓度为 TT′ 。所有可能的输出序列⼀共有 $ O(|Y|^{T’})$ 种。这些输出序列中所有特殊符号“<eos>”后⾯的⼦序列将被舍弃。

在本质上,seq2seq模型的预测,是在所有可能的序列组合中,寻找各时间步条件概率 $ P_{t’} $ 乘积(联合概率)最大的那个序列组合(即在所有可能的输出序列中,使下式最大)。

t=1TP(yty1,,yt1,e)\prod_{t^{\prime}=1}^{T^{\prime}} P\left(y_{t^{\prime}} \mid y_{1}, \ldots, y_{t^{\prime}-1}, e\right)

而满足该条件的输出序列被称为最优输出序列。

5.1 贪婪搜索

贪婪搜索(greedy search)。对于输出序列任⼀时间步 tt′ ,从 Y|Y| 个词中搜索出条件概率最⼤的词:

yt=argmaxyYP(yy1,,yt1,e)y_{t^{\prime}}=\operatorname{argmax}_{y \in Y} P\left(y \mid y_{1}, \ldots, y_{t^{\prime}-1},e\right)

作为输出。⼀旦搜索出“<eos>”符号,或者输出序列⻓度已经达到了最⼤⻓度T′,便完成输出。

贪婪搜索在每一步都在寻找概率最大的输出,但整个输出序列的联合概率并不一定是最大的,即贪婪搜索存在的主要问题是不能保证得到最优输出序列。

下⾯来看⼀个例⼦:

假设输出词典⾥⾯有 A”“B”“C“A”“B”“C”<eos>“<eos>” 这4个词。下图中每个时间步下的4个数字分别代表了该时间步⽣成 A”“B”“C“A”“B”“C” 和“<eos>”这4个词的条件概率。在每个时间步,贪婪搜索选取条件概率最⼤的词。因此,下图中将⽣成输出序列 A”“B”“C”“<eos>“A”“B”“C” “<eos>” 。该输出序列的条件概率是 0.5×0.4×0.4×0.6=0.0480.5 × 0.4 × 0.4 × 0.6 = 0.048

接下来,观察下面演⽰的例⼦:

与上图中不同,在时间步2中选取了条件概率第⼆⼤的词 C“C” 。由于时间步3所基于的时间步1和2的输出⼦序列由上图中的 A”“B“A”“B” 变为了下图中的 A”“C“A”“C” ,下图中时间步3⽣成各个词的条件概率发⽣了变化。选取条件概率最⼤的词 B“B” 。此时时间步4所基于的前3个时间步的输出⼦序列为 A”“C”“B“A”“C”“B” ,与上图中的 A”“B”“C“A”“B”“C” 不同。因此,下图中时间步4⽣成各个词的条件概率也与上图中的不同。此时的输出序列 A”“C”“B”“<eos>“A”“C”“B” “<eos>” 的条件概率是 0.5×0.3×0.6×0.6=0.0540.5 × 0.3 × 0.6 × 0.6 = 0.054 ,⼤于贪婪搜索得到的输出序列的条件概率。

由此可见,贪婪搜索得到的输出序列 A”“B”“C”“<eos>“A”“B”“C”“<eos>”并⾮最优输出序列。

5.2 穷举搜索

如果⽬标是得到最优输出序列,可以考虑穷举搜索:穷举所有可能的输出序列,输出条件概率最⼤的序列。

虽然穷举搜索可以得到最优输出序列,但它的计算开销 $ O(|Y|^{T’})$ 很容易过⼤。例如,当 Y=10000|Y|=10000T=10T′ = 10 时,将评估 $ 10000^{10}=10^{40}$ 个序列:这⼏乎不可能完成。而贪婪搜索的计算开销是 $ O(|Y|*{T’})$ ,通常显著小于穷举搜索的计算开销。例如,当 Y=10000|Y| = 10000T=10T′ = 10 时,只需评估 1000010=10510000*10=10^5 个序列。

5.3 束搜索

束搜索(beam search)是对贪婪搜索的⼀个改进算法。它有⼀个束宽(beam size)超参数。将它设为 kk 。在时间步1时,选取当前时间步条件概率最⼤的 kk 个词,分别组成 kk 个候选输出序列的⾸词。在之后的每个时间步,基于上个时间步的 kk 个候选输出序列,从 kYk|Y| 个可能的输出序列中选取条件概率最⼤的 kk 个,作为该时间步的候选输出序列。最终,从各个时间步的候选输出序列中筛选出包含特殊符号 <eos>“<eos>” 的序列,并将它们中所有特殊符号 <eos>“<eos>” 后⾯的⼦序列舍弃,得到最终候选输出序列的集合。

束宽为2,输出序列最⼤⻓度为3。候选输出序列有 ACABCEABDCEDA、C、AB、CE、ABD、CED 。将根据这6个序列得出最终候选输出序列的集合。在最终候选输出序列的集合中,取以下分数最⾼的序列作为输出序列:

1LαlogP(y1,,yL)=1Lαt=1TlogP(yty1,,yt1,c)\frac{1}{L^{\alpha}} \log P\left(y_{1}, \ldots, y_{L}\right)=\frac{1}{L^{\alpha}} \sum_{t^{\prime}=1}^{T^{\prime}} \log P\left(y_{t^{\prime}} \mid y_{1}, \ldots, y_{t^{\prime}-1}, c\right)

其中 LL 为最终候选序列⻓度, αα ⼀般可选为 0.750.75 。分⺟上的 LαL^α 是为了惩罚较⻓序列在以上分数中较多的对数相加项。分析可知,束搜索的计算开销为 $ O(k*|Y|*{T’})$ 。这介于贪婪搜索和穷举搜索的计算开销之间。此外,贪婪搜索可看作是束宽为1的束搜索。束搜索通过灵活的束宽 kk 来权衡计算开销和搜索质量。

6. BLEU得分

评价机器翻译结果通常使⽤BLEU(双语评估替补)。对于模型预测序列中任意⼦序列,BLEU考察这个⼦序列是否出现在标签序列中。

具体来说,设词数为 nn 的⼦序列的精度为 pnp_n。它是预测序列与标签序列匹配词数为 nn 的⼦序列的数量与预测序列中词数为 nn 的⼦序列的数量之⽐。举个例⼦,假设标签序列为 ABCDEFA、B、C、D、E、F ,预测序列为 ABBCDA、B、B、C、D ,那么:

P1= 预测序列中的 1 元词组在标签序列是否存在的个数  预测序列 1 元词组的个数之和 P_1=\frac{\text { 预测序列中的 } 1 \text { 元词组在标签序列是否存在的个数 }}{\text { 预测序列 } 1 \text { 元词组的个数之和 }}

预测序列一元词组: A/B/C/DA/B/C/D ,都在标签序列里存在,所以 P1=4/5P_1=4/5,以此类推,p2=3/4,p3=1/3,p4=0p_2 = 3/4, p_3 = 1/3, p_4 = 0。设 lenlabel,lenpredlen_{label},len_{pred} 分别为标签序列和预测序列的词数,那么,BLEU的定义为:

exp(min(0,1 len label lenpred ))n=1kpn12n\exp \left(\min \left(0,1-\frac{\text { len }_{\text {label }}}{l e n_{\text {pred }}}\right)\right) \prod_{n=1}^{k} p_{n}^{\frac{1}{2^{n}}}

其中 kk 是希望匹配的⼦序列的最⼤词数。可以看到当预测序列和标签序列完全⼀致时,BLEU为1。

因为匹配较⻓⼦序列⽐匹配较短⼦序列更难,BLEU对匹配较⻓⼦序列的精度赋予了更⼤权重。例如:

pnp_n 固定在 0.50.5 时,随着 nn 的增⼤ 0.5120.7,0.5140.84,0.5180.92,0.51160.96 0.5^{\frac{1}{2}} \approx 0.7,0.5^{\frac{1}{4}} \approx 0.84,0.5^{\frac{1}{8}} \approx 0.92,0.5^{\frac{1}{16}} \approx 0.96

另外,模型预测较短序列往往会得到较⾼ pnp_n 值。因此,上式中连乘项前⾯的系数是为了惩罚较短的输出而设的。举个例⼦,当 k=2k = 2 时,假设标签序列为 ABCDEFA、B、C、D、E、F ,而预测序列为 ABA、 B 。虽然 p1=p2=1p_1 = p_2 = 1 ,但惩罚系数 exp(16/2)0.14exp(1-6/2) ≈ 0.14 ,因此BLEU也接近0.14。

7. 代码实现

TensorFlow seq2seq的基本实现

8. 参考文献

[1] Cho et al., 2014 . Learning Phrase Representations using RNN Encoder-Decoder for Statistical Machine Translation
[2] Sutskever et al., 2014. Sequence to Sequence Learning with Neural Networks
[3] Bahdanau et al., 2014. Neural Machine Translation by Jointly Learning to Align and Translate
[4] Jean et. al., 2014. On Using Very Large Target Vocabulary for Neural Machine Translation
[5] Vinyals et. al., 2015. A Neural Conversational Model[J]. Computer Science
[6]动手学深度学习,https://zh.d2l.ai