本文转载自 知乎专栏 智能对话机器人技术
上一篇文章提到了对迁移学习和模型训练的思考,及对未来工作方向的粗略预测,本篇将对各类代表性工作做具体介绍。
套用下前面对迁移学习分类的方式,把接下来要介绍的具体模型放到对应的模块里,这样逻辑会更清楚一些。
有监督模型预训练:CoVe
CoVe是在 McCann et al., Learned in Translation: Contextualized Word Vectors 这个论文中提出的。自然语言中的一词多义非常常见,比如“苹果手机”和“苹果香蕉”里的“苹果”,含义明显不同。以Word2Vec为代表的词表示方法没法依据词所在的当前背景调整表示向量。所以NLPer一直在尝试找背景相关的词表示法(Contextualized Word Representation)。CoVe就是这方面的一个尝试。
CoVe首先在翻译标注数据上预训练encoder2decoder模型。其中的encoder模块使用的是BiLSTM。训练好的encoder,就可以作为特征抽取器,获得任意句子中每个token的带背景词向量:
使用的时候,只要把 和 拼接起来就行。
论文作者在分类和匹配下游任务对CoVe的效果做过验证,效果肯定是有一些提升了,但提升也不是很明显。
总结下CoVe的特点:
预训练依赖于有监督数据(翻译数据)。
CoVe结果以特征抽取的方式融合到下游任务模型中,但下游任务还是要自定义对应的模型。
自监督学习同时训练:CVT
CVT (Cross-View Training)在利用有监督数据训练特定任务模型时,同时会使用无监督数据做自监督学习。Encoder使用的是2层的CNN-BiLSTM,训练过程使用标注数据和非标注数据交替训练。利用标注数据训练主预测模块,同时构造多个辅助模块,辅助模块利用非标注数据拟合主模块的预测概率。辅助模块的输入仅包含所有输入中的部分信息,这个思想和dropout有点像,可以提高模型的稳定性。不同的特定任务,辅助模块的构造方式不同,如何选输入中部分信息的方式也不同。
例如,对于序列标注任务,论文中以biLSTM第一层和第二层的状态向量拼接后输入进主预测模块。而4个辅助模块则使用了第一层的各个单向状态向量作为输入。 使用的是第一层前向LSTM当前词的状态向量, 使用的是第一层后向LSTM当前词的状态向量。 使用的是第一层前向LSTM前一个词的状态向量,而 使用的是第一层后向LSTM后一个词的状态向量。
作者也在多任务学习上验证了CVT带来效果提升。CVT使用多个标注数据和非标注数据交替训练。使用标注数据训练时,CVT随机选择一个任务,优化对应任务的主模块目标函数。使用非标注数据训练时,CVT为所有任务产生对应的辅助模块。这些辅助模块同时被训练,相当于构造了一些所有任务共用的标注数据。这种共用的训练数据能提升模型收敛速度。作者认为效果提升的主要原因是,同时训练多个任务能降低模型训练一个任务时遗忘其他任务知识的风险。
总结下CVT的特点:
在训练特定任务模型时加入无监督数据做自监督学习,获得了精度的提升。其中辅助模块的构建是关键。
嗯,需要为不同任务定制不同的辅助模块。
应用于MTL问题效果比ELMo好。
无监督模型预训练
ELMo
ELMo (Embedding from Language Models) 的目的是找到一种带背景的词向量表示方法,以期在不同的背景下每个词能够获得更准确的表示向量。
ELMo的使用过程分为以下三个步骤:
第一步是预训练阶段,ELMo利用2层的biLSTM和无监督数据训练两个单向的语言模型,它们统称为biLM。
第二步利用特定任务的数据精调第一步得到的biLM。作者发现这步能显著降低biLM在特定任务数据上的PPL,结果如下图。但对特定任务最终的任务精度未必有帮助(但也不会降低任务精度)。作者发现在SNLI(推断)任务上效果有提升,但在SST-5(情感分析)任务上效果没变化。
第三步是训练特定的任务模型。任务模型的输入是上面已训练biLM的各层状态向量的组合向量。
其中 是经过softmax归一化后的权重, 是整体的scale参数。它们都是任务模型中待学习的参数。
可以以额外特征的方式,加入到特定任务的输入和输出特征中。作者发现,对于某些任务,把 同时加入到输入和输出特征中效果最好,具体见下图。
作者发现,biLM底层LSTM的输出状态对句法任务(如POS)更有帮助,而高层LSTM的输出状态对语义任务(如WSD)更有帮助。ELMo对(标注)数据量少的有监督任务精度提升较大,对数据量多的任务效果提升就不明显了。这说明ELMo里存储的信息比较少,还是它主要功能是帮助有监督数据更好地提炼出其中的信息?
总结下ELMo的特点:
把无监督预训练技术成功应用于多类任务,获得了效果提升。
ELMo以特征抽取的形式融入到下游任务中,所以不同下游任务依旧需要使用不同的对应模型。
ELMo改进的效果依赖于下游任务和对应的模型,且改进效果也不是特别大。
ULMFiT & SiATL
ULMFiT (Universal Language Model Fine-tuning) 使用和ELMo类似的流程:
使用通用数据预训练LM,模型使用了3层的AWD-LSTM。
在特定任务数据上精调LM,其中使用到差异精调和倾斜三角lr两个策略。
以LM作为初始值,精调特定任务分类模型,其中使用到逐层解冻、差异精调和倾斜三角lr三个策略。经过AWD-LSTM之后,输出给分类器的向量为三个向量的拼接: 。
最后一层最后一个词对应的向量;
最后一层每个词向量做max pooling;
最后一层每个词向量做mean pooling。
论文中提出了几个优化策略,能够提升精调后模型的最终效果。
论文中的实验主要针对各种分类任务,相比于之前最好的结果,ULMFiT把分类错误率降低了18-24%。
论文中也设计了实验来说明流程中第二步(在特定任务数据上精调LM)的作用。结果表明第二步的加入,能够让第三步的分类任务在很少的数据量下获得好的结果。只要使用 1%~10%
的标注数据,就能达到不加第二步时的模型效果。
作者也设计了去除实验验证论文中提出的三个策略的效果:差异精调(discr)、倾斜三角lr(stlr)、逐层解冻(Freez)。结果表明相比于其他人提出的策略,这几个策略能获得更好的结果。而且,相比于不使用discr和stlr机制的精调策略(Full),ULMFiT模型更稳定,没出现灾难性遗忘。
之后的另一篇论文 An Embarrassingly Simple Approach for Transfer Learning from Pretrained Language Models 建议了一些新的策略,解决精调时的灾难性遗忘问题。模型称为 SiATL (Single-step Auxiliary loss Transfer Learning)。SiATL只包含两个步骤:无监督数据预训练LM、精调分类模型。但在精调分类模型时,SiATL把LM作为辅助目标加入到优化目标函数当中。SiATL的第二步相当于把ULMFiT的第二步和第三步一起做了。所以它们的流程其实是一样的。
预训练模型使用的是两层LSTM+Linear,而分类模型在预训练模型的上面增加了一层带self-attention的LSTM和输出层。SiATL建议的几个策略:
论文发现辅助LM目标对于小数据集更有用,可能是辅助LM减轻了小数据集上的过拟合问题。其中的系数 ,论文实验发现初始取值为 0.2
,然后指数下降到 0.1
效果最好。 的取值需要考虑到 和 的取值范围。这个结论和ULMFiT中验证第二步流程作用的实验结果相同,也侧面说明了它们本质上差不多。
另一个发现是如果预训练用的无监督数据和任务数据所在领域不同,序贯解冻带来的效果更明显。这也是合理的,领域不同说明灾难性遗忘问题会更严重,所以迁移知识时要更加慎重,迁移过程要更慢。序贯解冻主要就是用途就是减轻灾难性遗忘问题。
论文还发现,和ULMFiT相比,SiATL在大数据集上效果差不多,但在小数据集要好很多。
总结下 ULMFiT 和 SiATL:
ULMFiT使用序贯训练的方式组合特定任务LM和任务目标函数,而SiATL使用同时训练的方式,也即加入辅助LM目标函数。
它们建议的策略都是在解决灾难性遗忘问题,也都解决的不错。可以考虑组合使用这些策略。
它们在小数据集上都提升明显,只要使用 1%~10% 的标注数据,就能达到之前的效果。
虽然它们只在分类任务上验证了各自的效果,但这些策略应该可以推广到其他任务上。
GPT/GPT-2
前面介绍的工作中预训练模型用的都是多层LSTM,而OpenAI GPT首次使用了Transformer作为LM预训练模型。GPT使用12层的Transformer Decoder训练单向LM,也即mask掉当前和后面的词。
在做精调时,使用最高层最后一个词的向量作为后续任务的输入,类似SiATL也加入了辅助LM目标函数。
GPT的另一个大贡献是为下游任务引入了统一的模型框架,也即不再需要为特定任务定制复杂的模型结构了。不同的任务只需把输入数据做简单的转换即可。
GPT在多种类型的任务上做了实验,12个任务中的9个任务有提升,最高提升幅度在9%左右,效果相当不错。
针对预训练、辅助LM和Transformer,论文中做了去除实验,结果表明预训练最重要,去掉会导致指标下降14.8%,而Transformer改为LSTM也会导致指标下降5.6%。比较诡异的是去掉辅助LM的实验结果。去掉辅助LM,只在QQP (Quora Question Pairs)和NLI上导致指标下降。在其他任务上反而提升了指标。作者观察到的趋势是辅助LM对于大的数据集比小的数据集更有帮助。。这也跟ULMFiT和SiATL中的结论相反。
总结下GPT的主要贡献:
验证了Transformer在Unsupervised Pretraining中的有效性。
验证了更大的模型效果更好: 6 --> 12 层。
为下游任务引入了通用的求解框架,不再为任务做模型定制。
之后OpenAI又训练一个更大的模型,叫GPT-2。GPT-2把GPT中12层的Transformer提升到48层,参数数量是GPT的十几倍,达到了15亿。
GPT-2依旧使用单向LM训练语言模型,但使用数量更多、质量更好、覆盖面更广的数据进行训练。而且,GPT-2没有针对特定模型的精调流程了。作者想强调的是,预训练模型中已经包含很多特定任务所需的信息了,只要想办法把它们取出来直接用即可,可以不用为特定任务标注数据,真正达到通用模型的能力。
那,没有精调如何做特定任务呢?一些任务说明如下:
不做精调的GPT-2不仅在很多特定任务上已经达到了SOTA,还在生成任务上达到了吓人的精度。
BERT
和GPT一样,BERT的基本模型使用了Transformer,只是模型又变大了(12层变成了24层)。
相比于GPT的单向LM,BERT使用了双向LM。但显然预测时不能让待预测的词看到自己,所以需要把待预测词mask掉。BERT建议了masked LM机制,即随机mask输入中的 k%
个词,然后利用双向LM预测这些词。
但mask时需要把握好度。mask太少的话,训练时每次目标函数中包含的词太少,训练起来就要迭代很多步。mask太多的话,又会导致背景信息丢失很多,与预测时的情景不符。而且,简单的mask会带来预训练和精调训练的不一致性:精调阶段,输入数据里是不mask词的。
BERT建议了以下的策略,解决这些问题:
BERT的另一大贡献,是引入了新的预训练目标 Next Sentence Prediction (NSP) 。对于两个句子A和B,NSP预测B是不是A的下一个句子。训练时NSP的正样本就是从文档从随机选的两个临近句子,而负样本就是B是随机从文档中选取的,与A的位置没关系。NSP可以学习句子与句子间的关系。
预训练的目标函数是Masked LM和NSP的加和。
BERT的输入词向量是三个向量之和:
Token Embedding:WordPiece tokenization subword词向量。
Segment Embedding:表明这个词属于哪个句子(NSP需要两个句子)。
Position Embedding:学习出来的embedding向量。这与Transformer不同,Transformer中是预先设定好的值。
BERT也为下游任务引入了通用的求解框架,不再为任务做模型定制。对于分类和匹配任务,下游任务只要使用第一个词 [CLS]
对应的最上层输出词向量作为分类器的输入向量即可。对于抽取式QA和序列标注问题,使用每个词对应的最上层输出词向量作为下游任务的输入即可。
BERT的惊艳结果,引爆了NLP行业。BERT在11个任务上获得了最好效果,GLUE上达到了80.4%,提升了整整7.6个点,把SQuAD v1.1 F1又往上提升了1.5个点,达到了93.2 。
BERT的去除实验表明,双向LM和NSP带了的提升最大。
另一个结论是,增加模型参数数量可以提升模型效果。
BERT预训练模型的输出结果,无非就是一个或多个向量。下游任务可以通过精调(改变预训练模型参数)或者特征抽取(不改变预训练模型参数,只是把预训练模型的输出作为特征输入到下游任务)两种方式进行使用。BERT原论文使用了精调方式,但也尝试了特征抽取方式的效果,比如在NER任务上,最好的特征抽取方式只比精调差一点点。但特征抽取方式的好处可以预先计算好所需的向量,存下来就可重复使用,极大提升下游任务模型训练的速度。
后来也有其他人针对ELMo和BERT比较了这两种使用方式的精度差异。下面列出基本结论:
总结下BERT的主要贡献:
引入了Masked LM,使用双向LM做模型预训练。
为预训练引入了新目标NSP,它可以学习句子与句子间的关系。
进一步验证了更大的模型效果更好: 12 --> 24 层。
为下游任务引入了很通用的求解框架,不再为任务做模型定制。
刷新了多项NLP任务的记录,引爆了NLP无监督预训练技术。
MASS
BERT只能做NLU类型的任务,无法直接用于文本产生式(NLG)类型的任务,如摘要、翻译、对话生成。NLG的基本框架是encoder2decoder,微软的MASS (MAsked Sequence to Sequence pre-training)把BERT推广到NLG任务。MASS的结构如下,它的训练数据依旧是单句话,但是会随机mask这句话中连续的 k
个词,然后把这些词放入decoder模块的相同位置,而encoder中只保留未被mask的词。MASS期望decoder利用encoder的信息和decoder前面的词,预测这些被mask的词。
比较有意思的是,BERT和GPT都是MASS的特例。当 k=1
时,也即随机mask单个词时,MASS就退化成BERT;当 k=句子长度
时,也即mask所有词时,MASS就退化成GPT,或者标准的单向LM。
论文中使用了4层的Transformer作为encoder和decoder,跟encoder使用BERT,decoder
使用标准单向LM的框架BERT+LM做了效果对比,PPL上降低了不少。而且作者也对比了 k
取不同值时的效果变化,结果发现在多个任务上它取50%句子长度都是最优的。
为什么MASS能取得比较好的效果?作者给出了以下解释:
Encoder中mask部分tokens,迫使它理解unmasked tokens。
Decoder中需要预测masked的连续tokens,让decoder能获取更多的语言信息。
Decoder中只保留了masked的tokens,而不是所有的tokens,迫使decoder也会尽量从encoder中抽取信息。
作者也做了两个去除实验验证上面的后两条解释。
总结下MASS的特点:
把BERT推广到NLG类型任务,并且统一了BERT和传统单向LM框架。
实验表明MASS效果比BERT+LM好,但实验使用的模型太小,不确定这种优势在模型变大后是否还会存在。
UNILM
UNILM (UNIfied pretrained Language Model)是微软另一波人最近放出的论文。UNILM同时训练BERT中的双向LM、GPT中的单向LM和seq2seq中的LM。用的方法也很自然,核心思想在Transformer那篇论文中其实就已经在用了。
UNILM中的核心框架还是Transformer,只是用无监督数据预训练模型时,同时以双向LM、单向LM和seq2seq LM为目标函数。这些目标函数共享一个Transformer结构,训练也都使用了类似BERT中的 [MASK]
机制。
和BERT的双向LM不同的是,单向LM在做self-attention时不能使用这个词后面的词。seq2seq LM在做decoder 预测时也有类似的约束,做self-attention时能使用encoder中的所有词,以及decoder中当前词(替换为 [MASK]
了)和前面的词,而不能使用decoder中这个词后面的词。UNILM在做self-attention时通过mask机制来满足这些约束,也即在softmax函数中把后面词对应的向量元素值改为 -∞
。
seq2seq LM是把两个句子拼起来(和BERT相同)直接输入一个Transformer(只是预测encoder和decoder中被mask的词时,对self-attention使用了不同的约束条件),所以encoder和decoder使用的是同一个Transformer。seq2seq LM的训练样本,和NSP任务类似,为连续的两个句子,然后随机mask掉两个句子中的词让模型进行预测。
对词随机mask的机制和BERT类似,只是会以一定概率mask临近的两个或三个词,具体说明如下:
训练时目标函数的设定也参照BERT,只是要同时兼顾双向LM、单向LM和seq2seq LM。作者使用的模型大小同 ,也即用了24层的Transformer。
精调阶段,对于NLU类型的任务UNILM和BERT相同。对于NLG类型的任务,UNILM随机mask decoder中的一些词,然后再预测它们。以下是UNILM应用于生成式QA任务的做法,效果提升很明显。
对于GLUE的所有任务,UNILM据说是首次不添加外部数据打赢BERT的模型!
总结下UNILM的特点:
预训练同时训练双向LM、单向LM和seq2seq LM,使用
mask
机制解决self-attention中的约束问题。可以处理NLU和NLG类型的各种任务。
在GLUE上首次不加外部数据打赢了BERT。
多任务学习:MT-DNN
MT-DNN (Multi-Task Deep Neural Network)是去年年底微软的一篇工作,思路很简单,就是在MTL中把BERT引入进来作为底层共享的特征抽取模块。
预训练就是BERT,精调时每个batch随机选一个任务进行优化。整体算法步骤如下:
MT-DNN在GLUE上效果比BERT好不少,当然主要原因可能是加入了额外的数据了。作者也对比了多任务与单任务的结果,多任务确实能给每个任务都带来效果提升。
总结下MT-DNN的特点:
框架简单明了:MT-DNN = BERT + MTL。