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

2018/12/21 NLP

针对本文的github地址:https://github.com/JepsonWong/Pre-training_Techniques_For_NLP

这篇文章主要讲基于语言模型的词向量,其实词向量还有基于统计方法的(例如:基于共现矩阵、SVD)。

1 词向量技术-从word2vec到ELMo

下游任务如何使用Word Embedding

下游NLP任务在使用Word Embedding的时候也类似图像有两种做法,一种是Frozen,就是Word Embedding那层网络参数固定不动;另外一种是Fine-Tuning,就是Word Embedding这层参数使用新的训练集合训练也需要跟着训练过程更新掉。

语言模型(神经网络语言模型NNLM)

2003年Bengio提出。

学习任务是输出入某个句中某个单词w前面的t-1个单词,要求网络正确预测单词w,单词的word embedding是这个模型的副产物。

语言模型(Word2Vec/Glove)

Word2Vec网络结构和NNLM类似,但尽管网络结构相近,而且也是做语言模型任务,但是其训练方法不太一样。Word2Vec有两种训练方法,一种叫CBOW,核心思想是从一个句子里面把一个词抠掉,用这个词的上文和下文去预测被抠掉的这个词;第二种叫做Skip-gram,和CBOW正好反过来,输入某个单词,要求网络预测它的上下文单词。而你回头看看,NNLM是怎么训练的?是输入一个单词的上文,去预测这个单词。这是有显著差异的。为什么Word2Vec这么处理?原因很简单,因为Word2Vec和NNLM不一样,NNLM的主要任务是要学习一个解决语言模型任务的网络结构,语言模型就是要看到上文预测下文,而word embedding只是无心插柳的一个副产品。但是Word2Vec目标不一样,它单纯就是要word embedding的,这是主产品,所以它完全可以随性地这么去训练网络。

Word2Vec工具主要包含两个模型:跳字模型(skip-gram)和连续词袋模型(continuous bag of words,简称CBOW),以及两种近似训练法:负采样(negative sampling)和层序softmax(hierarchical softmax)。

Word2Vec得到以该词作为背景词和中心词的两组词向量。我们会使用连续词袋模型的背景词向量,使用跳字模型的中心词向量。来自链接[1]。

TagLM

讲的不错 Semi-supervised sequence tagging with bidirectional language models

2017年ACL会议提出,做命名实体识别任务。和ELMo是同一个作者。

利用语言模型来训练,分为Forward LM和Backward LM;输入均为一行文本,然后接Embedding层,接着接LM层(LM层是rnn或者lstm实现,可以为单向也可以为双向;Forward LM层输入为一个正向文本序列,预测下一个词;Backward LM输入为反向的文本序列,预测也为下一个词;也就是说这连两个输入序列顺序不一样,但预测结果是一样),然后两个LM层接一个Dense层(将Embedding转化为len(单词)的预测结果)。

图示如下,图中LM画的只是单向rnn,其实可以实现成单向和双向。

实现:

Python包 可以使用提供的接口

上述Python包的源代码

使用:

要使用TagLM生成的词向量,首先在大型语料上训练好TagLM模型,然后给定每一行输入,跑LM模型得到其两个LM模型隐藏层的输出组成词向量,每个LM模型得到对应位置词的一个一个词向量

TagLM的例子 用bi-lm做序列问题

ULFMiT

没仔细看

ULMFiT使用的是三阶段模式,在通用语言模型训练之后,加入了一个领域语言模型预训练过程,而且论文重点工作在这块,方法还相对比较繁杂,这并不是一个特别好的主意,因为领域语言模型的限制是它的规模往往不可能特别大,精力放在这里不太合适,放在通用语言模型上感觉更合理;再者,尽管ULFMiT实验做了6个任务,但是都集中在分类问题相对比较窄,不如ELMO验证的问题领域广,我觉得这就是因为第二步那个领域语言模型带来的限制。

参考:https://zhuanlan.zhihu.com/p/53666333

ULMFiT_Text_Classification

Classification

ELMo(Embedding from Language Models)

