学习时间:2022.04.22~2022.04.25
5. 基于深度学习的语言模型
5.1 从NNLM到词嵌入
从语言模型到词嵌入Word Embedding。一些基于CNN或RNN的结构本节并未提及,可见:深度学习NLP的各类模型及应用总结。
5.1.1 神经网络语言模型 NNLM
Neural Network Language Model,NNLM,生于2003,火于2013。
NNLM依然是一个概率语言模型,它通过神经网络来计算概率语言模型中每个参数。与N-gram模型一样是求 P ( X n ∣ X 1 n − 1 ) P(X_n|X_1^{n-1}) P(Xn∣X1n−1) ,NNML通过输入一段文本,预测下一个单词的方式来建立模型。采用的是MLP+tanh+softmax模型,使用交叉熵作为损失函数,反向传播算法进行训练。
模型解释:
- 输入层:将context(w)每个词映射成一个长度为m的词向量(长度训练者指定,一般使用tf.random_normal随机初始化生成nxm的矩阵),词向量在开始是随机的,也是超参数参与网络训练;
- 使用随机初始化的方法建立一个 ∣ m ∣ × N |m|×N ∣m∣×N个词大小的查找表(lookup table);
- context(w):可以称之为上下文窗口长度,类似N-gram取多少个词作为添加。例如上下文窗口长度为 c = 3 c=3 c=3,则循环取四个词,前三个为特征值,最后一个为目标值;
- 投影层:将所有的上下文此项来给你拼接成一个长向量,作为目标w的特征向量。长度为 m ( n − 1 ) m(n-1) m(n−1);
- 隐藏层:拼接后的向量会经过一个规模为h的隐藏层,论文中使用tanh激活函数;
- 输出层:最后输出会通过softmax输出所有n个词的大小概率分布。
缺陷:
- 计算复杂度过大,参数较多(word2vec是一种改进的办法);仍然限制于N的大小,最好的就是不限制N的数量,且跟前后任意词汇都可能产生依赖,那么基于循环神经网络的RNNLM就是解决办法,因为RNN天生的结构就是针对序列前后任意长度的依赖的。
- 模型优化不够,输出的类别较多导致训练过慢、自回归语言模型,无法获取下文信息、早期方案,应用较少,一般python工具不会集成;
5.1.2 基于循环神经网络的语言模型 RNNLM
Recurrent Neural Network Language Model,RNNLM。
引入了RNN,形成了一个根据上下文,预测下一个词语概率的模型,所以以后想要预测某个句子S的概率,就可以按照模型计算多个时刻的P,相乘,即可得到句子的概率。天生的结构能处理任意长度的序列的依赖,可以不用人为限制模型输入长度。缺陷是计算和训练时间长。
5.1.3 Word2Vec
Word2Vec是2013年Google发布的工具,也可以说是一个产生词向量的一群模型组合。该工具主要包含两个词向量的生成模型,跳字模型(skip-gram)和连续词袋模型(continuous bag of words,CBOW),以及两种高效训练(优化加速)的方法:负采样(negative sampling)和层序softmax(hierarchical softmax)。Word2vec 本质上是一种降维操作——把词语从one-hot形式的表示降维到Word2vec表示的词嵌入。
由于 Word2vec 会考虑上下文,跟之前的Embedding方法相比,效果要更好;比之前的Embedding方法维度更少,所以速度更快;通用性很强,可以用在各种NLP任务中。但由于词和向量是一对一的关系,所以多义词的问题无法解决;word2vec是一种静态的方式,虽然通用性强,但是无法针对特定任务做动态优化。
需要说明的是:Word2vec 是上一代的产物(18年之前),18年之后想要得到最好的效果,已经不使用Word Embedding的方法了,所以也不会用到 Word2vec。
1. 词向量的生成模型
即Word2Vec如何计算得到词向量,一次计算只能采用一种方式。
个人理解:两种方式本质上都是一样的,网络结构、权重 W W W的大小和位置都一样。
(1)连续词袋模型CBOW
核心思想是从一个句子里面把一个词抠掉,在上下文已知的情况下,预测被抠掉的这个词出现的概率。
eg:把 {“The”,“cat”,“over”,“the”,“puddle”} 作为上下⽂,希望从这些词中预测或者⽣成中心词“jumped”。
CBOW模型的网络图如下,是一个简单的3层神经网络(输入层到Projection层没有非线性变换):
- 输入层:输入上下文单词的onehot(不包括单词本身),假设单词向量空间维度为 V × 1 V×1 V×1,上下文单词个数为 C C C;
- 隐藏/投影层:所有onehot分别乘共享的输入权重 W W W(初始化权重矩阵 W W W为 N × V N×V N×V矩阵, N N N为自己设定的数),所得的向量相加求平均作为隐层向量,Size为 N × 1 N×1 N×1;
- 输出层:隐藏层计算得到的 N × 1 N×1 N×1向量作为隐藏层的输入向量,乘输出权重矩阵 W ′ W' W′( W ′ W' W′为 V × N V×N V×N矩阵),得到 V × 1 V×1 V×1的输出向量,最后经过激活函数Softmax得到 V − d i m V-dim V−dim的概率分布,其中概率最大的index所指示的单词为预测出的中间词(也是one hot);
- 损失函数:预测得到的one hot与真实的one hot进行对比,采用交叉熵损失函数,通过梯度下降反向传播更新参数 W W W,这也是我们通过训练最终需要的权重矩阵。
(2)跳字模型Skip-Gram
与CBOW正好相反,Skip-Gram是输入某个单词,要求网络预测它的上下文单词。相当于给你一个词,让你猜前面和后面可能出现什么词。
- 我们拥有的词汇库大小是 V V V(one hot的维数),预测模型输出的的窗口大小是 C C C, C = 2 R C=2R C=2R,我们的嵌入词向量的低维维度是 N N N;
- 输入层:1个词的one-hot的原始词向量( V × 1 V×1 V×1);由于嵌入词向量矩阵是唯一的,所以从输入层到隐藏层的权重 W W W(词向量矩阵, N × V N×V N×V)是唯一的,共享的;
- 隐藏/投影层:这一层输出经过 W W W加权处理的矩阵 N × 1 N×1 N×1;从隐藏层到输出层的权重矩阵 W ′ W' W′是 V × N V×N V×N矩阵;
- 输出层:矩阵和权重相乘得到 V × C V×C V×C矩阵的输出,最后经过softmax处理得到所有单词的one hot概率形式;
- 损失函数:同样,根据预测得到的one hot与真实的one hot进行对比,采用交叉熵损失函数,通过梯度下降反向传播更新参数 W W W和 W ′ W' W′。最后所得的权重矩阵 W W W就是我们所求的嵌入词向量Embedding矩阵。
有一点需要说明的是,因为一个中心词对应的上下文词可能有多个,所以正样本会有多个pair对<input_word,output_context1>,<input_word,output_context2>等,所以相同的语料,skip-gram比cbow训练的要更久。
2. 优化模型的加速方法
利用softmax求概率值时,每一次的训练我们都需要将词表里的每一个词都计算一遍(对所有的词进行排序,然后取最大值),这样遍历整个词表的操作会在大语料库计算时变得异常耗时、困难。因此,word2vec还有两个加速运算的机制,层次softmax和负采样。
两种方法好像没必要同时使用:层次Softmax和负采样能同时应用在Word2Vec模型中吗。
(1)Hierarchical Softmax
详细解释:word2vec原理(二) 基于Hierarchical Softmax的模型。
原理:为了避免隐藏层到输出的softmax层这里的庞大计算量,采用哈夫曼树来代替从隐藏层到输出softmax层的映射。哈夫曼树的所有内部节点就类似之前神经网络投影层的神经元,其中,根节点的词向量对应我们的投影后的词向量,而所有叶子节点就类似于之前神经网络softmax输出层的Vector,叶子节点的个数就是词汇表的大小。
在哈夫曼树中,隐藏层到输出层的Softmax映射不是一下子完成的,而是沿着哈夫曼树一步步完成的,因此这种Softmax取名为"Hierarchical Softmax"。其时间复杂度就从 O ( n ) O(n) O(n)变成了 O ( l o g n ) O(log^n) O(logn),高频词需要更少的时间被找到,符合贪心优化思想。
个人理解:隐藏层到输出层,所做的工作就是将所得到的词向量映射成所对应的值,用来与真实值作对比。
首先对所有在 V V V维词表的词,根据词频来构建哈夫曼树,词频越大,路径越短,编码信息更少。树中所有的叶子节点构成了词 V V V,中间节点则共有 V − 1 V-1 V−1个,每个叶子节点存在唯一的从根到该节点的最短路径。
如何“沿着霍夫曼树一步步完成”呢?在word2vec中采用了二元逻辑回归的方法,即规定沿着左子树走,那么就是负类(霍夫曼树编码1),沿着右子树走,那么就是正类(霍夫曼树编码0)。判别正类和负类的方法是使用sigmoid函数,即:
P
(
+
)
=
σ
(
x
w
T
θ
)
=
1
1
+
e
x
w
T
θ
P
(
−
)
=
1
−
P
(
+
)
P(+) = σ(x_w^Tθ) = \frac{1}{1+e^{x_w^Tθ}}\\ P(-) = 1- P(+)
P(+)=σ(xwTθ)=1+exwTθ1P(−)=1−P(+)
其中
x
w
x_w
xw是当前内部节点的词向量,而
θ
θ
θ则是我们需要从训练样本求出的逻辑回归的模型参数。
补充:哈夫曼树(Huffman):
当用 n 个结点(都做叶子结点且都有各自的权值)试图构建一棵树时,如果构建的这棵树的带权路径长度最小,称这棵树为“最优二叉树”,有时也叫“赫夫曼树”或者“哈夫曼树”。
在构建哈弗曼树时,要使树的带权路径长度最小,只需要遵循一个原则:权重越大的结点离树根越近。
(2)Negative Sampling
本部分来源:word2vec原理(三) 基于Negative Sampling的模型。
使用哈夫曼树可以提高模型训练的效率,但是如果训练样本里的中心词 w w w是一个很生僻的词,那么就得在霍夫曼树中辛苦的向下走很久了。负采样Negative Sampling摒弃了复杂的霍夫曼树,每次只是通过采样 n e g neg neg个不同的中心词做负例就可以训练模型,采用了比Hierarchical Softmax更简单的模型求解方法。
比如我们有一个训练样本,中心词是 w w w,它周围上下文共有 2 c 2c 2c个词,记为 c o n t e x t ( w ) context(w) context(w)。由于这个中心词ww,的确和 c o n t e x t ( w ) context(w) context(w)相关存在,因此它是一个真实的正例。通过Negative Sampling采样,我们得到 n e g neg neg个和 w w w不同的中心词 w i , i = 1 , 2 , … , n e g w_i,i=1,2,…,neg wi,i=1,2,…,neg,这样 c o n t e x t ( w ) context(w) context(w)和 w i w_i wi就组成了 n e g neg neg个并不真实存在的负例。利用这一个正例和 n e g neg neg个负例,我们进行二元逻辑回归,得到负采样对应每个词 w i w_i wi对应的模型参数 θ i θ_i θi,和每个词的词向量。
如何进行负采样?
- 如果词汇表的大小为 V V V,那么我们就将一段长度为 1 1 1的线段分成 V V V份,每份对应词汇表中的一个词。当然每个词对应的线段长度是不一样的,高频词对应的线段长,低频词对应的线段短。每个词 w w w的线段长度由下式决定(在word2vec中,分子和分母都取了3/4次幂):
l e n ( w ) = c o u n t ( w ) 3 4 ∑ u ∈ v o c a b c o u n t ( u ) 3 4 len(w) = \frac{count(w)^{\frac{3}{4}}}{\sum_{u∈vocab}count(u)^{\frac{3}{4}}} len(w)=∑u∈vocabcount(u)43count(w)43
- 在采样前,我们将这段长度为 1 1 1的线段划分成 M M M等份,这里 M > V M>V M>V,这样可以保证每个词对应的线段都会划分成对应的小块。而M份中的每一份都会落在某一个词对应的线段上。在采样的时候,我们只需要从 M M M个位置中采样出 n e g neg neg个位置就行,此时采样到的每一个位置对应到的线段所属的词就是我们的负例词。在word2vec中, M M M取值默认为 1 0 8 10^8 108。
5.1.4 Context2Vec
Context2Vec(Melamud et al., 2016)使用了双向长短期记忆(LSTM)围绕一个中心词编码上下文。
Context2vec 的主要目标是学习一个通用的与任务无关嵌入模型,用来表示目标词上下文的变长序列向量表示。借鉴了 word2vec 的 CBOW 模型,利用上下文来预测目标词。与 CBOW 不同的是,将原来的上下文向量求平均操作替换成了双向 LSTM 模型。
Context2Vec可以作为一种进行sentence embedding的方法,我们直接输入句子可以生成其embedding的结果,这个结果包含了序列信息。
5.2 预训练模型:从ELMo到GPT&BERT
但到目前为止,使用Word Embedding的效果都没有期待中的好,因为Word Embedding有问题——每一个单词都假设只有一个特定的嵌入向量表示,这显然还是不太符合我们的语言习惯,因为显然有时候同一个词语会有不同的意思,也就是一词多义,不论是中文英文都有这现象。为了解决这一问题,典型的两阶段过程的模型——预训练模型就出现了。
需要提一点的是,早期的预训练模型研究者们在模型结构上做的尝试比较多,比如ELMo使用了双向LSTM。然而在Transformer出现后,研究者们研究的重点就从模型结构转移到了训练策略上。
比如GPT和BERT都是基于Transformer结构的: GPT基于Transformer decoder,而BERT基于Transformer encoder。
补充:Transformer
往下大致看了一下,之后的模型基本都是基于这种网络结构,不看真不行,所以临时在这里停一下,先补充一点相关的知识,见:学习笔记:深度学习(7)——从Encoder-Decoder到Transformer。
5.2.1 ELMo
深入理解可参考:ELMo详解、NLP词向量篇(四)ELMo。
ELMo(Embeddings from Language Models),来自论文:Deep contextualized word representation(深层语境化词语表示),即动态变化的词向量,2018.3华盛顿大学。模型图如下:
ELMo是一个RNN-based的多层双向的LSTM语言模型(论文中是双层双向,Bi-LSTM,简称BiLM)。
ELMo将每一层前后向LSTM的输出与初始词向量整合起来,形成一个向量,在正则化之后输入Softmax层,最后输出的权重向量就是ELMo输出的词向量:
![img](https://i-blog.csdnimg.cn/blog_migrate/71c3ce0dd5847ceb60c8ed6ac82aa78e.png)
不像传统的词向量,每一个词只对应一个词向量,ELMo利用预训练好的双向语言模型,然后根据具体输入从该语言模型中可以得到上下文依赖的当前词表示(对于不同上下文的同一个词的表示是不一样的),通过双层LSTM提取三种Embedding特征,加权和得到最终的Embedding给其他任务(也就是基于特征融合),其词向量是动态生成的。
在此之前的Word Embedding本质上是个静态的方式,所谓静态指的是训练好之后每个单词的表达就固定住了,以后使用的时候,不论新句子上下文单词是什么,这个单词的Word Embedding不会跟着上下文场景的变化而改变。
ELMO的本质思想是:我事先用语言模型学好一个单词的Word Embedding,此时多义词无法区分,不过这没关系。在我实际使用Word Embedding的时候,单词已经具备了特定的上下文了,这个时候我可以根据上下文单词的语义去调整单词的Word Embedding表示,这样经过调整后的Word Embedding更能表达在这个上下文中的具体含义,自然也就解决了多义词的问题了。
缺点:
- 特征抽取器选择方面,LSTM抽取特征能力不够强,相对来说较Transformer(17年提出)弱;
- 训练速度慢,不能并行化,因为是RNN模型,只能递归预算,无法并行;
- 输出拼接前向、后向融合特征的方式不够好,Bert一体化的融合特征方式更优;
- 没有一个比较好的通用训练好的中文EMLo预训练模型,也没有比较好用的工具。
5.2.2 GPT
论文:Improving Language understanding by Generative Pre-Training(2018.06,OpenAI)。
GPT主要基于Transformer的Decoder部分,但是却做了一点结构上的改动,去掉了Encoder-Decoder Muti-Head Attention层,因为GPT只使用的是Decoder,那么Encoder的输入层就不需要了。具体模块图示如下,它包含了12个Decoder的叠加:
因为GPT继承了Transformer的Decoder,因此,Decoder的一些特点也用在了GPT——它是单向而非双向的。模型在第 t t t时间步时,只知道 t − 1 t-1 t−1时间步及以前的输出情况,而不知道 i i i时间步及以后的输出情况。具体内容如下:
第一阶段:预训练(Pre-Trained)
- 将词向量Embedding与Positional Encoding叠加。输入到Transformer之前的 h 0 h_0 h0是词嵌入向量和位置向量的叠加, U U U是词的one hot向量, W e W_e We是词嵌入向量矩阵, W p W_p Wp是位置向量矩阵。
h 0 = U ⋅ W e + W p h_0 = U·W_e + W_p h0=U⋅We+Wp
- 用一个Mask把“不应该看到”的输出给遮蔽掉,就像Transformer一样。这样,GPT只能从左到右,或者从右到左扫描输入数据。
-
经过GPT每一层(一共12层)的计算,得到输出(具体计算过程与Transformer相同): h t = t r a n s f o r m e r _ b l o c k ( h l − 1 ) , l ∈ [ 1 , t ] h_t = transformer\_block(h_{l-1}),\ l∈[1, t] ht=transformer_block(hl−1), l∈[1,t]。
-
最后,经过Softmax输出当前词的概率: P ( u ) = s o f t m a x ( h t W e T ) P(u) = softmax(h_tW^T_e) P(u)=softmax(htWeT)。
-
损失函数为: L 1 ( U ) = ∑ i l o g P ( u i ∣ u i − k , … , u i − 1 ; Θ ) L_1(U) = \sum_ilogP(u_i|u_{i-k},…,u_{i-1};Θ) L1(U)=∑ilogP(ui∣ui−k,…,ui−1;Θ)。其中,给定句子 U = [ u 1 , u 2 , … , u n ] U=[u_1, u_2, …, u_n] U=[u1,u2,…,un], k k k是上下文窗口的大小, P P P为条件概率, Θ Θ Θ为条件概率的参数。然后反向传播,通过随机梯度下降SGD更新参数。
第二阶段:微调(Fine-tuning)
- 即根据具体的下游任务对模型进行微调。比如有下游任务如下图所示(论文中主要介绍了:分类任务、推理任务、相似性分析、问答任务):
-
微调方式根据任务类型不同也不同:
- 分类任务:输入就是文本,最后一个词的向量直接作为微调的输入,得到最后的分类结果;
- 推理任务:输入是先验+分隔符+假设,最后一个词的向量直接作为微调的输入,得到最后的分类结果(即,是否成立);
- 相似性分析:输入是两个句子相互颠倒,得到的最后一个词的向量再相加,然后进行Linear,得到最后分类结果(即,是否相似);
- 问答任务/多项选择任务:输入是上下文和问题放在一起,加上多个回答,中间也是分隔符分隔,对于每个回答构成的句子的最后一个词的向量作为微调的输入,然后进行Linear,将多个Linear的结果进行softmax,得到最后概率最大的即为输出的词。
-
微调的过程采用的是有监督学习,具体步骤如下:
- 假设我们有个带标签的数据集 C C C,即每一个Token序列 x 1 , x 2 , … , x m x^1,x^2,…,x^m x1,x2,…,xm都有一个标签 y y y,该Token序列即为微调的输入。
- 将Token序列输入Transformer模型,得到最终的输出状态,然后将输出加到线性层Linear进行处理输出,并预测标签 y y y。公式表示: P ( y ∣ x 1 , x 2 , … , x m ) = s o f t m a x ( h l m ⋅ W y ) P(y|x^1,x^2,…,x^m) = softmax(h^m_l·W_y) P(y∣x1,x2,…,xm)=softmax(hlm⋅Wy),其中 W y W_y Wy是Linear的权重。
- 损失函数为: L 2 ( C ) = ∑ ( z , y ) l o g P ( y ∣ x 1 , x 2 , … , x m ) L_2(C) = \sum_{(z,y)}logP(y|x^1,x^2,…,x^m) L2(C)=∑(z,y)logP(y∣x1,x2,…,xm)。但是,GPT在微调的时候也考虑预训练语言模型的损失函数(可以使模型更具泛化性,并加速收敛),于是最终需要优化的函数为: L 3 ( C ) = L 2 ( C ) + λ L 1 ( C ) L_3(C) = L_2(C) + λL_1(C) L3(C)=L2(C)+λL1(C),其中 λ λ λ表示权重。同样反向传播更新参数。
GPT最主要的缺点就在于它是一个单向语言模型,如果改造成双向的语言模型任务估计也没有Bert太多事了。此外,GPT在微调阶段还需要对输入数据的结构进行调整。
补充:GPT-2&GPT-3
① GPT-2:
GPT-2是在GPT的基础上升级的版本,基本的结构和内容没有做特别大的变动(2019.02)。结构图如下:
![img](https://i-blog.csdnimg.cn/blog_migrate/dd3bdc448bccac4af51449a3f89d0985.jpeg#pic_center)
GPT-2与GPT的主要区别如下:
- Fine-tuning步骤:不再针对不同任务分别进行微调建模,而是不定义这个模型应该做什么任务,模型会自动识别出来需要做什么任务。学习的是一个通用NLP模型;
- 增加数据集:GPT-2收集了更加广泛、数量更多的语料组成数据集。该数据集包含800万个网页,大小为40G,且数据(带有任务信息的数据)都是筛选后的优质的网页;
- 增加网络参数:GPT-2将Transformer堆叠的层数增加到48层(GPT2:12 层、GPT2-XL:48 层),隐层的张量维度为1600维,参数量更是达到了15亿(1558M,Bert large是3.4亿);
- 调整Transformer:将Layer Normalization层标准化放到每个sub-block之前,并在最后一个Self-Attention后添加了额外的Layer Normalization,残差层的参数初始化根据网络深度进行调节;
- GPT-2将词汇表增加到5万(Bert英文是3万,中文是2万);Embedding size包括768、1024、1280、1600;可处理单词序列长度从GPT的512提升到1024;batch_size增加到512。
② GPT-3:
论文:Language Models are Few-Shot Learners(2020.5)。
GPT-3想要做的就是拿掉微调部分,它想用来直接解决下游任务(Zero-shot Learing,0样本)。主要内容如下:
-
参数量1750亿;
175亿的参数至少需要700G硬盘来保存,再大10倍……,由于参数量过于巨大,如果想自己训练一个GPT-3需要花费1200万美元。
-
GPT-3比非稀疏模型参数量大10倍(稀疏模型是指很多权重是0,是稀疏的)。
-
做子任务时不需要计算梯度,因为参数量很大参数很难调。
- Fine-tuning:预训练 + 训练样本计算loss更新梯度,然后预测。会更新模型参数。
- Zero-shot:预训练 + task description + prompt,直接预测。不更新模型参数。
- One-shot:预训练 + task description + example + prompt,预测。不更新模型参数。
- Few-shot:预训练 + task description + examples + prompt,预测。不更新模型参数。
5.2.3 BERT
BERT,Bidirectional Encoder Representations from Transformers(2018.10,Google),是基于Transformer的Encoder部分进行词向量训练的。来自论文《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》(深层双向Transformer预训练语言模型)。同样,BERT也分为预训练和下游任务微调两个阶段,其结构框架如下:
![img](https://i-blog.csdnimg.cn/blog_migrate/203addccdcb7c5d43d9effc65237f8bf.png)
模型的核心由BERT Encoder组成,BERT Encoder由多层BERT Layer组成,每一层的BERT Layer其实都是Transformer中的Encoder层。
两种不同规模的BRET:
BRET-Base:12层Transformer Encoder,768个隐藏层单元(输出768维)和12个头(Mutil-Head Attention),总参数量110M(1.1亿);
BRET-Large:24层Transformer Encoder,1024个隐藏层单元(输出1024维)和16个头(Mutil-Head Attention),总参数量340M(3.4亿)。
BERT及其变体一览:
模型名称 隐层数 张量维度 自注意力头数 参数量 训练语料 Bert-base-uncased 12 768 12 110M 小写英文文本 Bert-large-uncased 24 1024 16 340M 小写英文文本 Bert-base-cased 12 768 12 110M 不区分大小写的英文文本 Bert-large-cased 24 1024 16 340M 不区分大小写的英文文本 Bert-base-multilingual-uncased 12 768 12 110M 小写的102种语言文本 Bert-large-multilingual-uncased 24 1024 16 340M 小写的102种语言文本 Bert-base-chinese 12 768 12 110M 简体和繁体中文文本
1. 第一阶段:预训练
BERT的预训练过程主要分为两个部分:MLM(Mask Language Model,遮盖语言模型<完形填空>)和NSP(Next Sentence Prediction,句对预测)。Mask LM可以获取上下文相关的双向特征表示,NSP擅长处理句子或段落的匹配任务,进而实现多任务训练的目标。
Mask Language Model
BERT的MASK方式(自编码)与Transformer(自回归)不太一样。MLM通过对输入的文本序列进行随机的Mask,然后通过上下文来预测这个Mask应该是什么词,以此解决双向的问题。具体做法如下:
-
对输入句子中的Token以15%的概率进行随机选取;
-
对选取出来的Token以80%的概率替换成
[MASK]
,10%的概率替换成替换成其他Token
,最后10%保持不变。比如句子是“my dog is hairy”,15%的概率选中了“hairy”做MASK。在做MASK时,有80%的概率将“hairy”替换成
[MASK]
,10%的概率将“hairy”替换成“apple”,还有10%的概率将“hairy”保持不变。
步骤2为了提高模型的泛化能力,因为后面的微调阶段并不会做Mask的操作,为了减少预训练和微调阶段输入分布不一致的问题导致的模型表达能力差的现象,故采用了这种策略。
Next Sentence Prediction
NSP的任务描述为:给定一篇文章中的两句话,判断第二句话在文本中是否紧跟在第一句话之后(只考虑两句话,判断是否是一篇文章中的前后句)。具体做法如下:
- 从文本语料库中随机选择50%的正确语句对(即,句子2是句子1的下一句)作为正样本;
- 从文本语料库中随机选择50%的错误语句对(即,句子2是在语料库里随机抽的,不是句子1的下一句)作为负样本;
- 将正负样本作为输入数据训练,与MLM任务相结合,用输出的
CLS
进行二分类,判断两个句子是不是前后相连的关系。
2. 模型输入
BERT的输入是一个线性序列,支持单句文本和句对文本,句首用符号[CLS]
表示,句尾用符号[SEP]
表示,若是是句对,句子之间添加符号[SEP]
。(BERT中输入的Tokens的长度不能超过512,即一个句子最大的长度加上[CLS]
和[SEP]
不能超过512)
因为网络用到的Transformer中有Self-Attention,每一个位置都可以学习到其他位置词的信息,所以在这一点上他们是一样的。多几个或少几个
[CLS]
或[SEP]
都是可以的,甚至可以一个Token后面跟一个[SEP]
。
最终输入的Embedding由Token Embeddings(词向量)、Segment Embeddings(句子向量)和Position Embeddings(位置向量)相加而成,如下图。
![img](https://i-blog.csdnimg.cn/blog_migrate/fce5ad029900b7d5faccda70b5fd1244.png#pic_center)
-
Token Embeddings:最传统的词向量,与之前的语言模型的输入一样,它是将token嵌入到一个高维空间中表示的结果。
BERT采用一种新的Tokenize(分词)策略:
- 之前的策略有:word-level(将句子拆分为词)、char-level(拆分成字符);
- BERT使用的是subword:即尽量不分解常用词,而是将不常用词分解为常用的子词。比如"annoyingly"可能被认为是一个罕见的单词,并且可以分解为"annoying"和"##ly"(也有可能是"annoy"、“##ing”和"##ly")。也使模型一定程度上能够处理以前从未见过的词(OOV问题,Out Of Vocabulary)。
-
Segment Embeddings:该向量用于刻画文本的全局语义信息,表明这个词属于哪个句子(NSP需要两个句子)。
-
Position Embeddings:和Transformer不一样(预先设定好的),是学习出来的Embedding向量。
在BERT中,如果有这样两个句子“Hello world” 和“Hi there”,其中的“Hello” 和“Hi”会有完全相同的Position Embedding,“world” 和“there”也会有相同的Position Embedding。
但对于“I think, therefore I am”,其中第一个“I”和第二个“I”有着不同的Position Embedding。
Token、Segment、Position Embeddings都是通过学习自动得到的。以输入2句话为例,各层Embedding及其维数(BRET-Base的词向量是768维)可以表示如下:
3. 第二阶段:微调
这个阶段的做法和GPT是一样的。论文中提到了4种不同类型的下游任务:
- 句子对匹配(sentence pair classification):文本匹配类任务的输入是两个不同的句子,最终其实要实现的仍然是一个二分类的问题,若要用BERT实现,基本代码和流程与分类问题一致,全连接层输出维度是2。但实际工程应用上,直接采用BERT来做文本匹配问题的话最终效果不一定会好,一般解决文本匹配问题可以采用一些类似孪生网络的结构去解决,这块可以另外再去探究。
- 文本分类(single sentence classification):文本分类是BERT最擅长做的事情了,最基本的做法就是将预训练的BERT加载后,同时在输出[CLS]的基础上加一个全连接层来做分类,全连接层输出的维度就是我们要分类的类别数。当然也可以在分类之前加一个其他的网络层以达到对应的目的。
- 抽取式问答(question answering):注意由于BERT没有生成能力,所以只能做抽取式的问答。可以这样理解:这个回答其实是在一篇文章中找答案的过程,通过预测答案开始与结束位置在文中的id来进行训练。
- 序列标注(single sentence tagging):由于一般的分词任务、词性标注和命名体识别任务都属于序列标注问题。这类问题因为输入句子的每一个token都需要预测它们的标签,所以序列标注是一个单句多label分类任务,BERT模型的所有输出(除去特殊符号)都要给出一个预测结果。
迁移策略:拿到一个BERT预训练模型后,我们会有两种选择:① 把BERT当做特征提取器或者句向量,不在下游任务中微调;② 把BERT做为下游业务的主要模型,在下游任务中微调。
4. 总结评价
Bert借鉴了ELMO,GPT及CBOW,主要提出了Masked 语言模型及NSP,但是这里NSP基本不影响大局,而MLM借鉴了CBOW的思想。Bert最大的亮点在于效果好及普适性强,几乎所有NLP任务都可以套用Bert这种两阶段解决思路,而且效果应该会有明显提升。缺陷如下:
- 硬件资源消耗巨大、训练时间长;
- 预训练、微调时候输入不一致,预训练用了
[MASK]
,影响微调时模型表现; - 相同句式的词语语义区分不明显;
- 不大适合生成式任务;
- 对于超长文本不太友好,无法文档级别的NLP任务,只适合于句子和段落级别的任务。
5.2.4 预训练的基本范式
GPT和BERT代表了两种最基本的预训练范式,它们分别被称作“自回归预训练“(Auto-regressive model,AR)和“自编码预训练”(Auto-encoding model,AE),各自适用于不同类型的下游任务。
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/53dd2ab8a23a7868dca8ec5d2afd02a1.png)
其中GPT往往更适合文本生成任务,BERT往往更适合文本理解任务,两者都是基于Transformer结构的部分参数。
- 自回归模型(Auto-regressive model):经典的语言建模任务,根据已读取文本内容预测下一个单词。比如Transformer的解码器,在训练阶段应用掩码机制,使得注意力计算过程中只能看到某个单词之前的内容,而没有后面的内容。尽管可以对这类预训练好的模型进行微调并在许多下游任务上取得出色的结果,但其最自然的应用是文本生成。这种模型的代表是GPT系列模型。
- 自编码模型(Auto-encoding model):通过某种方式破坏输入文本(比如Bert中对输入文本进行遮盖)并尝试重建原始文本的方式进行预训练。从某种意义上讲,与Transformer的编码器相对应,因为它们无需任何掩码,每一个位置都可访问全部输入的内容。这些模型通常能建立整个句子的双向编码表示,可以对它们进行微调并在许多下游任务上取得出色的结果,其最自然的应用是句子分类或序列标注。此类模型的典型代表是BERT。
5.3 预训练模型:PTM After GPT&BERT
5.3.1 发展概述
从GPT和BERT出发,提出了一些改进方案,下图是目前(2021.6)主要的改进模型(来自:《Pre-Trained Models: Past, Present and Future》)。
![img](https://i-blog.csdnimg.cn/blog_migrate/d5ed598d3e822b075096f387e3618ac7.png)
GPT和BERT之后的预训练模型研究可以分为以下几个方面:
1. 改进架构设计
两个方向,Unified Sequence Modeling(统一序列模型)和Cognitive-inspired Architectures(认知启发架构)。
- 统一序列模型
- 结合自回归和自编码建模。如XLNet通过在预训练对token进行排列,然后应用自回归预测范式。
- 应用广义编解码器。如编码器结构(如BERT)或解码器结构(如GPT)都不能解决重要问题:用可变长度填充空白。
- 认知启发架构
- 可维护工作记忆。如Transformer-XL 引入段级递归和相对位置编码,CogQA 提出多跳阅读中保持认知图。
- 可持续的长短记忆。 如用大型键值存储网络取代Transformer 层的前馈网络,或者将masked预训练扩展为自回归生成。
2. 利用多源数据
利用多源异构数据的PTMs,包括多语言PTMs、多模态PTMs和知识增强PTMs。
- 更多模态:除了图像和文本,视频和音频也可以用于多模态的预训练。
- 更深刻解释:深度学习可视化工具可以用于多模式训练前的解释。
- 更多下游应用:多模态预处理可以应用于图像-文本检索、图像-文本生成、文本-图像生成和其他下游任务。
- 迁移学习:要使多模态多语言模型处理不同的语言,预训练需要每种语言的数据。
3. 提高计算效率
主要集中在三个方面:系统级优化、高效学习和模型压缩策略。
- 系统级优化:单设备优化、多设备优化;
- 高效学习
- 高效训练方法:不同层次上自适应地使用不同的学习速率在batch size较大时加快收敛速度。
- 高效模型架构:更多模型结构变体也可以降低计算复杂度,提高训练 PTMs效率。
- 模型压缩
- 参数共享:可以通过在相似单元之间共享参数来压缩PTMs。
- 知识蒸馏:训练一个小模型,以再现一个大模型的行为。即使用一个小型的精馏模型进行推断时,内存使用和时间开销都减少了。
- 模型剪枝:削减注意层和线性层的权重,以减少PTMs中的参数数量,同时保持与原始模型的可比性能。
- 模型量化:指将精度较高的浮点参数压缩为精度较低的浮点参数。
4. 计算有效性
- 数据传输:开发一个高效分布式深度学习框架。
- 并行策略:在并行策略的选择上,数据并行、模型并行、流水线并行以及各种混合并行方法都可以根据神经网络的结构和硬件配置找到自己的最佳使用。
- 大规模训练:鉴深度学习框架对模型并行性和流水线并行性的支持不足,一开发了专门用于大规模训练框架。
- 包装和插件:通过在现有框架上计算操作之间手工插入数据路由操作来开发各种专门用于某些特定算法的库。
5.3.2 XLM
论文:《Cross-lingual Language Model Pretraining》(2019.1 Facebook)
XLM,是基于BERT优化的跨语言的语言模型。在多个跨语言理解(XLU)的benchmark上有显著提升,贡献在于:① 引入一种新的无监督方法,用于学习跨语言的representation;② 引入一种新的有监督训练目标,在并行数据可用的情况下提高了跨语言预训练的效果;③ 跨语言模型可以显著改进低资源语言的复杂性。
相比BERT,XLM所做的工作如下:
-
采用一种Byte-Pair Encoding(BPE)编码方式(Subword的一种方法),替换Bert用单词或字符作为模型输入的方式。
- 先对各个语料分别采样,然后将各个语言的采样语料拼接,最后进行正常的BPE处理。将文本输入切分成所有语言最通用的子单词,从而提升不同语言之间共享的词汇量。
-
提出了三个训练目标,前两个只需要单语数据(用于无监督训练),第三个需要并行语料(用于有监督训练)。
- Causal Language Modeling,CLM:用于在给定前面单词的情况下给出下一个单词的概率;
- Masked Language Modeling,MLM:与BERT预训练不同的是,BERT的输入使用成对的句子,而XLM使用由任意数量的句子(每个句子截断为256个token)组成的文本流代替成对的句子——即,将物理上相邻的多个句子当作一整个句子组,选择两个句子组作为输入对;
- Translation Language Modeling,TLM:TLM是MLM的扩展,不考虑单语种的文本流, 而是将并行的翻译句子拼接起来。如下图所示,在source句和target句中都随机mask一些tokens,此时要预测英文句子中被mask的token时,模型不仅能够注意到英文token,还能够注意到法语的翻译内容,因此能引导模型将英语和法语的representation进行对齐;特别地,该模型在source句不足以推断出被mask的token时,能够利用target句的上下文信息。
完整的XLM模型是通过MLM和TLM两种策略进行训练和交互。
5.3.3 ERINE
论文:《ERNIE: Enhanced Representation through Knowledge Integration》(2019.4 百度)
BERT中用[Mask]
代替单个字符而非实体或短语,没有考虑词法结构/语法结构,因此ERINE在预训练过程利用了更丰富的语义知识和更多的语义任务,主要针对中文进行训练。
-
在预训练阶段引入知识(即预先识别出的实体),引入3中Mask策略:
-
Basic-Level Masking:与BERT一样,对Subword进行Mask,无法获取高层次语义;
-
Phrase-Level Masking:Mask连续短语;
-
Entity-Level Masking:Mask预先识别出的实体;
-
-
在预训练阶段引入论坛对话类数据:
- 利用对话语言模式(DLM, Dialogue Language Model)建模Query-Response对话结构,将对话Pair对作为输入,引入Dialogue Embedding标识对话的角色,利用对话响应丢失(DRS, Dialogue Response Loss)学习对话的隐式关系,进一步提升模型的语义表示能力。
ERNIE 2.0
论文:《ERNIE 2.0: A Continual Pre-training Framework for Language Understanding》(2019.7 百度)
ERNIE 2.0的架构图如下:
在ERNIE 1.0的基础上,在预训练阶段引入了多任务学习,包括三大类学习任务,分别是:
- Word-aware Tasks(词法层任务):学会对句子中的词汇 (lexical) 进行预测。
- Structure-aware Tasks(语法层任务):学习语法 (syntactic) 级别的信息,学会将多个句子结构重建,重新排序。
- Semantic-aware Tasks(语义层任务):学习语义 (semantic) 级别的信息,学会判断句子之间的逻辑关系,例如因果关系、转折关系、并列关系等。
5.3.4 BERT-wwm
论文:《Pre-Training with WholeWord Masking for Chinese BERT》(2019.7 哈工大 +讯飞 ),介绍及使用可见:Chinese-BERT-wwm。
Whole Word Masking (wwm),全词Mask/整词Mask。与百度ERNIE相比,BERT-WWM不仅仅是连续mask实体词和短语,而是连续mask所有能组成中文词语的字。具体做法是,针对中文,如果一个完整的词的部分字被mask,则同属该词的其他部分也会被mask,即对组成同一个词的汉字全部进行Mask,即为全词Mask。
需要注意的是,这里的Mask指的是广义的Mask(替换成
[MASK]
;保持原词汇;随机替换成另外一个词),并非只局限于单词替换成[MASK]
标签的情况。
这样做的目的是:预训练过程中,模型能够学习到词的语义信息,训练完成后字的embedding就具有了词的语义信息了,这对各类中文NLP任务都是友好的。
后续相关改进的模型有:BERT-wwm-ext、RoBERTa-wwm-ext、RoBERTa-wwm-ext-large、RBT3、RBTL3。
5.3.5 XLNet
论文:《XLNet: Generalized Autoregressive Pretraining for Language Understanding》(2019.6 CMU+google brain)
XLNet基于BERT和GPT等两类预训练模型进行改进,分别吸取了两类模型的长处。主要思路是基于AR(自回归)采用了一种新的方法实现双向编码,将GPT的从左向右建模扩展成乱序建模,来弥补GPT无法获取双向上下文信息的缺陷。改进点如下(具体可见:Google XLNet原理解读):
- Permutation Language Modeling,排列语言模型(Permutation LM)
- PLM的本质是LM联合概率的多种分解机制的体现,将LM的顺序拆解推广到随机拆解,但是需要保留每个词的原始位置信息,遍历其中的分解方法,并且模型参数共享,就可以学习到预测词上下文。
- Two-Stream Self-Attention
- 解决由PLM带来的没有目标位置信息的问题。
- Transformer-XL
- 使用Transformer-XL学习到更长距离的信息
5.3.6 RoBERTa
论文:《RoBERTa: A Robustly Optimized BERT Pretraining Approach》(2019.7.26 Facebook)
RoBERTa模型是更具鲁棒性的、优化后的BERT模型。具体的调整点如下:
-
更多训练数据/更大的batch size/训练更长时间;
-
训练数据上,RoBERTa 采用了 160G 的训练文本,而 BERT 仅使用 16G 的训练文本,最多达 500 K 500K 500K步。
同样有roberta-base和roberta-large两个模型。
-
-
Dynamic Masking(动态Mask机制):
- 采用动态Mask策略,RoBERTa一开始把预训练的数据复制10份,每一份中都采用不同的静态Mask操作(即,每一份都随机选择15%的Tokens进行Masking,同样的一句话有10种不同的mask方式),使得预训练的每条数据有了10种不同的Mask数据。
- 然后,每份数据都训练N/10个epoch。这就相当于在这N个epoch的训练中,每个序列的被Mask掉的词是会发生变化的。(注意这些数据不是全部都喂给同一个epoch,是不同的epoch,例如
dupe_factor=10
,epoch=40
, 则每种mask的方式在训练中会被使用4次。)
-
No NSP and Input Format:
- 移除了NSP(Next Sentence Prediction)任务;
- 在RoBERTa中,采用的是Full-sentence的形式。取消下一个句子预测,每一个训练样本都是从一个文档中连续Sample出来的。从结论中发现效果会更好,说明NSP任务不是必须的。
5.3.7 T5
论文:《Exploring the Limits of Transfer Learning with a Unified Text-to-Text Transformer》(2019.10,Google)
T5(Text-to-Text Transfer Transformer),统一的文本到文本任务模型。T5基于Bert,使用传统的Transformer,为了能够处理所有NLP任务,将所有NLP任务都转换为统一的文本到文本格式,任务的输入和输出始终是文本字符串。这个框架允许在任何NLP任务上使用相同的模型、损失函数和超参数。具体内容可见:Google预训练语言模型T5。
5.3.8 ALBert
论文:《[ALBERT: A Lite BERT for Self-supervised Learning of Language Representations](https://arxiv.org/abs/1909.11942#:~:text=ALBERT%3A A Lite BERT for Self-supervised Learning of,to GPU%2FTPU memory limitations and longer training times.)》(2019.10 Google)
为了解决目前预训练模型参数量过大的问题,谷歌的研究者设计了一个轻量化的 BERT(A Lite BERT,ALBERT),参数量远远少于传统的 BERT 架构。ALBERT提出了两种能够大幅减少预训练模型参数量的方法,此外还提出用Sentence-Order Prediction(SOP)任务代替BERT中的Next-sentence prediction(NSP)任务,在多个自然语言理解任务中取得了State-Of-The-Art(SOTA)的结果。
ALBERT架构的骨干网络与BERT是相似的,即使用Transformer编码器和GELU非线性激活函数,三大调整如下:
-
词嵌入参数因式分解:
- 作者认为,词向量只是记忆了相对少量的词语的信息,更多的语义和句法等信息是由隐层记忆的,因此,他们认为,词嵌入的维度可以不必与隐藏层的维度一致,可以通过降低词嵌入的维度的方式来减少参数量,于是对词嵌入参数进行了因式分解,将它们分解为两个小矩阵。
- 研究者不再将 one-hot 向量直接映射到大小为 H 的隐藏空间,而是先将它们映射到一个低维词嵌入空间 E,然后再映射到隐藏空间。通过这种分解,研究者可以将词嵌入参数从 O ( V × H ) O(V × H) O(V×H)降低到 O ( V × E + E × H ) O(V × E + E × H) O(V×E+E×H),这在 H 远远大于 E 的时候,参数量减少得非常明显。
-
跨层参数共享:
- 全连接层、注意力层的参数均是共享的,避免参数量随着网络深度的增加而增加,也就是ALBERT依然有多层的深度连接,但是各层之间的参数是一样的。
- 主要是为了减少参数量(性能轻微降低,参数大量减少,这整体上是好的事情)。
-
句间顺序预测(SOP)
-
提出用Sentence-Order Prediction(SOP)任务代替BERT中的NSP任务,是给模型两个句子,让模型去预测两个句子的前后顺序。
-
SOP的正例选取方式与BERT一致(来自同一文档的两个连续段),而负例不同于BERT中的sample,同样是来自同一文档的两个连续段,但交换两段的顺序,从而避免了主题预测,只关注建模句子之间的连贯性。
在先前的研究中,已经证明NSP是并不是一个合适的预训练任务。推测其原因是模型在判断两个句子的关系时不仅考虑了两个句子之间的连贯性(coherence),还会考虑到两个句子的话题(topic)。而两篇文档的话题通常不同,模型会更多的通过话题去分析两个句子的关系,而不是句子间的连贯性,这使得NSP任务变成了一个相对简单的任务。
-
两种参数削减技术可以充当某种形式的正则化,使训练更加稳定,而且有利于泛化。
最小的ALBERT-base参数只有12M, 效果要比BERT低1-2个点,最大的ALBERT-xxlarge也就233M。可以看到在模型参数量上减少的还是非常明显的,但是在速度上似乎没有那么明显。最大的问题就是这种方式其实并没有减少计算量,也就是受推理时间并没有减少。
![img](https://i-blog.csdnimg.cn/blog_migrate/d907b3033b5cbd786e27a24b52445a80.jpeg)