NLP中的语言模型预训练&微调

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/muumian123/article/details/84990765

 

1 引言

语言模型(Language Model),语言模型简单来说就是一串词序列的概率分布。具体来说,语言模型的作用是为一个长度为m的文本确定一个概率分布P,表示这段文本存在的可能性。在实践中,如果文本的长度较长,P(wi | w1, w2, . . . , wi−1)的估算会非常困难。因此,研究者们提出使用一个简化模型:n元模型(n-gram model)。在 n 元模型中估算条件概率时,只需要对当前词的前n个词进行计算。在n元模型中,传统的方法一般采用频率计数的比例来估算n元条件概率。当n较大时,机会存在数据稀疏问题,导致估算结果不准确。因此,一般在百万词级别的语料中,一般也就用到三元模型。

为了缓解n元模型估算概率时遇到的数据稀疏问题,研究者们提出了神经网络语言模型。代表性工作是Bengio等人在2003年提出的神经网络语言模型,该语言模型使用了一个三层前馈神经网络来进行建模。其中有趣的发现了第一层参数,用做词表示不仅低维紧密,而且能够蕴涵语义,也就为现在大家都用的词向量(例如word2vec)打下了基础。其实,语言模型就是根据上下文去预测下一个词是什么,这不需要人工标注语料,所以语言模型能够从无限制的大规模单语语料中,学习到丰富的语义知识。

目前神经网络在进行训练的时候基本都是基于后向传播(BP)算法,通过对网络模型参数进行随机初始化,然后通过BP算法利用例如SGD这样的优化算法去优化模型参数。那么预训练的思想就是,该模型的参数不再是随机初始化,而是先有一个任务进行训练得到一套模型参数,然后用这套参数对模型进行初始化,再进行训练。其实早期的使用自编码器栈式搭建深度神经网络就是这个思想。还有词向量也可以看成是第一层word embedding进行了预训练,此外在基于神经网络的迁移学习中也大量用到了这个思想。

2 ELMo

以前的词向量都是与上下文无关的词向量,比如用word2vec训练的时候,使用单层神经网络,通过几个周围单词预测中心单词,但是这几个周围单词的顺序并不重要,而且生成的词向量只是代表一种统计意义上最大概率上的语义,即生成的词向量与句法、放入某个特定环境的语义等没有太多关联。

ELMO 的本质思想是:我事先用语言模型学好一个单词的 Word Embedding,此时多义词无法区分,不过这没关系。在我实际使用 Word Embedding 的时候,单词已经具备了特定的上下文了,这个时候我可以根据上下文单词的语义去调整单词的 Word Embedding 表示,这样经过调整后的 Word Embedding 更能表达在这个上下文中的具体含义,自然也就解决了多义词的问题了。所以 ELMO 本身是个根据当前上下文对 Word Embedding 动态调整的思路。

2.1 预训练

ELMO 采用了典型的两阶段过程,第一个阶段是利用语言模型进行预训练;第二个阶段是在做下游任务时,从预训练网络中提取对应单词的网络各层的 Word Embedding 作为新特征补充到下游任务中。

上图展示的是其预训练过程,它的网络结构采用了双层双向 LSTM,目前语言模型训练的任务目标是根据单词Wi的上下文去正确预测单词WiWi之前的单词序列 Context-before 称为上文,之后的单词序列 Context-after 称为下文。图中左端的前向双层LSTM代表正方向编码器,输入的是从左到右顺序的除了预测单词外Wi的上文 Context-before;右端的逆向双层 LSTM 代表反方向编码器,输入的是从右到左的逆序的句子下文 Context-after;每个编码器的深度都是两层 LSTM 叠加。这个网络结构其实在 NLP 中是很常用的。

使用这个网络结构利用大量语料做语言模型任务就能预先训练好这个网络,如果训练好这个网络后,输入一个新句子Snew,句子中每个单词都能得到对应的三个Embedding:最底层是单词的 Word Embedding,往上走是第一层双向 LSTM中对应单词位置的 Embedding,这层编码单词的句法信息更多一些;再往上走是第二层 LSTM 中对应单词位置的 Embedding,这层编码单词的语义信息更多一些。也就是说,ELMO 的预训练过程不仅仅学会单词的 Word Embedding,还学会了一个双层双向的 LSTM 网络结构,而这两者后面都有用。