Word Embedding存在多义词问题。多义词对Word Embedding来说有什么负面影响?比如一个多义词w有两个常用含义,但是Word Embedding在对w这个单词进行编码的时候,是区分不开这两个含义的,因为它们尽管上下文环境中出现的单词不同,但是在用语言模型训练的时候,不论什么上下文的句子经过word2vec,都是预测相同的单词w,而同一个单词占的是同一行的参数空间,这导致两种不同的上下文信息都会编码到相同的word embedding空间里去。所以word embedding无法区分多义词的不同语义,这就是它的一个比较严重的问题。

ELMO提供了一种简洁优雅的解决方案。提出ELMO的论文题目:“Deep contextualized word representation”体现了其精髓,而精髓在哪里?在deep contextualized这个短语,一个是deep,一个是context,其中context更关键。在此之前的Word Embedding本质上是个静态的方式,所谓静态指的是训练好之后每个单词的表达就固定住了,以后使用的时候,不论新句子上下文单词是什么,这个单词的Word Embedding不会跟着上下文场景的变化而改变,所以对于一个多义词,它事先学好的Word Embedding中混合了几种语义 ,在应用中来了个新句子,即使从上下文中明显可以看出它代表的是某种确定的含义,但是对应的Word Embedding内容也不会变,它还是混合了多种语义。这是为何说它是静态的,这也是问题所在。ELMO的本质思想是:我事先用语言模型学好一个单词的Word Embedding,此时多义词无法区分,不过这没关系。在我实际使用Word Embedding的时候,单词已经具备了特定的上下文了,这个时候我可以根据上下文单词的语义去调整单词的Word Embedding表示,这样经过调整后的Word Embedding更能表达在这个上下文中的具体含义,自然也就解决了多义词的问题了。所以ELMO本身是个根据当前上下文对Word Embedding动态调整的思路。

学习任务是根据单词w的上下文去预测单词,单词w之前的单词序列Context-before称为上文,之后的单词序列Context-after称为下文。分别有两个编码器,分别是正方向编码器和反方向编码器,均为双层LSTM结构;正方向编码器输入的是从左到右顺序的除了预测单词外的上文Context-before和下文Context-after,反方向编码器输入的是从右到左的逆序的句子上文和下文;

开源项目ELMo:机器学习在自动翻译中的应用

ELMo 最好用的词向量《Deep Contextualized Word Representations》

ELMo的方法,可以看作是TagLM的一个改进版。首先,ELMo不局限于sequence labeling,而是作为一个一般性的词向量表示方法;其次,ELMo不仅仅使用了neural language model 的最后一层的输出,而是对所有层的输出做了加权平均来构造最后的向量

公式中的s是由softmax算出来的(加起来是1),gamma是一个需要学习的变量,加不加这个变量对performance的影响是比较大的,这两个变量都是和具体的任务相关

通过这样的迁移策略,那些对词义消歧有需求的任务就更容易通过训练给第二隐层一个很大的权重,而对词性、句法有明显需求的任务则可能对第一隐层的参数学习到比较大的值(实验结论)。总之,这样便得到了一份“可以被下游任务定制”的特征更为丰富的词向量,效果比word2vec好得多也就不足为奇了。

缺点,在GPT和Bert出来之后对比发现

  • LSTM抽取特征的能力远弱于Transformer
  • 拼接方式双向融合特征融合能力偏弱

ELMo项目主页

ElMo项目 github pytorch

ELMo词向量 tennsorflow实现

里面有用法 NAACL2018 一种新的embedding方法Deep contextualized word representations ELMo原理与用法

ELMo的总结 主要是实战,可以用中文,中文词向量在下方 流水账Elmo词向量中文训练过程杂记

ELMo预训练的多国语言模型

A standalone ELMo package github(很好)(没看)

ELMo_Chin (没看)

一些说明

上述ELMo的目标也仅仅是学习到上下文相关的、更强大的词向量,其目的依然是为下游任务提供一个扎实的根基,还没有想要弑君称王的意思。

而我们知道,仅仅是对文本进行充分而强大的encoding(即得到每个词位非常精准丰富的特征)是远不够覆盖所有NLP任务的。在QA、机器阅读理解(MRC)、自然语言推理(NLI)、对话等任务中,还有很多更复杂的模式需要捕捉,比如句间关系。为此,下游任务中的网络会加入各种花式attention(参考NLI、MRC、Chatbot中的SOTA们)。

