深度学习进阶篇-预训练模型[3]:XLNet、BERT、GPT,ELMO的区别优缺点,模型框架、一些Trick、Transformer Encoder等原理详

1.XLNet:Generalized Autoregressive Pretraining for Language Understanding

1.1. 从AR和AE模型到XLNet模型

自回归模型(Autoregressive Model, AR),通过估计一串文本序列的生成概率分布进行建模。一般而言,AR模型通过要么从前到后计算文本序列概率,要么从后向前计算文本序列概率,但不论哪种方式的建模,都是单向的。即在预测一个单词的时候无法同时看到该单词位置两边的信息。假设给定的文本序列$x=(x_1, x_2, ..., x_n)$,其从左到右的序列生成概率为:

$$p(x)=\prod_{t=1}^n p(x_t|x_{<t})$$

自编码模型(Autoencoding Model, AE), 通过从破坏的输入文本序列中重建原始数据进行建模。例如BERT通过预测【mask】位置的词重建原始序列。它的优点在于在预测单词的时候能够同时捕获该单词位置前后双向的信息;它的缺点是预训练过程中采用了mask单词的策略,然而微调阶段并没有,因此导致了预训练阶段和微调阶段的的GAP,另外在训练过程中,对不同mask单词的预测是相互独立的。假设序列中被mask的词为$w\in W_m$,未被mask的词为$w\in W_n$,则其相应的计算概率为:

$$p(x) = \prod_{w\in Wm} p(w|W_n)$$

这里对传统的AR和AE模型简单总结一下,AR模型是生成式模型,是单向的;AE模型是判别式模型,是双向的。鉴于传统的AR模型和AE模型自身的优点和缺点,XLNet期望能够融合两者的优点同时又避免两者的缺点,这就是XLNet的设计思路。

整体上XLNet是基于AR模型的建模思路设计的,同时避免了只能单向建模的缺点,因此它是一种能看得见双向信息的广义AR模型。作为一个AR模型,XLNet并没有采用预测mask单词的方式进行建模,因此它不存在上述预训练-微调的GAP,更不存在预测mask的独立性假设。

1.2. Permutation Language Model

Permuatation Language Mode (下文将简称为PLM) 是XLNet的核心建模思路,在正式介绍之前,我们再来回顾一下AR模型的建模策略,给定一串文本序列$\text{x}=[x_1,x_2,...,x_n]$,其中每个$x_i$表示一个token,AR模型的通过最大化下边这个似然函数进行建模:

$$\begin{split} \begin{align} \mathop{max}\limits_{\theta} \quad log \; P_{\theta}(\text{x}) &= \sum_{t=1}^{n}log\;p_{\theta}(x_t|x_{<t}) \\&=\sum_{t=1}^{n}log\;\frac{exp(h_{\theta}(x_{1:t-1})^{T}e(x_t))}{\sum_{x^{'}}exp(h_{\theta}(x_{1:t-1})^{T}e(x^{'}))} \end{align} \end{split}$$

这里,$\text{x}_{<t}$表示在$t$位置前边的token序列,$h_{\theta}(\text{x}_{1:t-1})$表示数据由模型产生的上下文向量,$e(x_t)$表示token $x_t$的embedding。

这种建模方式是单向的,为了在预测某个位置单词的时候,能够让模型看见双向的信息,XLNet采用了全排列的思路,允许模型在不同文本序列排列上进行建模,但模型的参数在不同序列上是共享的,相当于是模型能够看见预测位置单词左右两侧的信息。

举个例子,假设当前有文本序列$\text{x}=[x_1,x_2,x_3]$,这串序列中共有3个token,这三个token共计有6种排列组合方式,其相关的索引序列为:

  • $\text{z}_1 = (1,2,3)$
  • $\text{z}_2=(1,3,2)$
  • $\text{z}_3=(2,1,3)$
  • $\text{z}_4=(2,3,1)$
  • $\text{z}_5=(3,1,2)$
  • $\text{z}_6=(3,2,1)$

采用索引序列$\text{z}_1$的文本序列为$\text{x}=[x_1, x_2, x_3]$,采用索引序列$\text{z}_2$的文本序列为$\text{x}=[x_1, x_3, x_2]$,如果模型在训练过程中能同时看到这样的两个排列,那么在预测$x_2$的时候将可以看到$x_1$,又可以看到$x_3$,因为训练过程中模型的参数是共享的,因此相当于模型能够看到$x_2$前后双向的信息。

