编者按git
深度迁移学习是基于深度神经网络的迁移学习方法,BERT经过预训练模型达到深度迁移学习的效果,自从2018年末BERT横空出世以来,就以势不可挡的姿态横扫了众多榜单,甚至在阅读理解任务SQuAD 中超越人类水平。BERT在公检法、媒体出版、军工、快消零售等工业界也迅速落地,如百分点智能对话系统、百分点智能审校系统和百分点智能翻译系统等。BERT几乎在全部的下游任务中效果都得到了明显提高,BERT自此开创了一个NLP的新时代,那就是pre-train + fine-tuning的时代。github
基于BERT的各类改进版预训练模型层出不穷,使人眼花缭乱,彷佛一不当心就会落伍。可是万变不离其宗,只要掌握了一些最基本的的思想、技术,就能让本身紧跟大神们的脚步,让更优秀的算法模型在工业界持续落地。百分点认知智能实验室梳理了以BERT为表明的基于fine-tuning模式的深度迁移学习中一些疑难问题,整理出18个典型的问题,对理解BERT论文和源代码有明显的帮助,所以分享给你们。算法
本文做者:崔丙剑 苏海波api
基本概念网络
1.如何正确理解深度迁移学习?机器学习
答:迁移学习是机器学习的一个重要分支,是指利用数据、任务、或模型之间的类似性,将在源领域学习过的模型,应用于新领域的一种学习过程。ide
图1: 迁移学习示意图函数
迁移学习主要有几种形式:基于样本的迁移、基于特征的迁移、基于模型的迁移和基于关系的迁移。重点说下基于模型的迁移,其基本思想是指从源域和目标域中找到他们之间共享的参数信息,以实现迁移。性能
图2: 基于模型的迁移学习学习
深度迁移学习主要就是模型的迁移,一个最简单最经常使用的方法就是fine-tuning,就是利用别人已经训练好的网络,针对目标任务再进行调整。近年来大火的BERT、GPT、XLNET等都是首先在大量语料上进行预训练,而后在目标任务上进行fine-tuning。
答:特征提取(Feature-extract):特征提取是使用以前训练好的模型对新的样本生成特征向量,而后将这些特征做为task-specific模型的输入,训练新的模型参数。好比BERT就是以前学好的模型,把一个句子输入到BERT,能够获得这个句子的向量表示,而后将这个向量做为后续的好比分类模型的输入,在训练的过程当中只训练后面的分类模型,BERT的输出仅仅是做为分类模型的输入特征。
模型微调(Fine-tuning):不一样于特征提取的方式要另起灶炉针对具体任务设计新的模型,模型微调是直接使用已训练好的模型,针对当前的任务对输出层简单修改,而后在当前任务的数据上进行训练,对部分网络层的参数进行微调,让模型更适合当前的任务。这种模型微调的方式能充分利用深度神经网络强大的泛化能力,还避免了设计新的的模型,无需从头开始训练,能达到更快的收敛速度和更好的效果。
模型输入
答:BERT embedding layer有三个输入,分别是token-embedding、segment-embedding和position-embedding。
Token-embedding:将单词转换为固定维的向量表示形式,在BERT-base中,每一个单词都表示为一个768维的向量。
Segment-embedding:BERT在解决双句分类任务(如判断两段文本在语义上是否类似)时是直接把这两段文本拼接起来输入到模型中,那么模型是如何区分这两段文本呢,答案就是经过segment-embedding。对于两个句子,第一个句子的segment-embedding部分全是0,第二个句子的segment-embedding部分全是1。
Position-embedding:BERT使用transformer编码器,经过self-attention机制学习句子的表征,self-attention不关注token的位置信息,因此为了能让transformer学习到token的位置信息,在输入时增长了position-embedding。
答:三个向量是相加后做为第一层transformer的输入,三个向量的维度都是768。Pytorch版BERT-embedding具体实现代码以下,从中咱们能够明显看出是相加的关系。
图3: BERT embedding层源码
答:BERT论文中做者对此没有说明缘由,不过能够从如下几点进行分析:
a) 用于机器翻译的平行语料有限,transformer那篇论文在作机器翻译任务时没有像如今训练BERT⼀样海量的训练数据,因此即便⽤了learned-position-embedding也未必可以学到⼀个好的表⽰。⽽BERT训练的数据⽐transformer⼤的多,所以可让模型⾃⼰去学习位置特征。
b) 对于翻译任务,encoder的核⼼任务是提取完整的句⼦语义信息,无需特别关注某个词的具体位置。而BERT在作下游的序列标注类任务时须要确切的位置信息,模型须要给出每一个位置的预测结果,所以BERT在预训练过程当中须要建模完整的词序信息。
答:先说为何这么作,若是以传统的方式进行分词,因为单词存在时态、单复数等多种变化会致使词表很是大,严重影响训练速度,而且即便一个很是大的词表仍没法处理未登陆词(OOV, Out Of Vocabulary),影响训练效果。而若是以character级别进行文本表示粒度又太细。Subword粒度在word与character之间,可以较好的解决上述分词方式面临的问题,已成为了一个重要的NLP模型性能提高方法。Subword的实现方式主要有wordpiece和BPE(Byte Pair Encoding),BERT使用了wordpiece方式。
Wordpiece的功能:Wordpiece能够理解为把⼀个单词再拆分红subword,好比"loved","loving", "loves"这三个单词,其实本⾝的语义都是“爱”,可是若是以单词为单位,那这些词就算是不⼀样的词。Wordpiece算法可以把这3个单词拆分红"lov", "#ed", "#ing", "#es"几部分,这些单词都有一个共同的subword“lov”,这样能够把词的本⾝的意思和前缀、后缀分开,使最终的词表变得精简,而且寓意也能更清晰。
答:可能不少人没思考过这个问题,虽然在上一个问题中咱们已经知道wordpiece会把单词拆分红subword,可是能拆分的前提是有一个subword词汇表。这个问题中咱们就来详细看下这个subword词汇表的生成方法。
将wordpiece词汇表生成以前咱们仍是先看下BPE词汇表是怎么生成的,由于二者很是类似。
BPE词汇表生成算法以下:
a) 准备训练语料用于生成subword词表,须要量足够大;
b) 预设定好指望的subword词表的大小;
c) 将单词拆分为字符序列并在末尾添加后缀“ </ w>”,统计单词频率,例如“ low”的频率为5,那么咱们将其改写为“ l o w </ w>”: 5。这一阶段的subword的粒度是单字符;
d) 统计连续字节对出现的频率,选择频率最高的合并成新的subword;
e) 重复第4步,直到subword词表大小达到第2步设定的值,或下一个最高频的字节对出现频率为1。
下边来看一个例子:假设咱们的训练语料为:
lower出现2次,newest出现6次,widest出现3次,low出现5次
根据上述第3步的操做能够处理成以下格式:
{'l o w e r </w>': 2, 'n e w e s t</w>': 6, 'w i d e s t </w>': 3, 'l o w </w>': 5}
其中的key是词表中的单词拆分红字母,末尾添加后缀“</w>”,value表明单词出现的频率。此时初始的词表中是训练语料中全部单词的字母集合,大小为10,以下表:
[l, o, w, e, r, n, s, t, i, d]
咱们设定最终的词表大小为18,而后开始整个算法中最重要的是第4步,过程以下:
原始词表: {'l o w e r </w>': 2, 'n e w e s t </w>': 6, 'w i d e s t </w>': 3, 'l o w </w>': 5}
出现最频繁的序列: ('s', 't') 9
将”st”加入词表,第1次循环结束,此时词表大小为11;
合并最频繁的序列后的词表: {'n e w e st </w>': 6, 'l o w e r </w>': 2, 'w i d e st </w>': 3, 'l o w </w>': 5}
出现最频繁的序列: ('e', 'st') 9
将”est”加入词表,第2次循环结束,此时词表大小为12;
合并最频繁的序列后的词表: {'l o w e r </w>': 2, 'l o w </w>': 5, 'w i d est </w>': 3, 'n e w est </w>': 6}
出现最频繁的序列: ('est', '</w>') 9
将“est</w>”加入词表,第3次循环结束,此时词表大小为13;
合并最频繁的序列后的词表: {'w i d est</w>': 3, 'l o w e r </w>': 2, 'n e w est</w>': 6, 'l o w </w>': 5}
出现最频繁的序列: ('l', 'o') 7
将“lo”加入词表,第4次循环结束,此时词表大小为14;
合并最频繁的序列后的词表: {'w i d est</w>': 3, 'lo w e r </w>': 2, 'n e w est</w>': 6, 'lo w </w>': 5}
出现最频繁的序列: ('lo', 'w') 7
将“low”加入词表,第5次循环结束,此时词表大小为15;
合并最频繁的序列后的词表: {'w i d est</w>': 3, 'low e r </w>': 2, 'n e w est</w>': 6, 'low </w>': 5}
出现最频繁的序列: ('n', 'e') 6
将“ne”加入词表,第6次循环结束,此时词表大小为16;
合并最频繁的序列后的词表: {'w i d est</w>': 3, 'low e r </w>': 2, 'ne w est</w>': 6, 'low </w>': 5}
出现最频繁的序列: ('w', 'est</w>') 6
将“west</w>”加入词表,第7次循环结束,此时词表大小为17;
合并最频繁的序列后的词表: {'w i d est</w>': 3, 'low e r </w>': 2, 'ne west</w>': 6, 'low </w>': 5}
出现最频繁的序列: ('ne', 'west</w>') 6
将“newest</w>”加入词表,第8次循环结束,此时词表大小为18,整个循环结束。
最终咱们获得的词表为:
[l, o, w, e, r, n, s, t, i, d, st, est,est</w>, lo, low, ne, west</w>, newest</w>]
Wordpiece与BPE稍有不一样,主要区别在于BPE是经过最高频率来肯定下一个subword,而wordpiece是基于几率生成新的subword,另外一个小的区别是wordpiece后缀添加的是“##”而不是“<\w>”,整个算法过程以下:
a)准备训练语料用于生成subword词表,须要量足够大;
b)预设定好指望的subword词表大小;
c)将单词拆分为字符序列并在末尾添加后缀“##”;
d)从全部可能的subword单元中选择加入语言模型后能最大程度地增长训练数据几率的组合做为新的单元;
e)重复第4步,直到subword词表大小达到第2步中设定的值,或几率增量低于某一阈值。
答:CLS是classification的缩写,添加该标志主要用于句子级别的分类任务。BERT借鉴了GPT的作法,在句子首部增长一个特殊的token“[CLS]”,在NSP预训练任务中,就取的是“[CLS]”位置对应的最后的隐状态,而后接一个MLP输出两个句子是不是上下句关系。能够认为“[CLS]”位置的信息包含了句子类别的重要特征。同理能够取“[MASK]”位置的向量用于预测这个位置的词是什么。
答:BERT因为position-embedding的限制只能处理最长512个词的句子。若是文本长度超过512,有如下几种方式进行处理:
a)直接截断:从长文本中截取一部分,具体截取哪些片断须要观察数据,如新闻数据通常第一段比较重要就能够截取前边部分;
b)抽取重要片断:抽取长文本的关键句子做为摘要,而后进入BERT;
c)分段:把长文本分红几段,每段通过BERT以后再进行拼接或求平均或者接入其余网络如lstm。
模型原理
答:在传统的seq2seq模型中,咱们通常使用RNN或CNN对序列进行编码,而后采用pooling操做或者直接取RNN的终态做为输入部分的语义编码C,而后把C输入到解码模块中,在解码过程当中,C对每一个位置的输出重要程度是一致的,以下图所示:
图4: 普通的seq2seq
然而在天然语言中,一个句子中不一样部分的重要性也是不同的,用RNN或CNN进行句子编码,并不能学习到这样的信息。所以出现了attention,顾名思义就是在解码时能对序列中不一样位置分配一个不一样的注意力权重,抽取出更加关键和重要的信息,从而使模型作出更好的判断,就像咱们人在看一个句子时,重点关注的是其中的重要信息,对不重要的信息根本不关心或基本不关心。
图5: 基于attention的seq2seq
答:mutli-head attention的计算过程以下图所示:
图6: Multi-head attention计算过程
输入向量维度为768维,通过每一个self-attention后获得隐层输出为64维,而后把12个输出拼接起来获得768维的向量。
答:要弄明白为何这样构造MLM的训练数据,咱们须要首先搞明白什么是MLM、为何要使用MLM,以及MLM存在哪些问题。
a)为何使用MLM:传统的语言模型通常都是单向的,要同时获取上下文信息的常见作法是分别训练正向与反向的语言模型,而后再作ensemble,但这种作法并不能充分利用上下文信息。MLM 的意义在于可以真正利用双向的信息,使模型学习到上下文相关的表征。具体作法就是随机屏蔽(mask)输入文本中的部分token,相似于完形填空,这样在预测被mask部分的token时就可以同时利用上下文信息。
b)MLM存在问题:因为预训练数据中存在“[MASK]”这个token,而在实际的下游任务中对BERT进行fine-tuning时,数据中没有“[MASK]”,这样就致使预训练模型使用的数据和fine-tuning任务使用的数据不一致,会影响fine-tuning的效果。
为了让MLM可以学习上下文相关特征,同时又尽可能避免pre-train和fine-tuning数据不一致的问题,数据处理时就采起题目中策略,具体处理策略和缘由解释以下:
答:要计算BERT的参数量,首先须要对BERT的结构了解的很是清楚,下面咱们就来看下base版BERT 110M的参数究竟是怎么计算出来的。
图7: BERT结构图
a) embedding层的参数
BERT的输入有三种embedding,以下源码中所示:
图8: BERT embedding层源码
vocab_size=30522,hidden_size为768,最大位置长度为512,type_vocab_size=2,所以能够计算出:
embedding层的参数量 =(30522+512+2)*768=23,835,648
b) multi-headattention的参数
图9: Self attention计算过程
先来看下multi-head attention的计算过程:embedding层的输出x分别与三个矩阵WQ、Wk、Wv相乘获得Q、K、V,再通过右上图的计算获得一个self-attention的输出,12个self-attention的输出拼接起来获得,再通过一个线性变换获得multi-head attention的输出。
WQ、Wk、Wv的维度均为76864,head数为12,线性变换矩阵为768768,所以能够计算出:
multi-head的参数量 =76864312+768768=2,359,296
c) 全链接层(FeedForward)的参数量
全链接层把multi-head attention输出的维度从768映射到3072又映射到768,公式以下图所示:
其中W1维度为7683072,W2维度为3072768,所以能够计算出:
全链接层的参数量 = 76830722=4,718,592
Base版BERT使用了12层transformer的encoder,所以能够计算出:
总参数量 = embedding参数量+12(multi-headattention参数量+全链接参数量)
=23,835,648+12*(2,359,296+4,718,592)=108,770,304≈110M
答:⾸先这些预训练任务的训练数据要能从⽆监督的数据中获取,这样才能获取到海量的训练数据,符合这⼀条件的任务均可以进⾏尝试,如百度的ERNIE增长了不少个预训练任务,相比于原始BERT有了明显的提高。几个有表明性的预训练任务以下:
Knowledge Masking Task:BERT的MLM任务中是对句⼦中单个的token进⾏mask,能够对于句⼦中的短语和命名实体进⾏mask。
Capitalization Prediction Task:预测单词是否⼤写,与其余词语相⽐,⼤写词语一般具备特定的语义价值。
Token-Document Relation Prediction Task:预测⼀个段落中的某个token是否出如今原始⽂档的其余段落中。根据经验,在⽂档不一样部分都出现的单词一般是⽂档的关键词,所以这⼀任务能够在⼀定程度上使模型可以捕获文档的关键字。
Sentence Distance Task:⼀个学习句⼦间距离的任务,该任务被建模为⼀个3类分类问题,“0”表示两个句⼦在同⼀个文档中相邻,“1”表示两个句⼦在同⼀个文档中,但不相邻,“2”表示两个句子来自两个不一样的文档。
模型的进化
15.自回归语言模型(AR, Autoregressive LM)与自编码语言模型(AE, Autoencoder LM)的区别?
答:自回归语言模型:根据上文内容预测下一个单词或者根据下文内容预测上一个单词,这样单向的语言模型就是自回归语言模型。LSTM、GPT、ELMO都是自回归语言模型。自回归语言模型的缺点,是不能同时利用上下文信息。
自编码语言模型:自编码器是一种经过无监督方式学习特征的方法,用神经网络把输入变成一个低维的特征,这就是编码部分,而后再用一个解码器把特征恢复成原始的信号,这就是解码部分。具体到语言模型中,BERT使用的MLM就是自编码语言模型,对一些token进行mask,而后拿被mask位置的向量(包含了上下文的信息)来预测该位置真正的token。
自编码语言模型的优势就是能够同时利用上下文信息,缺点就是预训练阶段和fine-tuning阶段使用的训练数据不一致,由于fine-tuning阶段的数据是不会被mask的。
16.XLNET相对于BERT作了哪些重要改进?
答:BERT的AE语言模型虽然能同时学习上下文信息可是会致使预训练数据和fine-tuning阶段的数据不一致从而影响fine-tuning的效果。而XLNET的思路就是使用AR语言模型,根据上文预测下文,可是在上文中添加了下文信息,这样既解决了BERT面临的问题也同时利用了上下文信息。
XLNET改进后的语言模型叫作PermutationLanguage Model(PLM),其重点就是permutation,用一个例子来解释:对于一个输入句子X=[x1, x2, x3, x4],咱们但愿预测x3,在正常的输入中经过AR语言模型只能看到x1和x2。为了在预测x3时能看到x4,XLNET的操做是固定x3的位置,而后把其它的词进行随机排列,获得如:[x4, x1, x3, x2],[x1, x4, x3, x2]等数据,这样就可使用单向的AR语言模型来学习双向信息。
这时有人可能就会有疑问:就算训练时能够对输入句子进行排列组合,可是fine-tuning时无法这样作啊。没错,fine-tuning阶段确实不能对输入作排列,只能输入原始句子,因此XLNET在预训练阶段也是不能显示地对输入进行排列的。为了解决这个问题,XLNET的输入仍是原始的句子,只不过是在transformer内部利用attention mask来实现的,而无需真正修改句子中词语的顺序。例如原来的句子是X=[x1, x2, x3, x4],若是随机生成的序列是[x3, x2, x4,x1],但输入到 XLNET 的句子仍然是[x1, x2,x3, x4],此时设置attention mask以下图:
图10: Attention mask示意图
图中的掩码矩阵,白色表示不遮掩,黑色表示遮掩。第 1 行表示 x1 的掩码,由于x1是句子的最后一个 token,所以能够看到以前的全部 token [x3,x2,x4];第2行是x2的掩码,由于x2是句子的第二个token,因此能看到前一个token x3;第3行、第4行同理。这样就实现了尽管当前输入看上去仍然是[x1, x2, x3, x4],可是已经改为排列组合的另一个顺序[x3, x2,x4, x1]了。若是用这个例子用来从左到右训练LM,意味着当预测x2的时候,它只能看到上文x3;当预测x4的时候,只能看到上文x3和x2,……
17.RoBERTa相对于BERT作了哪些重要改进?
答:RoBERTa相对于BERT在模型结构上并无改变,改进的是预训练方法,主要改进有如下几点:
a) 静态mask变为动态mask
BERT MLM任务中,有15%的样本在预处理阶段会进行一次随机mask,具体的mask方式参考问题12,而后在整个训练过程当中,这15%的被mask的样本其mask方式就再也不变化,也不会有新的被mask样本,这就是静态mask。
RoBERTa采用了一种动态mask的方式,它并无在预处理的时候对样本进行mask,而是在每次向模型提供输入时动态生成mask,因此训练样本是时刻变化的,而且实验代表这种动态mask的方式要比BERT原始的静态mask效果要好。
b) 去除NSP任务
不少实验代表NSP任务的没多大意义,RoBERTa中去除了该任务,不过在生成数据时也作了一些改进,原始的BERT中是选择同一篇文章中连续的两个句子或不一样文章中的两个句子,而RoBERTa的输入是连续的多个句子(总长度不超过512)。
c) 更多的数据、更大的mini-batch、更长的训练时间
BERT base的训练语料为13G,batch-size为256,而RoBERTa的训练语料扩大了10到130G,训练中batch-size为8000,实为大力出奇迹的杰出表明。
18.ALBERT相对于BERT作了哪些重要改进?
答:ALBERT是一个精简的BERT,参数量获得了明显的下降,使得BERT的大规模应用成为可能。相对于BERT,ALBERT主要有三点改进:
a) Embedding matrix因式分解
在BERT、XLNET等模型中,embedding的维度 E 和隐藏层维度 H 是相等的,都是768,V是词表的大小通常的3万左右。从建模的角度来讲,embedding层的目标是学习上下文无关的表示,而隐藏层的目标是学习上下文相关的表示,理论上来讲隐藏层的表述包含的信息应该更多一些,所以应该让H>>E。若是像BERT那样让E=H,那增大H以后,embedding matrix大小V*H会变的很大。
ALBERT采起因式分解的方式来下降参数量,先将单词映射到一个低维的embedding空间,而后再将其映射到高维的隐藏空间,让H>>E,这样就能够把embedding matrix的维度从O(VH)减少到O(VE+E*H),参数量减小很是明显。
b) 跨层权重共享
Transformer参数共享能够只共享全链接层、只共享attention层,ALBERT结合了这两种方式,让全链接层与attention层都进行参数共享,也就是说共享encoder内的全部参数,采用该方案后效果降低的并很少,可是参数量减小了不少,训练速度也提高了不少。此外实验还代表ALBERT每一层的输出embedding相比于BERT来讲震荡幅度更小一些,能够增长模型的鲁棒性。
c) 修改预训练任务NSP为SOP
一些研究代表BERT的NSP并不适合用于预训练任务,缘由多是负样原本源于不一样的文档,模型在判断两个句子的关系时不只考虑了两个句子之间的连贯性,还会考虑两个句子的话题,而两篇文档的话题一般不一样,模型可能更多的经过话题去分析两个句子的关系,而不是连贯性,这使得NSP任务变的相对简单。
ALBERT中设计了SOP(Sentence-orderprediction)任务,其正样本选取方式与BERT一致(来自同一文档的两个连续句子),而负样本也一样是选自同一文档的两个连续句子,但交换了两个句子的顺序,从而使模型能够更多地建模句子之间的连贯性而不是句子的话题。
参考文献:br/>1.http://jd92.wang/assets/files/transfer_learning_tutorial_wjd.pdf
2.https://jalammar.github.io/illustrated-transformer/
3.https://medium.com/@_init_/why-BERT-has-3-embedding-layers-and-their-implementation-details-9c261108e28a
4.https://medium.com/@makcedward/how-subword-helps-on-your-nlp-model-83dd1b836f46
5.BERT Explained: State ofthe art language model for NLP.
https://towardsdatascience.com/BERT-explained-state-of-the-art-language-model-for-nlp-f8b21a9b6270
6.https://zhuanlan.zhihu.com/p/70257427
7.https://arxiv.org/pdf/1706.03762
8.https://arxiv.org/pdf/1906.08237
9.https://arxiv.org/pdf/1907.11692
10.https://arxiv.org/pdf/1909.11942
11.https://arxiv.org/pdf/1905.07129