而随着捕捉更多神奇模式的需要,研究者们为每个下游任务定制出各种各样的网络结构,导致同一个模型,稍微一换任务就挂掉了,甚至在同一个任务的情况下换另一种分布的数据集都会出现显著的性能损失,这显然不符合人类的语言行为呀。要知道人类的generalization能力是非常强的,这就说明,或许现在整个NLP的发展轨迹就是错的,尤其是在SQuAD的带领下,穷尽各种trick和花式结构去刷榜,真正之于NLP的意义多大呢?

不过所幸,这条越走越偏的道路终于被一个模型shutdown了,那就是Google发布的Bidirectional Encoder Representations from Transformers (BERT)(GPT其实和它原理差不多)。

这两篇paper的最重要意义不在于用了什么模型,也不在于怎么训练的,而是它提出一种全新的游戏规则

像之前说的,为每个NLP任务去深度定制泛化能力极差的复杂模型结构其实是非常不明智的,走偏了方向的。既然ELMo相比word2vec会有这么大的提升,这就说明预训练模型的潜力远不止为下游任务提供一份精准的词向量,所以我们可不可以直接预训练一个龙骨级的模型呢?如果它里面已经充分的描述了字符级、词级、句子级甚至句间关系的特征,那么在不同的NLP任务中,只需要去为任务定制一个非常轻量级的输出层(比如一个单层MLP)就好了,毕竟模型骨架都已经做好了。

而这两篇paper正是做了这件事情,或者说,它真的把这件事情做成了,它作为一个general的龙骨级模型轻松的挑战了11个任务上的深度定制的模型。

也就是说,然后在具体NLP任务有监督微调时,与ELMo当成特征的做法不同,OpenAI GPT不需要再重新对任务构建新的模型结构,而是直接在Transformer这个语言模型上的最后一层接上softmax作为任务输出层,然后再对这整个模型进行微调。他们还发现,如果使用语言模型作为辅助任务,能够提升有监督模型的泛化能力,并且能够加速收敛。

GPT

GPT是“Generative Pre-Training”的简称,从名字看其含义是指的生成式的预训练。GPT也采用两阶段过程,第一个阶段是利用语言模型进行预训练,第二阶段通过Fine-tuning的模式解决下游任务

论文研读之OpenAI-Generative Pre-Training

利用语言模型来进行训练,即给定前面的单词预测下一个单词

GPT和ELMo是类似的,主要不同在于两点:

  • 特征抽取器不是用的RNN,而是用的Transformer,上面提到过它的特征抽取能力要强于RNN,这个选择很明显是很明智的。
  • GPT的预训练虽然仍然是以语言模型作为目标任务,但是采用的是单向的语言模型,所谓“单向”的含义是指:语言模型训练的任务目标是根据w单词的上下文去正确预测单词w,w之前的单词序列Context-before称为上文,之后的单词序列Context-after称为下文。ELMo在做语言模型预训练的时候,预测单词w同时使用了上文和下文,而GPT则只采用Context-before这个单词的上文来进行预测,而抛开了下文。这个选择现在看不是个太好的选择,原因很简单,它没有把单词的下文融合进来,这限制了其在更多应用场景的效果,比如阅读理解这种任务,在做任务的时候是可以允许同时看到上文和下文一起做决策的。如果预训练时候不把单词的下文嵌入到Word Embedding中,是很吃亏的,白白丢掉了很多信息。

由Attention看OpenAI的网红GPT

BERT

(没看)BERT源码理解

(原理)BERT – State of the Art Language Model for NLP

(原理)5 分钟入门 Google 最强NLP模型:BERT

Dissecting BERT Part 1: The Encoder

Understanding BERT Part 2: BERT Specifics

Dissecting BERT Appendix: The Decoder

BERT模型(没看)

使用超多层Transformer + 双任务预训练 + 后期微调的训练策略。

BERT是使用Transformer的编码器(GPT被认为用的是Transformer的解码器)来作为语言模型,在语言模型预训练的时候,提出了两个新的目标任务(即遮挡语言模型MLM和预测下一个句子的任务)。

BERT模型的训练分为预训练(Pre-training)和微调(Pre-training)两步。微调取决于下游的具体任务。不同的下游任务意味着不同的网络扩展结构:比如一个对句子进行情感分类的任务,只需要在BERT的输出层句向量上接入几个Dense层,走个softmax。而对于SQuAD上的阅读理解任务,需要对BERT输出的词向量增加match层和softmax。