下面正式归纳一下XLNet的目标函数,假设给定一串序列$\text{x}=[x_1,x_2,x_3,...,x_n]$,它将有$n!$个不同的排列组合$\mathbb{Z}=[\text{z}_1,\text{z}_2,...,\text{z}_{n!}]$,令$\text{z}\in \mathbb{Z}$表示某一排列方式,$z_{t}$表示为$\text{z}$这个排列的第$t$个位置,$\text{z}_{<t}$表示$t$这个位置前边的$t-1$个位置编号,$\text{x}_{\text{z}}$表示将序列$\text{x}$按照索引$\text{z}$进行排序。$\text{x}_{\text{z}<t}$表示将原始$\text{x}$按照$\text{z}$进行排列后,$\text{x}_{\text{z}}$前$t-1$个位置的token。另外假设排列$\text{z}$出现的概率为$p(\text{z})$,则XLNet的正式目标函数依据极大似然估计为:

$$\begin{split} \begin{align} \mathop{max}_{\theta} \quad L &=\mathbb{E}_{\text{z}∼\mathbb{Z}} \left[ \sum_{t=1}^n log\;p_{\theta}(x_{z_t}|\text{x}_{\text{z}_{<t}}) \right] \\ & = \sum_{_{\text{z}∼\mathbb{Z}}} p(\text{z}) \left[ \sum_{t=1}^n log\;p_{\theta}(x_{z_t}|\text{x}_{\text{z}_{<t}}) \right] \end{align} \end{split}$$

但是一个长度为$n$的文本序列,其排列组合数为$n!$,数量实在庞大,不便训练,所以在实际训练过程中,XLNet是通过采样的方式逼近目标函数的期望,即:

$$\begin{split} \begin{align} \mathop{max}_{\theta} \quad L &=\mathbb{E}_{\text{z}∼\mathbb{Z}} \left[ \sum_{t=1}^n log\;p_{\theta}(x_{z_t}|\text{x}_{z<t}) \right] \\ &\approx \frac{1}{m}\sum_{i=1}^{m}\sum_{t=1}^n log\;p_{\theta}(x_{z_{it}}|\text{x}_{\text{z}_i<t}) \end{align} \end{split}$$

其中,$z_{it}$表示第$i$个排列的第$t$个位置,$\text{x}_{\text{z}_i<t}$表示按照$\text{z}_i$排列进行重塑后的前$t-1$个token。每次采样一个排列顺序$\text{z}$进行重塑原始序列$\text{x}_{\text{z}}$,然后将$\text{x}_\text{z}$进行分解组合计算:$\sum_{t=1}^n log\;p_{\theta}(x_{z_t}|\text{x}_{\text{z}<t})$ 。这里需要注意的是,XLNet只是调整了联合概率$p(\text{x})$的分解顺序,但是原始token之间的顺序是不变的,即token之间还是使用原始顺序的position embedding,$p(\text{x})$的分解顺序的计算主要是通过transformer的mask机制实现的。这样的设定也保证了预训练阶段和微调阶段之间的顺序是一致的,均是正常的自然语序。

图1 不同排列计算第3个单词输出的示意图

图1中$mem^{(0)}$和$mem^{(1)}$代表前一个的segment的缓存信息,另外,输入的序列顺序都是固定的自然语序,position embedding还是按照正常的顺序确定的。只是不同的排列中,参与分解计算的内容不同。具体来讲,对第一个分解次序$3\rightarrow2\rightarrow4\rightarrow1$,因为$x_3$位于最前边,所以在这个分解次序中看不到其他的token内容,只能看到前一个segment的缓存信息;对第一个分解次序$2\rightarrow4\rightarrow3\rightarrow1$,$x_3$前边有$x_2$和$x_4$,所以在计算$x_3$位置输出的时候使用了$x_2$和$x_4$。

这个想法就是PLM的建模思路,看到这里,相信你已经很好地理解了。

1.3. Permutation Language Model如何建模

1.3.1 使用经典的transformer是否能建模PLM

上边看似找到了一个比较好想法去让AR模型在预测一个单词的时候同时能够看到前后双向的信息,但具体怎么来建模实现呢?使用原始的transformer技术直接实现可能会带来一些问题,具体来说,假设当前有两个排列$\text{z}^{1}$和$\text{z}^2$,他们之间具有如下的关系:

$$\text{z}_{<t}^{1} = \text{z}_{<t}^{2}=\text{z}_{<t} \qquad but \qquad z_t^1=i \neq j= z_t^2$$

这种情况下,使用经典transformer的方式去预测这两个排列$z_t$位置的输出,将会有:

$$\underbrace{p_{\theta}(X_{i}=x|\text{x}_{\text{z}_{<t}}) }_{z_t^1=i, \; \text{z}_{<t}^1=\text{z}_{<t}} = \underbrace{p_{\theta}(X_{j}=x|\text{x}_{\text{z}_{<t}}) }_{z_t^2=i, \; \text{z}_{<t}^2=\text{z}_{<t}} = \frac{exp\,(e(x)^T h(\text{x}_{\text{z}_{<t}}))}{\sum_{x^{'}}exp\,(e(x^{'})^T h(\text{x}_{\text{z}_{<t}}))}$$

显然在这种情况下,预测第$i$个位置的单词和预测第$j$个位置的单词的概率分布是相同的,这肯定是不对的,因此使用经典的transformer是无法去做Permutation Language Model建模的。

为了解决这个问题,XLNet在预测目标$z_t$位置的token时,向其引入了位置信息$z_t$,重新定义的预测token概率分布的计算方式为:

$$p_{\theta}(x_{z_t}|\text{x}_{\text{z}_{<t}})=\frac{exp\,(e(x)^T g_{\theta}(\text{x}_{\text{z}_{<t}},z_{t}))}{\sum_{x^{'}}exp\,(e(x^{'})^T g_{\theta}(\text{x}_{\text{z}_{<t}},z_{t})))}$$

从公式中可以看到,其在预测$z_t$位置token的时候,引入了位置信息$z_t$。这样就能解决上述的问题,即经过这个变换后上式将变为:

$$\underbrace{p_{\theta}(X_{i}=x|\text{x}_{\text{z}_{<t}}) }_{z_t^1=i, \; \text{z}_{<t}^1=\text{z}_{<t}} =\frac{exp\,(e(x)^T g_{\theta}(\text{x}_{\text{z}_{<t}},i))}{\sum_{x^{'}}exp\,(e(x^{'})^T g_{\theta}(\text{x}_{\text{z}_{<t}},i)))} \neq \underbrace{p_{\theta}(X_{j}=x|\text{x}_{\text{z}_{<t}}) }_{z_t^2=i, \; \text{z}_{<t}^2=\text{z}_{<t}}= \frac{exp\,(e(x)^T g_{\theta}(\text{x}_{\text{z}_{<t}},j))}{\sum_{x^{'}}exp\,(e(x^{'})^T g_{\theta}(x_{\text{z}_{<t}},j)))}$$

1.3.2 使用Two-Stream Self-Attention建模PLM

从上边讨论的这些可以看到,当预测$z_t$位置的token时,最多只能使用位置信息$z_t$,而不能使用该$z_t$对应的内容$x_{z_t}$,否则,就相当于使用$x_{z_t}$来预测$x_{z_t}$自己,这没有什么意义;当预测$j>t$后的token $x_{z_j}$时,不仅需要位置信息$z_t$,同时还需要该位置对应的内容$x_{z_t}$。

然而,经典的transformer中是将两者信息在输入层相加融合之后进行后续计算的,因此XLNet提出了一种双流自注意力机制:content-streamquery stream,下面我们将来具体探讨一下它们。

content-stream 提供了内容方面的表达 content representation $h_{\theta}(\text{x}_{\text{z}_{\leq t}} )$,简记为$h_{z_t}$,它等同经典的transformer 的状态向量,这个向量中既包含了位置信息$z_t$,又包含了内容信息$x_{z_t}$。

query-stream 提供了查询方面的表达 query representation $g_{\theta}(\text{x}_{\text{z}_{<t}}, z_t)$,简记为$g_{z_t}$,它仅仅包含了$x_{z<t}$的内容信息和$z_t$的位置信息,并不包含$x_{z_t}$的内容。

图2 双流机制计算图

图2展示了分解顺序为$3 \rightarrow 2 \rightarrow 4 \rightarrow 1$的two-stream计算过程,我们通过这张图来具体聊聊如何去定义$g_{\theta}(\text{x}_{\text{z}_{<t}},z_t)$。

图2a展示了content-stream的自注意力计算,其中$h_i^{(0)}$是由token的embedding进行初始化,可以看到它的计算和经典的transormer是一致的,因为在分解顺序中1位于最后,因此它能看见前边所有的token内容,最终计算得出的$h_1^{(1)}$同时包含了第$1$个位置的内容信息。