2.2 下游任务

下图展示了下游任务的使用过程,比如下游任务仍然是QA问题,此时对于问句X,我们可以先将句子X作为预训练好的ELMo网络的输入,这样句子X中每个单词在 ELMo网络中都能获得对应的三个 Embedding,之后给予这三个 Embedding 中的每一个 Embedding 一个权重 a,这个权重可以学习得来,根据各自权重累加求和,将三个 Embedding 整合成一个。

然后将整合后的这个 Embedding 作为 X 句在自己任务的那个网络结构中对应单词的输入,以此作为补充的新特征给下游任务使用。对于上图所示下游任务 QA 中的回答句子 Y 来说也是如此处理。因为 ELMO给下游提供的是每个单词的特征形式,所以这一类预训练的方法被称为“Feature-based Pre-Training”。

前面我们提到静态 Word Embedding 无法解决多义词的问题,ELMO 引入上下文动态调整单词的 embedding 后多义词问题得到解决了。

上图给了个例子,对于 Glove 训练出的 Word Embedding 来说,多义词比如 play,根据它的 embedding 找出的最接近的其它单词大多数集中在体育领域,这很明显是因为训练数据中包含 play 的句子中体育领域的数量明显占优导致;而使用 ELMO,根据上下文动态调整后的 embedding 不仅能够找出对应的「演出」的相同语义的句子,而且还可以保证找出的句子中的 play 对应的词性也是相同的,这是超出期待之处。之所以会这样,是因为我们上面提到过,第一层 LSTM 编码了很多句法信息,这在这里起到了重要作用。

ELMO 有什么值得改进的缺点呢?首先,一个非常明显的缺点在特征抽取器选择方面,ELMO 使用了 LSTM 而不是新贵 Transformer,Transformer 是谷歌在 17 年做机器翻译任务的“Attention is all you need”的论文中提出的,引起了相当大的反响,很多研究已经证明了 Transformer 提取特征的能力是要远强于 LSTM 的。

除了以 ELMO 为代表的这种基于特征融合的预训练方法外,NLP 里还有一种典型做法,这种做法和图像领域的方式就是看上去一致的了,一般将这种方法称为“基于 Fine-tuning 的模式”,而 GPT 就是这一模式的典型开创者。

3 Open AI GPT

GPT 是“Generative Pre-Training”的简称,从名字看其含义是指的生成式的预训练。GPT 也采用两阶段过程,第一个阶段是利用语言模型进行预训练,第二阶段通过 Fine-tuning 的模式解决下游任务。上图展示了 GPT 的预训练过程,其实和 ELMO 是类似的,主要不同在于两点:

  • 首先,特征抽取器不是用的 RNN,而是用的 Transformer,上面提到过它的特征抽取能力要强于 RNN,这个选择很明显是很明智的;

  • 其次,GPT 的预训练虽然仍然是以语言模型作为目标任务,但是采用的是单向的语言模型,所谓“单向”的含义是指:语言模型训练的任务目标是根据Wi单词的上下文去正确预测单词WiWi之前的单词序列 Context-before 称为上文,之后的单词序列 Context-after 称为下文。ELMO 在做语言模型预训练的时候,预测单词Wi同时使用了上文和下文,而 GPT 则只采用 Context-before 这个单词的上文来进行预测,而抛开了下文。

这个选择现在看不是个太好的选择,原因很简单,它没有把单词的下文融合进来,这限制了其在更多应用场景的效果,比如阅读理解这种任务,在做任务的时候是可以允许同时看到上文和下文一起做决策的。如果预训练时候不把单词的下文嵌入到 Word Embedding 中,是很吃亏的,白白丢掉了很多信息。

3.1 预训练

为了方便nlp的迁移学习,人们提出采用无标注数据训练语言模型(language model),并在其后加上一层全连接和softmax组成分类器,用有标注数据fine-tuning分类器。本文不使用rnn而使用transformer decoder做语言模型,并且将模型的input根据task做一定的转变,以期待分类器基本不变。本文在自然语言推断,问答系统,语义相似度,文本分类这四个问题上做了实验,并且做了预训练模型的zero-shot实验。