总体来说,对BERT的微调是一个轻量级任务,微调主要调整的是扩展网络而非BERT本身。换句话说,我们完全可以固定住BERT的参数,把BERT输出的向量编码当做一个特征(feature)信息,用于各种下游任务。

bert pytorch复现

BERT相关论文、文章和代码资源汇总

google开源代码

PyTorch版本BERT

Chainer版本BERT

Keras版: Keras implementation of BERT with pre-trained weights

bert_language_understanding github

google预训练好了BERT-Base、Multilingual和BERT-Base、Chinese在内的任意模型。

BERT预训练模型

实践部分

官方实践部分包括微调预训练BERT、通过预训练BERT抽取语义特征(可以使用脚本extract_features.py抽取语义特征)。下面链接有涉及到:

(原理讲的不错)谷歌终于开源BERT代码:3 亿参数量,机器之心全面解读

小数据福音!BERT在极小数据下带来显著提升的开源实现

【NLP】BERT中文实战踩坑(没看)

bert-Chinese-classification-task(没看)

使用预训练语言模型BERT做中文NER(没看)

BERT实战(源码分析+踩坑)(推荐)

我们可以用bert-as-service生成句向量和ELMo词向量等:

两行代码玩转 Google BERT 句向量词向量

bert-as-service github项目(Mapping a variable-length sentence to a fixed-length vector using BERT model)

下载那个中文的pretrain模型后,怎么能取出embeddings,如何评估一下这个pretrain出来的embeddings?

(详细讲解了run_classifier.py文件的结构)干货 BERT fine-tune 终极实践教程

三个embedding的tensor名字为:

  • bert/embeddings/word_embeddings:0
  • bert/embeddings/token_type_embeddings:0
  • bert/embeddings/position_embeddings:0

可以在载入后用如下的方式获取,通过sess.run()打印或者写入文件embedding_variables = [ var for var in tf.global_variables() if var.name in embedding_name_list]

一些trick

1.如何理解ptr-training和fine-tune的mismatching。就是为什么不能100%用MASK代替,而要用10%的random token和10%的原token?

不全用MASK是因为在finetune到下游任务的时候(例如POS Tagging)所有词都是已知的,如果模型只在带MASK的句子上预训练过,那么模型就只知道根据其他词的信息来预测当前词,而不会直接利用这个词本身的信息,会凭空损失一部分信息,对下游任务不利。还有10%random token是因为如果都用原token,模型在预训练时可能会偷懒,不去建模单词间的依赖关系,直接照抄当前词。

2.Transformer中encoder的self-attention本身就是双向的,只是利用mask的trick来训练语言模型。

ELMo、GPT、BERT对比

对比一下三种语言模型结构。

BERT使用的是Transformer编码器,由于self-attention机制,所以模型上下层直接全部互相连接的。而OpenAI GPT使用的是Transformer解码器,它是一个需要从左到右的受限制的Transformer,而ELMo使用的是双向LSTM,虽然是双向的,但是也只是在两个单向的LSTM的最高层进行简单的拼接。所以只有BERT是真正在模型所有层中是双向的

在模型的输入方面,BERT做了更多的细节。他们使用了WordPiece embedding作为词向量,并加入了位置向量和句子切分向量。此外,作者还在每一个文本输入前加入了一个CLS向量,后面会有这个向量作为具体的分类向量。

在语言模型预训练上,他们不再使用标准的从左到右预测下一个词作为目标任务,而是提出了两个新的任务。第一个任务他们称为MLM,即在输入的词序列中,随机的挡上15%的词,然后任务就是去预测挡上的这些词,可以看到相比传统的语言模型预测目标函数,MLM可以从任何方向去预测这些挡上的词,而不仅仅是单向的。

BERT实战

代码地址:https://github.com/google-research/bert

BERT中文实战(文本相似度)

干货 BERT fine-tune 终极实践教程

BERT中文文本相似度计算与文本分类

重点介绍fine-tuning过程。

代码通过tensorflow高级API—— tf.estimator进行封装(wrapper)的。因此对于不同数据集的适配,只需要修改代码中的processor部分,就能进行代码的训练、交叉验证和测试。