图2b展示了query-stream的自注意力计算,其中$g_i^{(0)}$由可学习的参数进行初始化,因为它能看见token 3,2,4的内容信息,所以这里用的是内容信息$h_3^{(0)},h_2^{(0)},h_4^{(0)}$,同时对于第1个位置,只能使用位置信息,而不能使用内容信息,所以这里使用的是$g_1^{(0)}$,它并不包含第1个位置的内容信息。 这样就能比较好地建模$g_{\theta}(\text{x}_{\text{z}_{<t}},z_t)$。

图2c展示了整体的计算流程,最后一层输出的query向量就是我们想要的$g_{\theta}(\text{x}_{\text{z}_{<t}},z_t)$。右边的两个矩阵分别展示了content-streamquery-stream的mask矩阵内容,就是使用这样的mask矩阵来计算序列分解式的。 关于这两个流的Self-Attention计算公式如下:

$$\begin{split} \begin{align} g_{z_t}^{(m)} & \leftarrow \text{Attention}(Q=g_{z_t}^{(m-1)},\, KV=h_{\text{z}_{<t}}^{(m-1)};\theta), \qquad \text{(query stream: use $z_t$ but cannot see $x_{z_t}$)} \\ h_{z_t}^{(m)} & \leftarrow \text{Attention}(Q=h_{z_t}^{(m-1)},\, KV=h_{\text{z}_{\leq t}}^{(m-1)};\theta), \qquad \text{(content stream: use both $z_t$ and $x_{z_t}$)} \end{align} \end{split}$$

以上是XLNet在预训练阶段的计算过程,这里需要注意的是,在微调阶段,XLNet仅仅使用content respresentation进行fine-tune下游任务。

1.3.3 引入Transformer-XL的想法

由于XLNet本身是个AR模型,它可以完美融入Transformer-XL的思想:相对位置编码segment循环机制。这两者的原理部分感兴趣的同学可以去阅读Transformer-XL内容,本文重点讨论一下segment循环机制XLNet的融入过程。

顾名思义,segment循环机制是指长序列切分成$n$个segment (文本片段),然后将每个segment依次传入模型之中,同时传入到模型中,同时传入到模型中还有上一个segment的产生的输出,这个操作有点像RNN,接收上一步的输出和当前步骤的输入,然后根据两者计算产生当前步骤的输出,只不过RNN的循环单位是单词,XLNet的循环单位是segment

给定一个长序列$\text{s}$,上一个segment为$\tilde{\text{x}}=s_{1:n}$,其对应的排列用$\tilde{\text{z}}$表示;当前的segment为$\text{x}=s_{n+1:2n}$,其对应的排列用$\text{z}$表示。基于排列$\tilde{\text{z}}$处理第1个segment,并将其输出进行缓存,第$m$层的输出用$\tilde{h^{(m)}}$表示。则第2个segment的计算可以按照如下方式进行:

$$\begin{split} \begin{align} g_{z_t}^{(m)} & \leftarrow \text{Attention}(Q=g_{z_t}^{(m-1)},\, KV=[\tilde{h}^{(m-1)},h_{\text{z}_{<t}}^{(m-1)}];\, \theta), \qquad \text{(query stream: use $z_t$ but cannot see $x_{z_t}$)} \\ h_{z_t}^{(m)} & \leftarrow \text{Attention}(Q=h_{z_t}^{(m-1)},\, KV=[\tilde{h}^{(m-1)},h_{\text{z}_{\leq t}}^{(m-1)}];\, \theta), \qquad \text{(content stream: use both $z_t$ and $x_{z_t}$)} \end{align} \end{split}$$

即将前一个segment的输出和当前位置$z_t$能看到的内容进行拼接,然后进行Self-Attention融合计算。

这里需要注意的是,由于序列中的position embedding 仅仅依赖于原始序列(输入序列)的位置,而不是排列的顺序,所以一旦前一个segment的输出$\tilde{h}^{(m)}$确定,上述的Attention计算和前一个segment的分解顺序无关。这允许我们去缓存或者复用前一个segment的输出,而不用去管前一个segment的分解顺序。

1.3.4 关于XLNet的一些Trick

Partial Prediction