如上图所示,看绿色部分,该模型在pretrain语言模型(第一阶段)使用transfomer+左边的绿色部分,该模型在fine-tuning分类器(第二阶段)使用transfomer+右边的绿色部分。然后再具体NLP任务有监督微调时,与ELMo当成特征的做法不同,OpenAI GPT不需要再重新对任务构建新的模型结构,而是直接在transformer这个语言模型上的最后一层接上softmax作为任务输出层,然后再对这整个模型进行微调。他们额外发现,如果使用语言模型作为辅助任务,能够提升有监督模型的泛化能力,并且能够加速收敛。由于不同NLP任务的输入有所不同,在transformer模型的输入上针对不同NLP任务也有所不同。具体如下图,对于分类任务直接讲文本输入即可;对于文本蕴涵任务,需要将前提和假设用一个Delim分割向量拼接后进行输入;对于文本相似度任务,在两个方向上都使用Delim拼接后,进行输入;对于像问答多选择的任务,就是将每个答案和上下文进行拼接进行输入。

1. 第一阶段:

语言模型采用transformer decoder,内有12个相同的transformer block,具体block细节请参考attention is all you need。

https://img-blog.csdnimg.cn/20181106212626166.PNG

https://img-blog.csdnimg.cn/20181106213148322.PNG

最后一个transformer block后跟一层全连接和softmax构成text prediction,预测下一个token的概率。

https://img-blog.csdnimg.cn/20181106213247275.PNG

我们希望根据之前的tokens预测下一个token的概率最大,目标函数为:

https://img-blog.csdnimg.cn/20181106212413808.PNG

2. 第二阶段: 

在最后一个transformer block后跟一层全连接和softmax构成task classifier,预测每个类别的概率。

https://img-blog.csdnimg.cn/20181106213512834.PNG

我们希望在输入所有的token后,预测true类别的概率最大,目标函数为:

https://img-blog.csdnimg.cn/2018110621363055.PNG

 为了更好的fine-tuning分类器,更快的收敛,修改目标函数为task classifier和text prediction相结合:

https://img-blog.csdnimg.cn/20181106213844625.PNG

3.2 下游任务

首先,对于不同的下游任务来说,本来你可以任意设计自己的网络结构,现在不行了,你要向 GPT 的网络结构看齐,把任务的网络结构改造成和 GPT 的网络结构是一样的。然后,在做下游任务的时候,利用第一步预训练好的参数初始化 GPT 的网络结构,这样通过预训练学到的语言学知识就被引入到你手头的任务里来了,这是个非常好的事情。再次,你可以用手头的任务去训练这个网络,对网络参数进行 Fine-tuning,使得这个网络更适合解决手头的问题。

根据task转变模型的输入:

 所有的任务的input都由开始token(start)开始,由结束token(extract)结束。

  • entailment:因输入有前提和假说两个句子,那就在两个句子中加入分隔符(delim)连接两条句子,作为输入,经过语言模型送入分类器。
  • similarity:因两条句子的顺序不影响结果,就按两种顺序分别放入语言模型得到各自的hidden state,将两种hidden state相加,送入分类器。
  • multiple choice:对于每个答案,都将context、问题、该答案以分隔符隔开连接起来,作为输入,经过语言模型送入分类器得到一个向量,将所有答案的向量送入softmax。

4 BERT

《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》有五个关键词,分别是 Pre-training、Deep、Bidirectional、Transformers、和 Language Understanding。其中 pre-training 的意思是,作者认为,确实存在通用的语言模型,先用文章预训练通用模型,然后再根据具体应用,用 supervised 训练数据,精加工(fine tuning)模型,使之适用于具体应用。为了区别于针对语言生成的 Language Model,作者给通用的语言模型,取了一个名字,叫语言表征模型 Language Representation Model。

能实现语言表征目标的模型,可能会有很多种,具体用哪一种呢?作者提议,用 Deep Bidirectional Transformers 模型。假如给一个句子 “能实现语言表征[mask]的模型”,遮盖住其中“目标”一词。从前往后预测[mask],也就是用“能/实现/语言/表征”,来预测[mask];或者,从后往前预测[mask],也就是用“模型/的”,来预测[mask],称之为单向预测 unidirectional。单向预测,不能完整地理解整个语句的语义。于是研究者们尝试双向预测。把从前往后,与从后往前的两个预测,拼接在一起 [mask1/mask2],这就是双向预测 bi-directional。

