如果总结过去 20 年里,无数先辈辛劳付出带来的璀璨成果,以下 3 个代表性工作列入 NLP 名人堂,应该实至名归:
1)2003 年 Bengio 提出神经网络语言模型 NNLM,从此统一了 NLP 的特征形式——Embedding;
2)2013 年 Mikolov 提出词向量 Word2vec,延续 NNLM 又引入了大规模预训练(Pretrain)的思路;
3)2017 年 Vaswani 提出 Transformer 模型,实现用一个模型处理多种 NLP 任务。
基于 Transformer 架构,2018 年底开始出现一大批预训练语言模型,刷新众多 NLP 任务,形成新的里程碑事件。本文将跨越 2018-2020,着眼于 3 个预训练代表性模型 BERT、XLNet 和 MPNet,从以下 4 个章节介绍 NLP 预训练语言模型的发展变迁史:
1.BERT 原理及 MLM 简述
2.XLNet 原理及 PLM 简述
3.MPNet 原理及创新点简述
4.NLP预训练模型趋势跟踪
附录:快速上手BERT的4大工具包
自谷歌 2018 年底开源 BERT,NLP 界的游戏规则某种程度上被 “颠覆” 了;一时间,这个芝麻街的可爱小黄人形象,成为众多 NLPer 及其他 DL、ML 研究者们的拥趸。
“BERT 一把梭 “,“遇事不决就 BERT”,“BERT 在手,天下我有”,表达了使用者们对 BERT 的心声。也因为 BERT,NLP 的准入门槛大幅下降,一些较浅层的 NLP 任务如文本分类、相似匹配、聚类某种程度上可以被认为是完全解决。
BERT 四大下游任务微调
BERT 为什么会有如此引人注目的优良效果?下面我们再来回顾下 BERT 到底是什么。
BERT 本质上是一个自编码(Auto Encoder)语言模型,为了能见多识广,BERT 使用 3 亿多词语训练,采用 12 层双向 Transformer 架构。注意,BERT 只使用了 Transformer 的编码器部分,可以理解为 BERT 旨在学习庞大文本的内部语义信息。
具体训练目标之一,是被称为掩码语言模型的 MLM。即输入一句话,给其中 15% 的字打上 “mask” 标记,经过 Embedding 输入和 12 层 Transformer 深度理解,来预测 “mask” 标记的地方原本是哪个字。
input: 欲把西[mask]比西子,淡[mask]浓抹总相宜
output: 欲把西[湖]比西子,淡[妆]浓抹总相宜
例如我们输入 “欲把西 [mask] 比西子,淡 [mask] 浓抹总相宜” 给 BERT,它需要根据没有被 “mask” 的上下文,预测出掩盖的地方是 “湖” 和“妆”。
MLM 任务的灵感来自于人类做完形填空。挖去文章中的某些片段,需要通过上下文理解来猜测这些被掩盖位置原先的内容。
训练目标之二,是预测输入的两句话之间是否为上下文(NSP)的二分类问题。继续输入 “欲把西[湖] 比西子,淡 [妆] 浓抹总相宜”,BERT 将预测这两句话的组合是否合理(这个例子是“yes”)。(随后的研究者对预训练模型探索中证明,NSP 任务过于简单,对语言模型的训练作用并不是很大)
通过这两个任务和大规模语料训练,BERT 语言模型可以很好学习到文本之间的蕴含的关系。
接下来简单介绍 BERT 以及 XLNet、MPNet 所使用 Transformer 的核心模块:自注意力机制。
自注意力机制重点在于学习输入序列自身的内部信息。具体地,每个 t o k e n token token 可以观察到序列中其他所有 t o k e n token token 的信息,并通过” 注意力 “交互,其余的 t o k e n ′ token' token′ 会产生不同大小地权重(整个过程类似加权)。上例中,“西子”和 “西湖” 关系紧密,因此它们之间的 attention 权重更大(大于 “西子” 和“淡妆”)。最终自注意力层的输出涵盖了序列所有 t o k e n token token 的语义信息,实现了双向编码上下文。
同时,这种双向性使得模型可以同时观测序列的所有位置,解决了 RNN 等递归模型无法高效并行的瓶颈。
由于架构采用 12 层双向 Transformer 且训练目标包含还原 m a s k mask mask 位置的信息,BERT 被称为去噪自编码语言模型(DAE)。
而在 BERT 之前,NLP 领域的语言模型几乎是 Auto Regression(自回归)类型,即当前位置的字符预测 T i Ti Ti需要编码之前 T ( 0 : i − 1 ) T(0:i-1) T(0:i−1) tokens 的语义信息,使得模型训练 / 预测只能单向进行。
虽然 ELMO 采用了 BiLSTM,但只是前向、后向两次输出的简单拼接,包含的全局语义信息依然较弱。
虽然效果好,BERT 的缺点也很明显。从建模本身来看,随机选取 15% 的字符 mask 忽视了被 mask 字符之间可能存在语义关联的现象,从而丢失了部分上下文信息。同时,微调阶段没有 mask 标记,导致预训练与微调的不一致。
和 BERT 不同,XLNet 本质上是用自回归语言模型来同时编码双向语义信息的思路,可以克服 BERT 存在的依赖缺失和训练 / 微调不一致的问题。同时为了弥补自回归模型训练时无法同时看到上下文的缺陷,XLNet 曲线救国地提出了 PLM 排列语言模型的训练方式。
对于一个长度为 N 的序列,我们知道其存在 N ! N! N! 种因式分解顺序,通过一次采样一种序列的因式分解组合,每个 token 总是能够在不同的序列中观察到其他所有 token;同时模型参数对于所有的因式分解顺序共享,因此从期望的角度上看,XLNet 模型能够双向地编码上下文。
例如,初始序列为 ( x 1 , x 2 , x 3 , x 4 ) (x1,x2,x3,x4) (x1,x2,x3,x4),这里的 x 3 x3 x3 只能关注到前面的 ( x 1 , x 2 ) (x1,x2) (x1,x2) ,但在某种因式分解排列 ( x 4 , x 2 , x 1 , x 3 ) (x4,x2,x1,x3) (x4,x2,x1,x3)中, x 3 x3 x3 具备了关注 ( x 4 , x 2 , x 1 ) (x4,x2,x1) (x4,x2,x1)的能力。另外,XLNet 的原始输入和 BERT 相同,依然是正常排序的 ( x 1 , x 2 , x 3 , x 4 ) (x1,x2,x3,x4) (x1,x2,x3,x4) 。
那 XLNet 是如何在保持输入顺序不变的同时,对序列进行乱序编码的呢?
简单而言,通过 Attention 掩码机制,将当前 token 及其之后的 token(不该看到的部分)嵌入信息用 attention-mask 掩盖。具体实现上,使用了一种双流自注意力机制。
例如某个序列的因式分解顺序为 ( x 2 , x 1 , x 4 , x 3 ) (x2,x1,x4,x3) (x2,x1,x4,x3) 和 ( x 2 , x 1 , x 3 , x 4 ) (x2,x1,x3,x4) (x2,x1,x3,x4),如果需要预测第三个位置的 token,传统的自回归模型通过 ( x 2 , x 1 ) (x2,x1) (x2,x1) 的编码来预测后面的 token,概率表达式为 P ( x t ∣ ( x 2 , x 1 ) ) P(xt|(x2,x1)) P(xt∣(x2,x1))。然而这样会带来一个问题:x2 和 x1 的编码和需要预测的下一个 token 是无关的,xt 既可以是 x4 也可以是 x3,即 P(x3|x2,x1) = P(x4|x2,x1),这显然不合理(传统 RNN 是按正常的序列进行递归预测,位置是正确的,所以不存在这个问题)。因此 XLNet 需要引入待预测 token 的位置信息,例如 P ( x 4 ∣ x 2 , x 1 , p o s 4 ) P(x4|x2,x1,pos4) P(x4∣x2,x1,pos4) 或 P ( x 3 ∣ x 2 , x 1 , p o s 3 ) P(x3|x2,x1,pos3) P(x3∣x2,x1,pos3) ,确保生成合理的结果。
然而这又带来了新的矛盾。对于某个因式分解顺序 ( x 2 , x 1 , x 4 , x 3 ) (x2,x1,x4,x3) (x2,x1,x4,x3),在预测 x4 的时候,模型不能编码自身的 token-embedding,只能编码前面的 ( x 2 , x 1 ) (x2,x1) (x2,x1)以及自身的 position-embedding,否则训练就没有意义了;然而在预测 x3 的时候,又需要用到 x4 的完整的编码信息。如果继续沿用 BERT 的自注意力机制必然存在问题,因此 XLNET 将自注意力机制拆分为 Query 流和 Content 流。Query 流中当前 token 只能关注到前面的 token 和自身的位置信息,Content 流中当前 token 可以关注到自身。
具体来看,XLNET 将序列拆分为 2 部分,序列的后部分(约占句长的 1/K,K 为超参数)为需要预测的部分,前部分为已知上下文。已知的上下文不做预测,因此只计算 content 流注意力,每个 token 都编码之前 token 以及自身的完整信息。从预测部分开始,每个 token 同时计算 Query 流和 Content 流注意力:Query 流的输出用于预训练做预测,Content 流的输出提供给后续待预测 token 计算 Query 流,这就保证了当预测当前 token 时,它无法看到自身编码;当前 token 预测结束后,将其 Content 流作为上下文部分的编码提供给后续需要预测的 token。预训练过程计算 2 种注意力,微调过程去除了 Query 流,只保留 Content 流,因为不需要对 token 进行词表空间的预测,而是需要编码整个上下文语义用于下游任务。
前面提到 Auto Regression 模型的缺点是只能单向编码,但它能够编码被预测的 token 之间的联系,即克服了 BERT 被 mask 字符间信息丢失的缺点。其次,通过上文的 PLM 模型弥补了自回归语言模型只能单向编码的缺点。AR 模型在预训练和下游任务中都没有对输入序列进行损坏(遮盖部分 token,引入噪声),消除了模型在预训练和微调过程中的差异。
虽然在期望上看,PLM 几乎实现了双向编码功能的自回归模型,但是针对某一个因式分解序列来说,被预测的 token 依然只能关注到它前面的序列,导致模型依然无法看到完整序列信息和位置信息。
结合 BERT、XLNet 的思路,南京大学和微软在 2020 年共同提出了新的预训练语言模型 MPNet:Masked and Permuted Pre-training for Language Understanding。
MPNet 的创新点在于 4 个字:位置补偿(position compensation),大家先留个印象,下文会再详细介绍。
论文开篇,作者针对上文 MLM、PLM 各自特点,希望用一种统一的模型既保留二者的优点,又弥补它们的不足,这就是 MPNet。
首先,作者通过重新排列和切分输入序列中的 tokens,将 MLM 和 PLM 统一为非预测部分(non-predicted)和预测部分(predicted),如图 (a),(b) 右侧。如此一来,MLM 和 PLM 就拥有了相似的数学表达公式,仅在条件部分有细小差异。
为缓解 BERT-mask 可能丢失依赖信息的问题,MPNet 沿用了 XLNet 的自回归结构,同时为弥补 XLNet 无法捕捉全部序列位置信息的缺陷,添加了「位置补偿」:针对需要预测的 token,额外添加了它们的位置信息。使得自回归过程中,在任意一个位置 i,除了可以看到之前部分的 token 编码,还能看到序列所有 token 的位置编码(类似于 BERT)。
例如,对于一个长度为 6 的 token 序列 x = ( x 1 , x 2 , x 3 , x 4 , x 5 , x 6 ) x=(x 1, x 2, x 3, x 4, x 5, x 6) x=(x1,x2,x3,x4,x5,x6),采样得到一个因式分解序列 x z = ( x 1 , x 3 , x 5 , x 4 , x 6 , x 2 ) x z=(x 1, x 3, x 5, x 4, x 6, x 2) xz=(x1,x3,x5,x4,x6,x2);假设非预测部分为 c = 3 , x ( z < = c ) = ( x 1 , x 3 , x 5 ) c=3,x(z<=c)=(x1,x3,x5) c=3,x(z<=c)=(x1,x3,x5),待预测部分为 x ( z > c ) = ( x 4 , x 6 , x 2 ) x(z>c)=(x4,x6,x2) x(z>c)=(x4,x6,x2) 。对于 z > c z>c z>c部分,作者在待预测的 tokens 左边额外添加了 mask-token M ( z > c ) M(z>c) M(z>c),最终整个 token 的输入序列由三部分组成: ( x ( z < = c ) , M ( z > c ) , x ( z > c ) ) = ( x 1 , x 3 , x 5 , [ M ] , [ M ] , [ M ] , x 4 , x 6 , x 2 ) (x(z<=c), M(z>c), x(z>c))=(x 1, x 3, x 5,[M],[M],[M], x 4, x 6, x 2) (x(z<=c),M(z>c),x(z>c))=(x1,x3,x5,[M],[M],[M],x4,x6,x2) ,[M] 表示遮盖该 token;对应的位置序列为: ( z ( < = c ) , z ( > c ) , z ( > c ) ) = ( p 1 , p 3 , p 5 , p 4 , p 6 , p 2 , p 4 , p 6 , p 2 ) (z(<=c), z(>c), z(>c))=(p 1, p 3, p 5, p 4, p 6, p 2, p 4, p 6, p 2) (z(<=c),z(>c),z(>c))=(p1,p3,p5,p4,p6,p2,p4,p6,p2)。
3 个 [M] 和对应位置 position-embedding 的加入,就是位置补偿。例如在序列 ( x 1 , x 3 , x 5 , [ M ] , [ M ] , [ M ] , x 4 , x 6 , x 2 ) (x 1, x 3, x 5,[M],[M],[M], x 4, x 6, x 2) (x1,x3,x5,[M],[M],[M],x4,x6,x2)中预测 x 4 x4 x4时,不仅能看到 ( x 1 , x 3 , x 5 ) (x1,x3,x5) (x1,x3,x5)的 token-embedding,还能看到 ( x 1 , x 3 , x 5 , x 4 , x 6 , x 2 ) (x1,x3,x5,x4,x6,x2) (x1,x3,x5,x4,x6,x2)的 position-embedding;依次递归预测 ( x 6 , x 2 ) (x6,x2) (x6,x2) 。
MPNet 使用自回归编码,避免了 BERT 做 Mask 时可能丢失被 Mask 的 token 的彼此关联信息和 pretrain(有 mask)、finetune(无 mask)不一致的问题;通过位置补偿,又解决了 XLNet 无法看到全局位置信息的缺陷。取其精华,确实是挺巧妙的一种思路。
观察输入信息的占比,MPNet 输入的信息量是最大的;从直观上理解,模型每次可以接受到更多的文本特征,从而容易训练出更优结果。
作者在权威的语义理解评估数据集 GLUE 上的实验结果表面,MPNet 确实比它的前辈 BERT 和 XLNet 略胜一筹。另外,作者表示 MPNet 在训练时加入了全词掩码 whole word mask 以及相对位置编码等已被证明有效的 trick,加上和 RoBERTa 训练一样的 160GB 训练语料,取得这样的结果应该说是情理之中了。
末尾的消融实验,可以看到位置补偿和 PLM 对实验结果的提升都很关键。
从目前来看,大规模语料预训练 + finetune 的方式,应该会是 NLP 接下去几年的主流。各种基于语言模型的改进也是层出不穷。虽然玩法种类各异,我们还是可以瞥见一些具有突破性的方向。
前期 BERT 到 RoBERTa,GPT 到 GPT2 效果的提升,已经证明更多数据可以跑出更强大更通用的预训练模型。去年底到今年,英伟达、谷歌、Open-AI 相继放出巨无霸模型 MegatronLM(83 亿参数)、T5(110 亿)、GPT3(1500 亿),不断刷榜令人咋舌的同时也彰显了巨头们的实力。
相信未来,巨无霸模型依然会成为大公司的研究目标之一,却让普通科研人员可望不可及。
没有前排巨头们的经济实力,普通公司和科研机构沿着相反赛道 - 模型轻量化下足了功夫。如何在尽可能少的参数量下,取得和大模型接近的效果,同时训练 / 预测速度翻倍,是很实际很有价值的课题。
这其中,有代表性的工作如华为诺亚方舟实验室发布的 TinyBERT、北大的 FastBERT 都取得了瞩目的效果。例如 FastBERT 在 BERT 的每一层都接入一个分类器,通过样本自适应机制自动调整每个样本的计算量(容易的样本通过一两层就可以预测出来,较难的样本则需要走完全程)。
图中 “Speed” 代表不确定性的阈值,和推理速度成正比。在 Speed=0.2 时,FastBERT 速度可以提升 1-10 倍,且精度下降全部在 0.11 个点之内。
除了知识蒸馏,常规的模型轻量化一般包含层数裁剪、精度量化等手段。
在实际业务场景中,对于中小 AI 企业往往容易出现数据量不足的问题。例如用户需要订制一个 FAQ 问答机器人,有 100 个标准问,但表示每个问句只有 2-3 条同义句…
战略上,“客户就是上帝 “的精神激励我们不能虚,要迎难而上。战术上,除了花高成本找标注团队造数据外,迁移学习、小样本学习可能会非常有帮助。受到人类具有快速从少量(单)样本中学习能力的启发(例如生活在北方的人可能没有见过榴莲,一旦看过一次榴莲的照片,就认识了!),让模型在少量样本中学习获得有力的泛化能力,成为近年的研究热点之一。
感兴趣的同学可以参考阿里的这篇 paper:Few-Shot Text Classification with Induction Network。
预训练语言模型的代表 BERT,已经成为 NLP 领域的重要工具,不同机构 / 个人也分别开发了轻松使用 BERT 的工具包。笔者结合自身经验,简单概括了一下:
5.1 肖涵 - bert-as-service
顾名思义,将 BERT 模型直接封装成一个服务,堪称上手最快的 BERT 工具。作者是 xxx 肖涵博士。
按照 GIthub 上的教程,下载 BERT 权重并安装工具包,三行代码即可轻松使用 BERT 获得文本的向量特征,完成下游 NLP 各项任务。bert-as-service 是跨平台的服务,不受限于 OS、深度学习框架,且作者对于并发做了大量优化与加速,可以满足日常实验甚至公司的实际业务需求。
5.2 Google - BERT 源码
BERT 源码官方仓库,可以学习 BERT 各模块的底层实现细节。Google 开源了权重的同时,也开源了预训练、子任务微调的脚本,是学习 BERT 不可略过的学习教程。代码基于 tensorflow,对 TF 熟练的同学会更快上手。
当前,仓库中还发布了 2/4/6/8… 层不同大小的 BERT,以缓解 BERT 资源开销大、inference 缓慢带来的问题。中文 BERT 可以参考哈工大崔一鸣、实在智能徐亮等开源的权重。
5.3 huggingface - transformers
有了 TF 版,pytorch 怎甘落后。机构 huggingface 开发的 transformers 工具包,堪称预训练模型大礼包,囊括了 10 几种火热模型。
种类齐全且 api 接口实现统一、调用简单,是 pytorch 框架与 BERT 的最佳组合。transformers 的 src 源码也是学习 BERT 等模型原理的绝佳资料。
5.4 苏剑林 - bert4keras
接下来自然而然该 Keras 出场了!作为 tf2.0 的官方高阶 api,Keras 的简洁特性始终拥有超高人气。
来自追一科技的苏神苏剑林,在业余时间自己实现了 bert4keras 框架,且提供了详细教程、众多下游任务微调脚本(分类、文本生成、QA、图片标题生成等)。始终走在 BERT 任务的前沿。
除以上工具包,github 上还有众多用户开源的 BERT 相关工具,按需参考使用即可。
[1] Devlin J, Chang M W, Lee K, et al. BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding[J]. arXiv preprint arXiv:1810.04805, 2018.
[2] Vaswani A, Shazeer N, Parmar N, et al. Attention is all you need [C]//Advances in Neural Information Processing Systems. 2017: 5998-6008.
[3]Zhilin Yang, Zihang Dai, Yiming, et.al. XLNet: Generalized Autoregressive Pretraining for Language Understanding[C]. arXiv preprint arXiv:1906.08237, 2019.
[4]Kaitao Song, Xu Tan, Tao Qin, Tie-Yan Liu, et.al. MPNet: Masked and Permuted Pre-training for Language Understanding [C]. arXiv preprint arXiv:2004.09297, 2020.
[5]Weijie Liu, PengZhou, QiJu, et.al. FastBERT: a Self-distilling BERT with Adaptive Inference Time[C]. arXiv preprint arXiv:2004.02178, 2020.
[6] 张俊林 - XLNet: 运行机制及和 Bert 的异同比较
[7] 李如 - FastBERT:又快又稳的推理提速方法