浅谈ELMO、GPT和BERT模型

ELMO模型

ELMo的整体图。第一使用了多层LSTM,第二增加了后向语言模型(backward LM)。

Note: 1 输入是一个句子的embedding  E1~En,输出是T1~Tn(分别对应其上下文)。

2 从图中箭头可以看出,目标T1生成的逻辑是,第1个lstm encoding的第1个输出(即E1对应)+ 第2个lstm encoding的最后一个输出(即En->E1)[即只使用了自己和下文];目标T2生成的逻辑是,第1个lstm encoding的第1-2个输出(即E1->E1对应)+ 第2个lstm encoding的倒数第2个输出(即En->E2)[即使用了上下文]。

优化的目标:最大化对数前向和后向的似然概率:

[NAACL2018:高级词向量(ELMo)详解(超详细) 经典]

[ELMo原理解析及简单上手使用]

-柚子皮-

 

 

OpenAI GPT

模型原理与架构

原文[Improving Language Understanding by Generative Pre-Training (未出版)]

        OpenAI Transformer是一类可迁移到多种NLP任务的,基于Transformer的语言模型,其利用Transformer的结构来进行单向语言模型的训练。它的基本思想同ULMFiT相同,都是在尽量不改变模型结构的情况下将预训练的语言模型应用到各种任务。不同的是,OpenAI Transformer主张用Transformer结构,而ULMFiT中使用的是基于RNN的语言模型。

文中所用的网络结构如下:

模型的训练过程分为两步:

1. Unsupervised pre-training

        第一阶段的目标是预训练语言模型,给定tokens的语料  ,目标函数为最大化似然函数:

 

        该模型中应用multi-headed self-attention,并在之后增加position-wise的前向传播层,最后输出一个分布:

 

 

 

2. Supervised fine-tuning

        有了预训练的语言模型之后,对于有标签的训练集 C ,给定输入序列  和标签 Y,可以通过语言模型得到  ,经过输出层后对 Y 进行预测:

 

则目标函数为:

 

整个任务的目标函数为:

 

GPT模型评价

模型的优缺点

优点

  1. 循环神经网络所捕捉到的信息较少,而Transformer可以捕捉到更长范围的信息。
  2. 计算速度比循环神经网络更快,易于并行化
  3. 实验结果显示Transformer的效果比ELMo和LSTM网络更好

缺点

对于某些类型的任务需要对输入数据的结构作调整

适用任务

  • Natural Language Inference

  • Question Answering and commonsense reasoning

  • Classification
  • Semantic Similarity

-柚子皮-

 

 

BERT模型

        BERT的全称是Bidirectional Encoder Representation from Transformers,即双向Transformer的Encoder。模型的主要创新点都在pre-train方法上,即用了Masked LM和Next Sentence Prediction两种方法分别捕捉词语和句子级别的representation。作为一个Word2Vec的替代者,其在NLP领域的11个方向大幅刷新了精度,可以说是近年来自残差网络最优突破性的一项技术了。

Bert模型的主要特点:

  1. BERT的网络架构使用的是《Attention is all you need》中提出的多层Transformer结构,其最大的特点是抛弃了传统的RNN和CNN,通过Attention机制将任意位置的两个单词的距离转换成1,有效的解决了NLP中棘手的长期依赖问题,能更彻底的捕捉语句中的双向关系。
  2. 使用了Mask Language Model(MLM) [Wilson L Taylor. 1953. cloze procedure: A new tool for measuring readability.] 和 Next Sentence Prediction(NSP) 的多任务训练目标;
  3. 使用更强大的机器训练更大规模的数据,使BERT的结果达到了全新的高度,并且Google开源了训练好的多语言BERT模型代码,用户可以直接使用BERT作为Word2Vec的转换矩阵并高效的将其应用到自己的任务中。

Bert模型的模型结构

其中的“Trm”就是下图的Transformer Block(Transformer的编码器结构)。

BERT提供了简单和复杂两个模型,对应的超参数分别如下:

        在上面的超参数中,L表示网络的层数(即Transformer blocks的数量Nx),H表示隐层大小,A表示Multi-Head Attention中self-Attention的数量/heads数量,feed-forward/filter 的尺寸是4H(即H = 768时为3072)。

        BERT的本质上是通过在海量的语料的基础上运行自监督学习方法为单词学习一个好的特征表示,所谓自监督学习是指在没有人工标注的数据上运行的监督学习。在以后特定的NLP任务中,我们可以直接使用BERT的特征表示作为该任务的词嵌入特征。所以BERT提供的是一个供其它任务迁移学习的模型,该模型可以根据任务微调或者固定之后作为特征提取器。