最开始的时候有提到,AR模型通过估计一串文本序列的生成概率分布进行建模:$\sum_{t=1}^n log\;p_{\theta}(x_{z_t}|\text{x}_{\text{z}<t})$ 。PLM虽然解决了AR模型建模过程中的双向问题,但是由于通过这种排列组合的形式训练,导致XLNet收敛会比较慢。

因此XLNet在训练过程中,只选择预测序列最后面的部分位置的token,这里涉及到一个切分点位置$c$,它将指示不预测在$c$前边的位置$\text{z}_{\leq c}$,只预测$c$后边的位置${\text{z}_{>c}}$。XLNet中切分点$c$ 的选择由超参数$K$来确定,$K \approx \frac{n}{n-c}$,其中$n$为序列长度。$K$越大,则需要预测的token数量越少。

这就是Partial Prediction 部分预测,对于切分点$c$之前的token无需计算query representation,这会大大节省内存,加快模型训练速度。加入切分点后,XLNet的目标函数将变为:

$$\begin{align} \mathop{max}_{\theta} \quad \mathbb{E}_{\text{z}∼\mathbb{Z}} log \, p_{\theta}(\text{x}_{z_{>c}}|\text{x}_{\leq c}) = \mathop{max}_{\theta}\quad \mathbb{E}_{\text{z}∼\mathbb{Z}} \left[ \sum_{t=c+1}^n log\;p_{\theta}(x_{z_t}|\text{x}_{\text{z}_{<t}}) \right] \end{align}$$

Multiple Segment Input

许多下游任务存在多段输入的情况,比如QA任务中包含query( 简记为A )和answer (简记为B)两个部分,数据的输入形式同BERT一致:$\text{[CLS, A, SEP, B, SEP]}$。

但是在segment循环的时候,每个部分仅仅使用对应上下文的状态缓存。

Relative Segment Encoding

Relative Segment Encoding (相对段编码) , 这里的Segment不是上边将的将序列划分为固定的Segment,而是指输入数据的不同部分,例如$\text{[CLS, A, SEP, B, SEP]}$,$\text{A}$和$\text{B}$分别属于不同的Segment。

BERT直接使用了绝对编码,直接给$\text{A}$和$\text{B}$中的token依次设置了0和1,用来指示整个序列中$\text{A}$和$\text{B}$是不同的segment,即是不同的文本段,例如一个是query,另一个是answer。

XLNet与BERT不同,它使用了相对编码,给定序列中的两个位置$i$和$j$,判断这两个位置对应的token是否在同一个segment里面,如果两者在同一个segment里面,$s_{ij}=s_+$,否则$s_{ij}=s_-$。 当预测第$i$个位置token的时候,需要计算用$i$位置的向量向另一位置$j$做attention获取分数,其按照如下公式计算:

$$\alpha_{i,j} = (q_i+b)^T s_{ij}$$

其中$q_i$为第$i$个位置的查询向量,$b$是一个可学习的参数。最终$a_{i,j}$将被加到正常Self-Attention的注意力分数上。

使用相对段编码有这样的优势:

  • 模型的泛化效果会更好;
  • 在微调任务上,它支持超过两个segment输入的下游任务(虽然预训练过程中使用了两个segment);

相关资料

  1. XLNet:Generalized Autoregressive Pretraining for Language Understanding
  2. XLNet Github

2.BERT

2.1 BERT介绍

  • BERT(Bidirectional Encoder Representation from Transformers)是2018年10月由Google AI研究院提出的一种预训练模型,该模型在机器阅读理解顶级水平测试SQuAD1.1中表现出惊人的成绩: 全部两个衡量指标上全面超越人类,并且在11种不同NLP测试中创出SOTA表现,包括将GLUE基准推高至80.4% (绝对改进7.6%),MultiNLI准确度达到86.7% (绝对改进5.6%),成为NLP发展史上的里程碑式的模型成就。
  • BERT的网络架构使用的是《Attention is all you need》中提出的多层Transformer结构,如 图1 所示。其最大的特点是抛弃了传统的RNN和CNN,通过Attention机制将任意位置的两个单词的距离转换成1,有效的解决了NLP中棘手的长期依赖问题。Transformer的结构在NLP领域中已经得到了广泛应用。

2.2 BERT框架

BERT整体框架包含pre-train和fine-tune两个阶段。pre-train阶段模型是在无标注的标签数据上进行训练,fine-tune阶段,BERT模型首先是被pre-train模型参数初始化,然后所有的参数会用下游的有标注的数据进行训练。