预训练的入口是在run_pretraining.py而fine-tune的入口针对不同的任务分别在run_classifier.py和run_squad.py。其中run_classifier.py适用的任务为分类任务。如CoLA、MRPC、MultiNLI这些数据集。而run_squad.py适用的是阅读理解(MRC)任务,如squad2.0和squad1.1。

run_classfier.py:

data_dir:指的是我们的输入数据的文件夹路径。输入数据格式在InputExample类中定义,它要求的输入分别是guid, text_a, text_b, label,其中text_b和label为可选参数。例如我们要做的是单个句子的分类任务,那么就不需要输入text_b;另外,在test样本中,我们便不需要输入lable。

task_name:用来选择processor的。

processor:任何模型的训练、预测都是需要有一个明确的输入,而BERT代码中processor就是负责对模型的输入进行处理。自定义的processor里需要继承DataProcessor,并重载获取label的get_labels和获取单个输入的get_train_examples,get_dev_examples和get_test_examples函数。其分别会在main函数的FLAGS.do_train、FLAGS.do_eval和FLAGS.do_predict阶段被调用。这三个函数的内容是相差无几的,区别只在于需要指定各自读入文件的地址。

process在得到字符串形式的输入后,在file_based_convert_examples_to_features里先是对字符串长度,加入[CLS]和[SEP]等一些处理后,将其写入成TFrecord的形式。这是为了能在estimator里有一个更为高效和简易的读入。

在create_model的函数里,除了从modeling.py获取模型主干输出之外,还有进行fine-tune时候的loss计算。因此,如果对于fine-tune的结构有自定义的要求,可以在这部分对代码进行修改。如进行NER任务的时候,可以按照BERT论文里的方式,不只读第一位的logits,而是将每一位logits进行读取。

运行命令:

训练:

python run_classifier.py \
  --data_dir=$MY_DATASET \
  --task_name=sim \
  --vocab_file=$BERT_BASE_DIR/vocab.txt \
  --bert_config_file=$BERT_BASE_DIR/bert_config.json \
  --output_dir=/tmp/sim_model/ \
  --do_train=true \
  --do_eval=true \
  --init_checkpoint=$BERT_BASE_DIR/bert_model.ckpt \
  --max_seq_length=128 \
  --train_batch_size=32 \
  --learning_rate=5e-5\
  --num_train_epochs=2.0 

预测:

python run_classifier.py \
  --task_name=sim \
  --do_predict=true \
  --data_dir=$MY_DATASET \
  --vocab_file=$BERT_BASE_DIR/vocab.txt \
  --bert_config_file=$BERT_BASE_DIR/bert_config.json \
  --init_checkpoint=/tmp/sim_model \
  --max_seq_length=128 \
  --output_dir=/tmp/output/
当然,我们需要在data_dir下有测试数据,测试完成后会在output_dir路径下生成一个test_results.tsv文件,该文件包含了测试用例和相似度probabilities

MT-DNN

GLUE排行榜上全面超越BERT的模型近日公布了!

研究人员认为MTL和语言模型预训练是互补的技术,因此可以结合起来改进文本表示的学习,从而提高各种NLU任务的性能。他们将2015年做的一项多任务深度神经网络(MT-DNN)模型加以拓展,将BERT合并到模型的共享文本编码层。

较低层(即文本编码层)在所有任务中共享,而顶层是任务特定的,组合不同类型的NLU任务,如单句分类、成对文本分类、文本相似性和相关性排序。与BERT模型类似,MT-DNN分两个阶段进行训练:预训练和微调。与BERT不同的是,MT-DNN在微调阶段使用MTL,在其模型架构中具有多个任务特定层。

XLM: 跨语言版的BERT

跨语言版BERT:Facebook提出跨语言预训练模型XLM

预训练语言模型:

  • 因果语言模型(CLM)—单语言
  • BERT 中通过掩码训练的语言模型(MLM)—单语言
  • 翻译语言模型(TLM)—跨语言

通过XNLI-15模型生成跨语言句子表征

跨语言版BERT:Facebook提出跨语言预训练模型XLM

参考

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

BLMo迁移学习的理解不错 NLP的游戏规则从此改写?从word2vec, ELMo到BERT

预训练在自然语言处理的发展: 从Word Embedding到BERT模型 ppt

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

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

Search

    Table of Contents