Bert的输入

BERT的输入的编码向量(d_model=512)是3个嵌入特征的单位和,这三个词嵌入特征是:

  1. WordPiece 嵌入[Google’s neural machine translation system: Bridging the gap between human and machine translation. ]:WordPiece是指将单词划分成一组有限的公共子词单元,能在单词的有效性和字符的灵活性之间取得一个折中的平衡。例如示例中‘playing’被拆分成了‘play’和‘ing’。
  2. 位置嵌入(Position Embedding):位置嵌入是指将单词的位置信息编码成特征向量,位置嵌入是向模型中引入单词位置关系的至关重要的一环。和之前文章中的Transformer不一样,它不是三角函数,而是学习出来的。
  3. 分割嵌入(Segment Embedding):用于区分两个句子,例如B是否是A的下文(对话场景,问答场景等)。对于句子对,第一个句子的特征值是0,第二个句子的特征值是1。(lz:/segment_ids/token_type_ids。如果不使用,可以全默认为0)。

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

                               BERT的输入特征。特征是token嵌入,位置嵌入和分割嵌入的单位和

Note: 语料的选取很关键,要选用document-level的而不是sentence-level的,这样可以具备抽象连续长序列特征的能力。

预训练任务

        BERT是一个多任务模型,它的任务是由两个自监督任务组成,即MLM和NSP。《A Neural Probabilistic Language Model》这篇论文曾说,每一篇文章,天生是训练语料。

Task #1: Masked Language Model/完形填空

        Masked Language Model(MLM)和核心思想取自Wilson Taylor在1953年发表的一篇论文[cloze procedure: A new tool for measuring readability. ]。所谓MLM是指在训练的时候随即从输入预料上mask掉一些单词,然后通过的上下文预测该单词,该任务非常像我们在中学时期经常做的完形填空。正如传统的语言模型算法和RNN匹配那样,MLM的这个性质和Transformer的结构是非常匹配的。

        在BERT的实验中,15%的WordPiece Token会被随机Mask掉(而不是把像cbow一样把每个词都预测一遍),假如有 1 万篇文章,每篇文章平均有 100 个词汇,随机遮盖 15% 的词汇,模型的任务是正确地预测这 15 万个被遮盖的词汇。在训练模型时,一个句子会被多次喂到模型中用于参数学习,但是Google并没有在每次都mask掉这些单词,而是在确定要Mask掉的单词之后,80%的时候会直接替换为[Mask],10%的时候将其替换为其它任意单词,10%的时候会保留原始Token。

  • 80%: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

        这么做的原因是如果句子中的某个Token100%都会被mask掉,那么在fine-tuning的时候模型就会有一些没有见过的单词。加入随机Token的原因是因为Transformer要保持对每个输入token的分布式表征,否则模型就会记住这个[mask]是token ’hairy‘,随机词替换会给模型增加一点点噪声,但是因为此时模型不知道哪个词是被随机换了(不像[MASK],给模型[MASK]则模型知道此处词的是被挖了,他需要预测这个位置是啥),所以就迫使他去更好地保留每个词的词义,为下游任务提供方便。至于单词带来的负面影响,因为一个单词被随机替换掉的概率只有15%*10% =1.5%,这个负面影响其实是可以忽略不计的。

        另外文章指出每次只预测15%的单词,因此模型收敛的比较慢。

Note: 为什么要使用MaskLM的方式来训练语言模型?

        传统的语言模型是单向的(数学上已经定义了)。而且往往都很浅(想象一下LSTM堆三层就train不动了,就要上各种trick了),比如ELMo。与之前使用的单向语言模型进行预训练不同,BERT使用遮蔽语言模型来实现预训练的深度双向表示。举个例子:

> 今天 天气 不错, 我们 去 公园 玩 吧。

        这句话,单向语言模型在学习的时候是从左向右进行学习的,先给模型看到“今天 天气”两个词,然后告诉模型下一个要填的词是“不错”。然而单向语言模型有一个欠缺,就是模型学习的时候总是按照句子的一个方向去学的,因此模型学习每个词的时候只看到了上文,并没有看到下文。更加合理的方式应该是让模型同时通过上下文去学习,这个过程有点类似于完形填空题。例如:

>今天 天气 { }, 我们 去 公园 玩 吧。

        通过这样的学习,模型能够更好地把握“不错”这个词所出现的上下文语境。

        此前其实也有一些研究在语言模型这个任务上使用了双向的方法,例如在ELMo中是通过双向的两层RNN结构对两个方向进行建模,但两个方向的loss计算相互独立。而BERT的作者指出这种两个方向相互独立或只有单层的双向编码可能没有发挥最好的效果,我们可能不仅需要双向编码,还应该要加深网络的层数。但加深双向编码网络却会引入一个问题,导致模型最终可以间接地“窥探”到需要预测的词。这个“窥探”的过程可以用下面的图来表示:

 

        从图中可以看到经过两层的双向操作,每个位置上的输出就已经带有了原本这个位置上的词的信息了。这样的“窥探”会导致模型预测词的任务变得失去意义,因为模型已经看到每个位置上是什么词了。

ref: 《Semi-supervised sequence tagging with bidirectional language models》.

Task #2: Next Sentence Prediction

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

        对比:word2vec的一个精髓是引入了一个优雅的负采样任务来学习词向量(word-level representation),BERT使用句子级负采样任务学到句子表示。同时在句子表示上,BERT这里并没有像下游监督任务中的普遍做法一样,在encoding的基础上再搞个全局池化之类的,它首先在每个sequence(对于句子对任务来说是两个拼起来的句子,对于其他任务来说是一个句子)前面加了一个特殊的token[CLS]。然后让encoder对[CLS]进行深度encoding,深度encoding的最高隐层即为整个句子/句对的表示。这个做法乍一看有点费解,不过Transformer是可以无视空间和距离的把全局信息encoding进每个位置的,而[CLS]作为句子/句对的表示是直接跟分类器的输出层连接的,因此其作为梯度反传路径上的“关卡”,当然会想办法学习到分类相关的上层特征啦。为了让模型能够区分里面的每个词是属于“左句子”还是“右句子”,作者这里引入了“segment embedding”的概念来区分句子。

示例:

Input = [CLS] the man went to [MASK] store [SEP]

he bought a gallon [MASK] milk [SEP]

Label = IsNext

Input = [CLS] the man [MASK] to the store [SEP]

penguin [MASK] are flight ##less birds [SEP]

Label = NotNext

Note: 句子级别的任务对于阅读理解,推理等任务提升较大。

fine-turning微调

        在海量单预料上训练完BERT之后,便可以将其应用到NLP的各个任务中了。

        对于NSP任务来说,其条件概率表示为  ,其中 C 是BERT输出中的[CLS]符号, W 是可学习的权值矩阵。对于其它任务来说,我们也可以根据BERT的输出信息作出对应的预测。下图展示了BERT在11个不同任务中的模型,它们只需要在BERT的基础上再添加一个输出层便可以完成对特定任务的微调。其中Tok表示不同的Token, E 表示嵌入向量, T_i 表示第 i 个Token在经过BERT处理之后得到的特征向量。

                                                              BERT用于模型微调

微调的任务包括:

(a)基于句子对的分类任务:

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

(b)基于单个句子的分类任务

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

        对于GLUE数据集的分类任务(MNLI,QQP,QNLI,SST-B,MRPC,RTE,SST-2,CoLA),BERT的微调方法是根据[CLS]标志生成一组特征向量 C ,并通过一层全连接进行微调。损失函数根据任务类型自行设计,例如多分类的softmax或者二分类的sigmoid。

SWAG的微调方法与GLUE数据集类似,只不过其输出是四个可能选项的softmax:

 

(c)问答任务

  • SQuAD v1.1:给定一个句子(通常是一个问题)和一段描述文本,输出这个问题的答案,类似于做阅读理解的简答题。如图(c)表示的,SQuAD的输入是问题和描述文本的句子对。输出是特征向量,通过在描述文本上接一层激活函数为softmax的全连接来获得输出文本的条件概率,全连接的输出节点个数是语料中Token的个数。

 

(d)命名实体识别

  • CoNLL-2003 NER:判断一个句子中的单词是不是Person,Organization,Location,Miscellaneous或者other(无命名实体)。微调CoNLL-2003 NER时将整个句子作为输入,在每个时间片输出一个概率,并通过softmax得到这个Token的实体类别。