图1 BERT结构

BERT是用了Transformer的encoder侧的网络,encoder中的Self-attention机制在编码一个token的时候同时利用了其上下文的token,其中‘同时利用上下文’即为双向的体现,而并非想Bi-LSTM那样把句子倒序输入一遍。

在它之前是GPT,GPT使用的是Transformer的decoder侧的网络,GPT是一个单向语言模型的预训练过程,更适用于文本生成,通过前文去预测当前的字。

2.2.1Embedding

Embedding由三种Embedding求和而成:

  • Token Embeddings是词向量,第一个单词是CLS标志,可以用于之后的分类任务
  • Segment Embeddings用来区别两种句子,因为预训练不光做LM还要做以两个句子为输入的分类任务
  • Position Embeddings和之前文章中的Transformer不一样,不是三角函数而是学习出来的

其中[CLS]表示该特征用于分类模型,对非分类模型,该符号可以省去。[SEP]表示分句符号,用于断开输入语料中的两个句子。

BERT在第一句前会加一个[CLS]标志,最后一层该位对应向量可以作为整句话的语义表示,从而用于下游的分类任务等。因为与文本中已有的其它词相比,这个无明显语义信息的符号会更“公平”地融合文本中各个词的语义信息,从而更好的表示整句话的语义。 具体来说,self-attention是用文本中的其它词来增强目标词的语义表示,但是目标词本身的语义还是会占主要部分的,因此,经过BERT的12层(BERT-base为例),每次词的embedding融合了所有词的信息,可以去更好的表示自己的语义。而[CLS]位本身没有语义,经过12层,句子级别的向量,相比其他正常词,可以更好的表征句子语义。

2.2.2Transformer Encoder

BERT是用了Transformer的encoder侧的网络,如上图的transformer的Encoder部分,关于transformer的encoder的详细介绍可以参考链接:https://paddlepedia.readthedocs.io/en/latest/tutorials/pretra...

在Transformer中,模型的输入会被转换成512维的向量,然后分为8个head,每个head的维度是64维,但是BERT的维度是768维度,然后分成12个head,每个head的维度是64维,这是一个微小的差别。Transformer中position Embedding是用的三角函数,BERT中也有一个Postion Embedding是随机初始化,然后从数据中学出来的。

BERT模型分为24层和12层两种,其差别就是使用transformer encoder的层数的差异,BERT-base使用的是12层的Transformer Encoder结构,BERT-Large使用的是24层的Transformer Encoder结构。

2.2.3 BERT可视化

如上图将注意力看做不同的连线,它们用来连接被更新的位置(左半边)与被注意的位置(右半边)。不同的颜色分别代表相应的注意头,而线条颜色的深浅代表被注意的强度。

2.2.4 注意力六种模式

为了方便演示,这里采用以下例句:

句子A:I went to the store.句子B:At the store, I bought fresh strawberries.

BERT 用 WordPiece工具来进行分词,并插入特殊的分离符([CLS],用来分隔样本)和分隔符([SEP],用来分隔样本内的不同句子)。

因此实际输入序列为: [CLS] i went to the store . [SEP] at the store , i bought fresh straw ##berries . [SEP]

模式1:注意下一个词

在这种模式中,每个位置主要注意序列中的下一个词(token)。下面将看到第2层0号头的一个例子。(所选头部由顶部颜色条中突出的显示色块表示。)

模式1:注意下一个词。

左:所有词的注意力。 右:所选词的注意力权重(“i”)

左边显示了所有词的注意力,而右侧图显示一个特定词(“i”)的注意力。在这个例子中,“i”几乎所有的注意力都集中在“went”上,即序列中的下一个词。

在左侧,可以看到 [SEP]符号不符合这种注意力模式,因为[SEP]的大多数注意力被引导到了[CLS]上,而不是下一个词。因此,这种模式似乎主要在每个句子内部出现。

该模式与后向RNN 有关,其状态的更新是从右向左依次进行。模式1出现在模型的多个层中,在某种意义上模拟了RNN 的循环更新。

模式2:注意前一个词

在这种模式中,大部分注意力都集中在句子的前一个词上。例如,下图中“went”的大部分注意力都指向前一个词“i”。

这个模式不像上一个那样显著。有一些注意力也分散到其他词上了,特别是[SEP]符号。与模式1一样,这与RNN 有些类似,只是这种情况下更像前向RNN。

模式2:注意前一个词。