BERT 的作者认为,bi-directional 仍然不能完整地理解整个语句的语义,更好的办法是用上下文全向来预测[mask],也就是用 “能/实现/语言/表征/../的/模型”,来预测[mask]。BERT 作者把上下文全向的预测方法,称之为 deep bi-directional。如何来实现上下文全向预测呢?BERT 的作者建议使用 Transformer 模型。这个模型由《Attention Is All You Need》一文发明。

这个模型的核心是聚焦机制,对于一个语句,可以同时启用多个聚焦点,而不必局限于从前往后的,或者从后往前的,序列串行处理。不仅要正确地选择模型的结构,而且还要正确地训练模型的参数,这样才能保障模型能够准确地理解语句的语义。BERT 用了两个步骤,试图去正确地训练模型的参数。第一个步骤是把一篇文章中,15% 的词汇遮盖,让模型根据上下文全向地预测被遮盖的词。假如有 1 万篇文章,每篇文章平均有 100 个词汇,随机遮盖 15% 的词汇,模型的任务是正确地预测这 15 万个被遮盖的词汇。通过全向预测被遮盖住的词汇,来初步训练 Transformer 模型的参数。

然后,用第二个步骤继续训练模型的参数。譬如从上述 1 万篇文章中,挑选 20 万对语句,总共 40 万条语句。挑选语句对的时候,其中 210 万对语句,是连续的两条上下文语句,另外 210 万对语句,不是连续的语句。然后让 Transformer 模型来识别这 20 万对语句,哪些是连续的,哪些不连续。

这两步训练合在一起,称为预训练 pre-training。训练结束后的 Transformer 模型,包括它的参数,是作者期待的通用的语言表征模型。

4.1 Transformer 概览

在整个 Transformer 架构中,它只使用了注意力机制和全连接层来处理文本,因此 Transformer 确实没使用循环神经网络或卷积神经网络实现「特征抽取」这一功能。此外,Transformer 中最重要的就是自注意力机制,这种在序列内部执行 Attention 的方法可以视为搜索序列内部的隐藏关系,这种内部关系对于翻译以及序列任务的性能有显著提升。

如 Seq2Seq 一样,原版 Transformer 也采用了编码器-解码器框架,但它们会使用多个 Multi-Head Attention、前馈网络、层级归一化和残差连接等。下图从左到右展示了原论文所提出的 Transformer 架构、Multi-Head Attention 和点乘注意力。

其中点乘注意力是注意力机制的一般表达形式,将多个点乘注意力叠加在一起可以组成 Transformer 中最重要的 Multi-Head Attention 模块,多个 Multi-Head Attention 模块堆叠在一起就组成了 Transformer 的主体结构,并借此抽取文本中的信息。

https://image.jiqizhixin.com/uploads/editor/c718a24b-aaf2-4937-a51d-853fca8f770e/1541060383211.png

上图右边的点乘注意力其实就是标准 Seq2Seq 模型中的注意力机制。其中 Query 向量与 Value 向量在 NMT 中相当于目标语输入序列与源语输入序列,Query 与 Key 向量的点乘相当于余弦相似性,经过 SoftMax 函数后可得出一组归一化的概率。这些概率相当于给源语输入序列做加权平均,即表示在生成一个目标语单词时源语序列中哪些词是重要的。

上图中间的 Multi-head Attention 其实就是多个点乘注意力并行处理并将最后的结果拼接在一起。这种注意力允许模型联合关注不同位置的不同表征子空间信息,我们可以理解为在参数不共享的情况下,多次执行点乘注意力。

最后上图左侧为 Transformer 的整体架构。输入序列首先会转换为词嵌入向量,在与位置编码向量相加后可作为 Multi-Head 自注意力模块的输入,自注意力模块表示 Q、V、K 三个矩阵都是相同的。该模块的输出再经过一个全连接层就可以作为编码器模块的输出。

原版 Transformer 的解码器与编码器结构基本一致,只不过在根据前面译文预测当前译文时会用到编码器输出的原语信息。在 BERT 论文中,研究者表示他们只需要使用编码器抽取文本信息,因此相对于原版架构只需要使用编码器模块。