可以调整的参数和取值范围有:

  • Batch size: 16, 32
  • Learning rate (Adam): 5e-5, 3e-5, 2e-5
  • Number of epochs: 3, 4

因为大部分参数都和预训练时一样,精调会快一些,作者推荐多试一些参数。

Reddit上Bert作者Jacob的介绍和解释

(原帖链接)The basic idea is very simple. For several years, people have been getting very good results "pre-training" DNNs as a language model and then fine-tuning on some downstream NLP task (question answering, natural language inference, sentiment analysis, etc.).

Language models are typically left-to-right, e.g.:


 
 
   
   
   
   
  1. "the man went to a store"
  2. P(the | <s>)*P(man| <s> the)*P(went| <s> the man)*...

The problem is that for the downstream task you usually don't want a language model, you want a the best possible contextual representation of each word. If each word can only see context to its left, clearly a lot is missing. So one trick that people have done is to also train a right-to-left model, e.g.:

P(store|s>)*P(a|store s>)*...

 
 
   
   
   
   

Now you have two representations of each word, one left-to-right and one right-to-left, and you can concatenate them together for your downstream task.

But intuitively, it would be much better if we could train a single model that was deeply bidirectional.

It's unfortunately impossible to train a deep bidirectional model like a normal LM, because that would create cycles where words can indirectly "see themselves," and the predictions become trivial.

What we can do instead is the very simple trick that's used in de-noising auto-encoders, where we mask some percent of words from the input and have to reconstruct those words from context. We call this a "masked LM" but it is often called a Cloze task.

训练细节:

Task 1: Masked LM


 
 
   
   
   
   
  1. Input:
  2. the man [MASK1] to [MASK2] store
  3. Label:
  4. [MASK1] = went; [MASK2] = a

In particular, we feed the input through a deep Transformer encoder and then use the final hidden states corresponding to the masked positions to predict what word was masked, exactly like we would train a language model.通过一个深层转换Transformer编码器进行输入,随后使用对应于遮盖处的最终隐态对遮盖词进行预测,正如我们训练一个语言模型一样。

The other thing that's missing from an LM is that it doesn't understand relationships between sentences, which is important for many NLP tasks. To pre-train a sentence relationship model, we use a very simple binary classification task, which is to concatenate two sentences A and B and predict whether B actually comes after A in the original text.

Task 2: Next Sentence Prediction


 
 
   
   
   
   
  1. Input:
  2. the man went to the store [SEP] he bought a gallon of milk
  3. Label:
  4. IsNext
  5. Input:
  6. the man went to the store [SEP] penguins are flightless birds
  7. Label:
  8. NotNext

Then we just train a very big model for a lot of steps on a lot of text (we used Wikipedia + a collection of free ebooks that some NLP researchers released publicly last year). To adapt to some downstream task, you just fine-tune the model on the labels from that task for a few epochs.

By doing this we got pretty huge improvements over SOTA on every NLP task that we tried, with almost task-specific no changes to our model needed.

But for us the really amazing and unexpected result is that when we go from a big model (12 Transformer blocks, 768-hidden, 110M parameters) to a really big model (24 Transformer blocks, 1024-hidden, 340M parameters), we get huge improvements even on very small datasets (small == less than 5,000 labeled examples).

Bert模型的评价

优点

        用的是Transformer,也就是相对rnn更加高效、能捕捉更长距离的依赖。对比起之前的预训练模型,它捕捉到的是真正意义上的bidirectional context信息。

        模型有两个 loss,一个是 Masked Language Model,另一个是 Next Sentence Prediction。前者用于建模更广泛的上下文,通过 mask 来强制模型给每个词记住更多的上下文信息;后者用来建模多个句子之间的关系,强迫 [CLS] token 的顶层状态编码更多的篇章信息。文章的效果提升来自哪里?可能主要就是这两个精心设计的损失函数,尤其是前一个。