左:所有词的注意力。 右:所选词的注意力权重(“went”)

模式3:注意相同或相关的单词

这种模式注意相同或相关的单词,包括其本身。在下面的例子中,第一次出现的“store”的大部分注意力都是针对自身和第二次出现的“store”。这种模式并不像其他一些模式那样显著,注意力会分散在许多不同的词上。

模式3:注意相同/相关的词。

左:所有词的注意力。 右:所选词的注意权重(“store”)

模式4:注意“其他”句子中相同或相关词

这种模式注意另一个句子中相同或相关的单词。例如,第二句中“store”的大部分注意力都指向第一句中的“store”。可以想象这对于下句预测任务(BERT预训练任务的一部分)特别有用,因为它有助于识别句子之间的关系。

模式4:注意其他句子中相同/相关的单词。

左:所有词的注意力。 右:所选词的注意权重(“store”)

模式5:注意能预测该词的其他单词

这种模式似乎是更注意能预测该词的词,而不包括该词本身。在下面的例子中,“straw”的大部分注意力都集中在“##berries”上(strawberries 草莓,因为WordPiece分开了),而“##berries”的大部分注意力也都集中在“straw”上。

模式5:注意能预测该单词的其他单词。

左:所有词的注意力。 右:所选词的注意力(“## berries”)

这个模式并不像其他模式那样显著。例如,词语的大部分注意力都集中在定界符([CLS])上,而这是下面讨论的模式6的特征。

模式6:注意分隔符

在这种模式中,词语的大部分注意力都集中在分隔符[CLS]或 [SEP]上。在下面的示例中,大部分注意力都集中在两个 [SEP]符号上。这可能是模型将句子级状态传播到单个词语上的一种方式。

模式6:注意分隔符。 左:所有词的注意力。 右:所选词的注意权重(“store”)

2.3 BERT的预训练任务

BERT是一个多任务模型,它的预训练(Pre-training)任务是由两个自监督任务组成,即MLM和NSP,如 图2 所示。

图2 BERT 预训练过程示意图

2.3.1MLM

  • MLM是指在训练的时候随即从输入语料上mask掉一些单词,然后通过的上下文预测该单词,该任务非常像我们在中学时期经常做的完形填空。正如传统的语言模型算法和RNN匹配那样,MLM的这个性质和Transformer的结构是非常匹配的。在BERT的实验中,15%的WordPiece Token会被随机Mask掉。在训练模型时,一个句子会被多次喂到模型中用于参数学习,但是Google并没有在每次都mask掉这些单词,而是在确定要Mask掉的单词之后,做以下处理。

    • 80%的时候会直接替换为[Mask],将句子 “my dog is cute” 转换为句子 “my dog is [Mask]”。
    • 10%的时候将其替换为其它任意单词,将单词 “cute” 替换成另一个随机词,例如 “apple”。将句子 “my dog is cute” 转换为句子 “my dog is apple”。
    • 10%的时候会保留原始Token,例如保持句子为 “my dog is cute” 不变。

这么做的原因是如果句子中的某个Token 100%都会被mask掉,那么在fine-tuning的时候模型就会有一些没有见过的单词。加入随机Token的原因是因为Transformer要保持对每个输入token的分布式表征,否则模型就会记住这个[mask]是token ’cute‘。至于单词带来的负面影响,因为一个单词被随机替换掉的概率只有15%*10% =1.5%,这个负面影响其实是可以忽略不计的。 另外文章指出每次只预测15%的单词,因此模型收敛的比较慢。

优点

  • 1)被随机选择15%的词当中以10%的概率用任意词替换去预测正确的词,相当于文本纠错任务,为BERT模型赋予了一定的文本纠错能力;
  • 2)被随机选择15%的词当中以10%的概率保持不变,缓解了finetune时候与预训练时候输入不匹配的问题(预训练时候输入句子当中有mask,而finetune时候输入是完整无缺的句子,即为输入不匹配问题)。

缺点

  • 针对有两个及两个以上连续字组成的词,随机mask字割裂了连续字之间的相关性,使模型不太容易学习到词的语义信息。主要针对这一短板,因此google此后发表了BERT-WWM,国内的哈工大联合讯飞发表了中文版的BERT-WWM。