在模型架构上,BERT 使用了非常深的网络,原版 Transformer 只堆叠了 6 个编码器解码器模块,即上图的 N=6。而 BERT 基础模型使用了 12 个编码器模块(N=12),BERT 大模型堆叠了 24 个编码器模块(N=24)。其中堆叠了 6 个模块的 BERT 基础模型主要是为了和 OpenAI GPT 进行对比。

4.2 模型架构

BERT 的模型架构是一个多层双向 Transformer 编码器,基于 Vaswani 等人 (2017) 描述的原始实现,在 tensor2tensor 库中发布。将层数(即 Transformer 块)表示为 L,将隐藏尺寸表示为 H、自注意力头数表示为 A。在所有实验中,我们将前馈/滤波器尺寸设置为 4H,即 H=768 时为 3072,H=1024 时为 4096。我们要报告在两种模型尺寸上的结果:

BERTBASE: L=12, H=768, A=12, 总参数=110M

BERTLARGE: L=24, H=1024, A=16, 总参数=340M

为了比较,BERTBASE 的模型尺寸与 OpenAI GPT 相当。然而,BERT Transformer 使用双向自注意力机制,而 GPT Transformer 使用受限的自注意力机制,导致每个 token 只能关注其左侧的语境。我们注意到,双向 Transformer 在文献中通常称为「Transformer 编码器」,而只关注左侧语境的版本则因能用于文本生成而被称为「Transformer 解码器」。

4.2.1 输入表示(input representation)

论文的输入表示(input representation)能够在一个token序列中明确地表示单个文本句子或一对文本句子(例如, [Question, Answer])。对于给定token,其输入表示通过对相应的token、segment和position embeddings进行求和来构造。

https://image.jiqizhixin.com/uploads/editor/f313e8ea-a022-4109-a95d-d6686f82b350/1541060383093.png

  • 使用WordPiece嵌入(Wu et al., 2016)和30,000个token的词汇表。用##表示分词。
  • 使用学习的positional embeddings,支持的序列长度最多为512个token。
  • 每个序列的第一个token始终是特殊分类嵌入([CLS])。对应于该token的最终隐藏状态(即,Transformer的输出)被用作分类任务的聚合序列表示。对于非分类任务,将忽略此向量。
  • 句子对被打包成一个序列。以两种方式区分句子。首先,用特殊标记([SEP])将它们分开。其次,添加一个learned sentence A嵌入到第一个句子的每个token中,一个sentence B嵌入到第二个句子的每个token中。
  • 对于单个句子输入,只使用 sentence A嵌入。

4.3 预训练任务

与 Peters 等人 (2018) 和 Radford 等人 (2018) 不同,我们不使用传统的从左到右或从右到左的语言模型来预训练 BERT,而是使用两个新型无监督预测任务。

4.3.1 任务 #1:Masked LM

为了训练一个深度双向表示(deep bidirectional representation),研究团队采用了一种简单的方法,即随机屏蔽(masking)部分输入token,然后只预测那些被屏蔽的token。论文将这个过程称为“masked LM”(MLM),尽管在文献中它经常被称为Cloze任务(Taylor, 1953)。

在这个例子中,与masked token对应的最终隐藏向量被输入到词汇表上的输出softmax中,就像在标准LM中一样。在团队所有实验中,随机地屏蔽了每个序列中15%的WordPiece token。与去噪的自动编码器(Vincent et al., 2008)相反,只预测masked words而不是重建整个输入。

虽然这确实能让团队获得双向预训练模型,但这种方法有两个缺点。首先,预训练和finetuning之间不匹配,因为在finetuning期间从未看到[MASK]token。为了解决这个问题,团队并不总是用实际的[MASK]token替换被“masked”的词汇。相反,训练数据生成器随机选择15%的token。例如在这个句子“my dog is hairy”中,它选择的token是“hairy”。然后,执行以下过程:

数据生成器将执行以下操作,而不是始终用[MASK]替换所选单词:

  • 80%的时间:用[MASK]标记替换单词,例如,my dog is hairy → my dog is [MASK]
  • 10%的时间:用一个随机的单词替换该单词,例如,my dog is hairy → my dog is apple
  • 10%的时间:保持单词不变,例如,my dog is hairy → my dog is hairy. 这样做的目的是将表示偏向于实际观察到的单词。