缺点

        BERT算法还有很大的优化空间,例如我们在Transformer中讲的如何让模型有捕捉Token序列关系的能力,而不是简单依靠位置嵌入。

        每个batch只有15%的token被预测,所以BERT收敛得比left-to-right模型要慢。

        BERT的成功还有一个很大的原因来自于模型的体量以及训练的数据量,但BERT的训练在目前的计算资源下很难完成。BERT训练数据采用了英文的开源语料BooksCropus 以及英文维基百科数据,一共有33亿个词。同时BERT模型的标准版本有1亿的参数量,与GPT持平,而BERT的大号版本有3亿多参数量,这应该是目前自然语言处理中最大的预训练模型了。当然,这么大的模型和这么多的数据,训练的代价也是不菲的。谷歌用了16个自己的TPU集群(一共64块TPU,一块TPU的速度约是目前主流GPU的7-8倍)来训练大号版本的BERT,一共花了4天的时间。对于是否可以复现预训练,作者在Reddit上有一个大致的回复,指出OpenAI当时训练GPT用了将近1个月的时间,而如果用同等的硬件条件来训练BERT估计需要1年的时间。不过他们会将已经训练好的模型和代码开源,方便大家训练好的模型上进行后续任务。[训练好的模型(包括简体中文)bert#pre-trained-models]

        [MASK]标记在实际预测中不会出现,训练时用过多[MASK]影响模型表现。

-柚子皮-

 

 

模型对比

        从Word2vec到AllenNLP ELMo,从OpenAI GPT到BERT的不同。Word2vec本身是一种浅层结构,而且其训练的词向量所“学习”到的语义信息受制于窗口大小;因此后续有学者提出利用可以获取长距离依赖的LSTM语言模型预训练词向量,而此种语言模型也有自身的缺陷,因为此种模型是根据句子的上文信息来预测下文的,或者根据下文来预测上文,传统的LSTM模型只学习到了单向的信息。ELMO的出现在一定程度上解决了这个问题,ELMO是一种双层双向的LSTM结构,其训练的语言模型可以学习到句子左右两边的上下文信息,但此处所谓的上下文信息并不是真正意义上的上下文。OpenAI 的GPT是利用了transform的编码器作为语言模型进行预训练的,之后特定的自然语言处理任务在其基础上进行微调即可,和LSTM相比,此种语言模型的优点是可以获得句子上下文更远距离的语言信息,但也是单向的。BERT的出现,似乎融合了它们所有的优点,并摒弃了它们的缺点,因此才可以在诸多后续特定任务上取得最优的效果。

        BERT对比AI2的 ELMo和OpenAI的fine-tune transformer的优点是只有BERT表征会基于所有层中的左右两侧语境,BERT能做到这一点得益于Transformer中Attention机制将任意位置的两个单词的距离转换成了1。区别是:

 

1 它在训练双向语言模型时以减小的概率把少量的词替成了Mask或者另一个随机的词。我个人感觉这个目的在于使模型被迫增加对上下文的记忆。至于这个概率,可能是Jacob拍脑袋随便设的。

2 增加了一个预测下一句的loss。

3 对比ELMo,虽然都是“双向”,但目标函数其实是不同的。ELMo是分别以 和  作为目标函数,独立训练处两个representation然后拼接,是一种双向预测 bi-directional;而BERT则是以  作为目标函数训练LM,称之为 deep bi-directional。

        ELMO的设置其实是最符合直觉的预训练套路,两个方向的语言模型刚好可以用来预训练一个BiLSTM,非常容易理解。但是受限于LSTM的能力,无法变深了。那如何用transformer在无标注数据行来做一个预训练模型呢?一个最容易想到的方式就是GPT的方式。BERT用了两个反直觉的手段来找到了一个“更好”的方式。(1) 用比语言模型更简单的任务来做预训练。直觉上要做更深的模型,需要设置一个比语言模型更难的任务,而BERT则选择了两个看起来更简单的任务:完形填空和句对预测。(2) 完形填空任务在直观上很难作为其它任务的预训练任务。在完形填空任务中,需要mask掉一些词,这样预训练出来的模型是有缺陷的,因为在其它任务中不能mask掉这些词。而BERT通过随机的方式来解决了这个缺陷:80%加Mask,10%用其它词随机替换,10%保留原词。这样模型就具备了迁移能力。

from: https://blog.csdn.net/pipisorry

ref: [如何评价 BERT 模型?]*

[BERT详解]*

[【NLP】Google BERT详解]

[从Word Embedding到Bert模型—自然语言处理中的预训练技术发展史]

[自然语言处理中的语言模型预训练方法]

 

你可能感兴趣的:(机器学习,NLP)