2.3.2NSP

  • Next Sentence Prediction(NSP)的任务是判断句子B是否是句子A的下文。如果是的话输出’IsNext‘,否则输出’NotNext‘。训练数据的生成方式是从平行语料中随机抽取的连续两句话,其中50%保留抽取的两句话,它们符合IsNext关系,另外50%的第二句话是随机从预料中提取的,它们的关系是NotNext的。这个关系保存在图4中的[CLS]符号中。

注意

  • 在此后的研究(论文《Crosslingual language model pretraining》等)中发现,NSP任务可能并不是必要的,消除NSP损失在下游任务的性能上能够与原始BERT持平或略有提高。这可能是由于Bert以单句子为单位输入,模型无法学习到词之间的远程依赖关系。针对这一点,后续的RoBERTa、ALBERT、spanBERT都移去了NSP任务。

BERT预训练模型最多只能输入512个词,这是因为在BERT中,Token,Position,Segment Embeddings 都是通过学习来得到的。在直接使用Google 的BERT预训练模型时,输入最多512个词(还要除掉[CLS]和[SEP]),最多两个句子合成一句。这之外的词和句子会没有对应的embedding。

如果有足够的硬件资源自己重新训练BERT,可以更改 BERT config,设置更大max_position_embeddings 和 type_vocab_size值去满足自己的需求。

2.4 BERT的微调

在海量的语料上训练完BERT之后,便可以将其应用到NLP的各个任务中了。 微调(Fine-Tuning)的任务包括:基于句子对的分类任务,基于单个句子的分类任务,问答任务,命名实体识别等。

  • 基于句子对的分类任务:

    • MNLI:给定一个前提 (Premise) ,根据这个前提去推断假设 (Hypothesis) 与前提的关系。该任务的关系分为三种,蕴含关系 (Entailment)、矛盾关系 (Contradiction) 以及中立关系 (Neutral)。所以这个问题本质上是一个分类问题,我们需要做的是去发掘前提和假设这两个句子对之间的交互信息。
    • QQP:基于Quora,判断 Quora 上的两个问题句是否表示的是一样的意思。
    • QNLI:用于判断文本是否包含问题的答案,类似于我们做阅读理解定位问题所在的段落。
    • STS-B:预测两个句子的相似性,包括5个级别。
    • MRPC:也是判断两个句子是否是等价的。
    • RTE:类似于MNLI,但是只是对蕴含关系的二分类判断,而且数据集更小。
    • SWAG:从四个句子中选择为可能为前句下文的那个。
  • 基于单个句子的分类任务

    • SST-2:电影评价的情感分析。
    • CoLA:句子语义判断,是否是可接受的(Acceptable)。
  • 问答任务

    • SQuAD v1.1:给定一个句子(通常是一个问题)和一段描述文本,输出这个问题的答案,类似于做阅读理解的简答题。
  • 命名实体识别

    • CoNLL-2003 NER:判断一个句子中的单词是不是Person,Organization,Location,Miscellaneous或者other(无命名实体)。

图3 BERT 用于不同的 NLP 任务

2.5 BERT,GPT,ELMO的区别

如上图所示,图中的Trm代表的是Transformer层,E代表的是Token Embedding,即每一个输入的单词映射成的向量,T代表的是模型输出的每个Token的特征向量表示。

BERT使用的是双向的Transformer,OpenAI GPT使用的是从左到右的Transformer。ELMo使用的是单独的从左到右和从右到左的LSTM拼接而成的特征。其中只有BERT在所有的层考虑了左右上下文。除此之外,BERT和OpenAI GPT是微调(fine-tuning)的方法,而ELMo是一个基于特征的方法。

2.5.1 BERT 比 ELMo 效果好的原因

从网络结构以及最后的实验效果来看,BERT 比 ELMo 效果好主要集中在以下几点原因:

  1. LSTM 抽取特征的能力远弱于 Transformer
  2. 拼接方式双向融合的特征融合能力偏弱
  3. BERT 的训练数据以及模型参数均多于 ELMo

2.5.2 优缺点

  • 优点

    • BERT 相较于原来的 RNN、LSTM 可以做到并发执行,同时提取词在句子中的关系特征,并且能在多个不同层次提取关系特征,进而更全面反映句子语义。
    • 相较于 word2vec,其又能根据句子上下文获取词义,从而避免歧义出现。
  • 缺点

    • 模型参数太多,而且模型太大,少量数据训练时,容易过拟合。
    • BERT的NSP任务效果不明显,MLM存在和下游任务mismathch的情况。
    • BERT对生成式任务和长序列建模支持不好。

汀丶
84 声望62 粉丝