4.3.2 任务 #2:下一句预测

很多重要的下游任务(如问答(QA)和自然语言推断(NLI))基于对两个文本句子之间关系的理解,这种关系并非通过语言建模直接获得。为了训练一个理解句子关系的模型,我们预训练了一个二值化下一句预测任务,该任务可以从任意单语语料库中轻松生成。具体来说,选择句子 A 和 B 作为预训练样本:B 有 50% 的可能是 A 的下一句,也有 50% 的可能是来自语料库的随机句子。

4.4 微调过程

Fine-Tuning 阶段,这个阶段的做法和 GPT 是一样的。当然,它也面临着下游任务网络结构改造的问题,在改造任务方面 Bert 和 GPT 有些不同,下面简单介绍一下。通常而言,绝大部分 NLP 问题可以归入上图所示的四类任务中:

  • 一类是序列标注,这是最典型的 NLP 任务,比如中文分词,词性标注,命名实体识别,语义角色标注等都可以归入这一类问题,它的特点是句子中每个单词要求模型根据上下文都要给出一个分类类别。
  • 第二类是分类任务,比如我们常见的文本分类,情感计算等都可以归入这一类。它的特点是不管文章有多长,总体给出一个分类类别即可。
  • 第三类任务是句子关系判断,比如 Entailment,QA,语义改写,自然语言推理等任务都是这个模式,它的特点是给定两个句子,模型判断出两个句子是否具备某种语义关系。
  • 第四类是生成式任务,比如机器翻译,文本摘要,写诗造句,看图说话等都属于这一类。它的特点是输入文本内容后,需要自主生成另外一段文字。

上图给出示例,对于句子关系类任务,很简单,和 GPT 类似,加上一个起始和终结符号,句子之间加个分隔符即可。对于输出来说,把第一个起始符号对应的 Transformer 最后一层位置上面串接一个 softmax 分类层即可。对于分类问题,与 GPT 一样,只需要增加起始和终结符号,输出部分和句子关系判断任务类似改造;对于序列标注问题,输入部分和单句分类是一样的,只需要输出部分 Transformer 最后一层每个单词对应位置都进行分类即可。从这里可以看出,上面列出的 NLP 四大任务里面,除了生成类任务外,Bert 其它都覆盖到了,而且改造起来很简单直观。

对于机器翻译或者文本摘要,聊天机器人这种生成式任务,同样可以稍作改造即可引入 Bert 的预训练成果。只需要附着在 S2S 结构上,encoder 部分是个深度 Transformer 结构,decoder 部分也是个深度 Transformer 结构。根据任务选择不同的预训练数据初始化 encoder 和 decoder 即可。这是相当直观的一种改造方法。当然,也可以更简单一点,比如直接在单个 Transformer 结构上加装隐层产生输出也是可以的。

如上图所示,句子级的分类问题只需要使用对应 [CLS] 的 C 向量,例如(a)中判断问答对是不是包含正确回答的 QNLI、判断两句话有多少相似性的 STS-B 等,它们都用于处理句子之间的关系。句子级的分类还包含(b)中判语句中断情感趋向的 SST-2 和判断语法正确性的 CoLA 任务,它们都是处理句子内部的关系。

在 SQuAD v1.1 问答数据集中,研究者将问题和包含回答的段落分别作为 A 句与 B 句,并输入到 BERT 中。通过 B 句的输出向量,模型能预测出正确答案的位置与长度。最后在命名实体识别数据集 CoNLL 中,每一个 Tok 对应的输出向量 T 都会预测它的标注是什么,例如人物或地点等。

4.5 总结

从下图可见,Bert 其实和 ELMO 及 GPT 存在千丝万缕的关系,比如如果我们把 GPT 预训练阶段换成双向语言模型,那么就得到了 Bert;而如果我们把 ELMO 的特征抽取器换成 Transformer,那么我们也会得到 Bert。所以你可以看出:Bert 最关键两点,一点是特征抽取器采用 Transformer;第二点是预训练的时候采用双向语言模型。

【参考资料】 

猜你喜欢

转载自blog.csdn.net/muumian123/article/details/84990765