摘要:本篇主要分享基于BERT实现线上推理服务的FastBERT模型。首先讲了下为啥要用FastBERT模型。因为NLP数据集中不同样本的识别难度不同,厚重的模型会存在过度计算的问题,而轻量模型则很难识别复杂样本,FastBERT模型提出了一种根据样本复杂度动态调整使用Transformer层数的样本自适应机制(sample-wise adaptive mechanism),从而可以又快又好的提供线上推理服务;然后重点分析了FastBERT模型的来龙去脉,包括FastBERT模型的引入和优化思路、模型整体结构、模型训练和推理流程以及FastBERT论文重要的实验结论,从理论到实验论证了FastBERT模型可以又快又好的提供线上推理服务的原理;最后基于作者开源的代码实战了FastBERT。对BERT线上推理服务感兴趣的小伙伴可以一起了解下FastBERT模型。
下面主要按照如下思维导图进行学习分享:
01 为啥要学习FastBERT
1.1 基于BERT文本分类任务的离线和在线服务
目前我们实际项目中主要使用BERT来做文本分类任务,提供文本分类服务主要分成线下服务和线上推理两种方式:
线下服务其实是通过离线的方式,可以是一次性或者按一定频度来提供文本分类服务,比如我们现在按日度构建的pipline流来对用户搜索query进行文本分类,从而获取行业标签词包。这种方式的典型特点是对模型预测速度不敏感,只要在一定的时间内给我返回文本的分类结果就可以,但是需要模型的文本分类效果要好。
线上推理服务则和线下服务差别很大。线上推理服务是得到一条文本数据需要立即返回文本分类的结果,重点是要快,比如现在我们拿到用户搜索后需要理解用户的搜索意图,同时返回对应的行业标签,这一整套流程需要在10ms内搞定。所以线上推理服务的典型特点是对模型的inference速度要求非常高,在用户可感知的时间内立即返回对应的文本分类的结果,同时对文本分类的效果有一定要求。俗话说鱼和熊掌不可兼得,既然要求线上推理的速度非常高,所以在满足这个最重要的前提下可能就会在模型的分类效果上允许有一些轻微的损失。
1.2 传统线上推理服务的方式及缺点
因为使用BERT做文本分类中线下服务和线上推理各自的目标是不同的,所以各自的研究方向也是不同的。先说说线下服务。我目前在团队里主要负责的就是线下服务。因为对模型预测速度不会要求很高,重点目标是文本分类的效果,所以我可以使用各种优化操作来提升模型的文本分类效果指标。之前也尝试过一些基于BERT文本分类的优化,有兴趣的小伙伴可以去看看之前写的这篇文章《广告行业中那些趣事系列16:NLPer一定要知道的BERT文本分类优化策略及原理》(这篇是持续更新的,有啥优化方法都会实时更新到文章中),其中一个重点的研究方向就是对预训练模型进行折腾,使用更多的数据,更大的模型。对于预训练模型,这些最简单朴实的操作效果确是惊人的;而线上推理服务主要的目的是保证一定的分类效果的前提下在线推理速度足够快。对于BERT这种厚重的预训练模型来说,分类效果是没问题的,但是推理速度足够快则让宝宝头疼。
头疼的原因也好理解,因为BERT是基于Transormer的预训练模型,本身就是“厚重”的模型。谷歌原生基础版本的BERT预训练权重有300多M,模型网络参数有上亿个,在这样的大模型下要求线上推理速度快的确是有点难。针对线上推理要求速度快的问题,主要有剪枝、蒸馏、量化操作等方法。剪枝就是对模型的网络进行修剪,比如减掉多余的头(因为Transformer使用多头注意力机制),或者直接粗暴的使用更少的Transformer层数。这种方法直接会降低模型的拟合能力;模型蒸馏其实是把大模型或者多个模型ensemble学到的知识想办法迁移到一个轻量级的小模型上去,线上部署这个小模型就可以了。之前在分析美团基于BERT进行搜索实践的文章中看到模型蒸馏的效果是要好于剪枝的。蒸馏的缺点是存在不确定性,因为你永远无法知道同样一个老师能教出如何千奇百怪的学生;量化操作则是以精度换速度。总体来说,剪枝、蒸馏以及量化操作等大模型减肥提速的方法都或多或少的降低模型的分类效果。
02 FastBERT详解
2.1 FastBERT的引入和优化思路
FastBERT是北大+腾讯+北师大在ACL2020提出的一篇论文《FastBERT: a Self-distilling BERT with Adaptive Inference Time》。这篇文章从工业实践的角度提出了一种又快又好的使用BERT提供线上推理服务的方法FastBERT。简单通俗的理FastBERT可以根据样本的复杂程度来自适应的调节使用Transformer的层数,从而以最低的精度损失最大化提升线上推理的效率。要讲解FastBERT模型的优化原理,我们需要明确以下几个事实:首先,FastBERT作者发现NLP数据集中样本识别难易程度是不同的;然后,模型线上推理主要耗时在Transformer层;最后,原生基础版本的BERT是有十二层Transformer。
了解了上面三个事实,然后来看下常规方法存在的问题:FastBERT的作者发现厚重的模型会对简单样本进行过度的计算,而轻量模型可能无法处理复杂样本。比如对于原生基础版本的BERT来说,不管样本复杂还是简单都会经过十二层Transformer才会给出结果。也就是说如果有一条简单样本,可能只需要前面几层Transformer就可以得到分类结果,但是厚重模型还是会按照流程走完十二层,这自然是增加了多余的推理时间。针对这个问题,FastBERT模型提出了一种根据样本复杂度动态调整使用Transformer层数的样本自适应机制(sample-wise adaptive mechanism),通俗的理解就是简单样本可能只需要过前面几层Transformer层就能得到分类结果,那么就不需要再过后面的层,自然就节约了线上推理的时间,对于复杂的样本可能才需要经过完整的十二层Transformer。这就是FastBERT能又快又好的提供线上推理服务的原因。其实想想这完全是合情合理的。假如你是公司的老板,能用几个人搞定的工作何必要让所有人都去参与?
2.2 模型整体结构
FastBERT模型整体结构如下图所示:
FastBERT模型结构主要由骨干Backbone和分支Branch组成。骨干Backbone主要由embedding层、Transformer组成的编码器以及老师分类器三部分组成,这个结构和常规的BERT文本分类器结构很相似。Embedding层主要将文本数据转化成三层embedding,分别是词embedding、句子对关系embedding和位置embedding,然后将三者相加得到最终的embedding表示;Transormer组成的编码器则扮演特征抽取器的角色,底层的Transformer的结果作为上一层Transformer的输入,不断积累;有区别的是最后的老师分类器。常规的是将Transformer得到的[CLS]向量再经过一层全连接层得到分类的结果。这里的老师分类器会将提取到的特征分别经过一个768缩小到128的全连接层、一个矢量大小不变但是有自注意力机制self-attention的全连接层和一个将向量投射到分类数目N的带softmax函数的全连接层。
上面讲到FastBERT通过样本自适应机制来调整使用Transformer的层数来又好又快的进行线上推理,具体做法是通过自蒸馏self-distillation给每一层的Transformer后面再加入一层分类器。这里每层加入分类器就对应模型结构中的Branch分支。咱们对应原生基础BERT模型来说,原生基础BERT有十二层Transformer,那么就会有十一个学生分类器Student-Classifier,最后一个则是老师分类器Teacher-Classifier。
FastBERT通过不确定性来评估样本的复杂程度。如果样本比较难识别,那么不确定性就高。咱们对应模型结构图通过例子详细说明。模型结构图中左下角有四条样本,其中两条样本非常简单,所以它们的不确定性就低,经过第一个学生分类器就可以识别分类了,我们就可以给出这两条样本的预测结果,那么自然就不需要再经过剩下的十一层Transformer了,这对于线上推理的速度提升非常大;还有一条样本稍微复杂点,在经过第二个学生分类器时不确定性比较低,所以可以得到预测结果;只有最后一条样本非常复杂,经过完整的十二层Transformer后才最终被识别,基本上推理时间和常规模型是一样的。通过这么一个形象的例子咱们可以理解FastBERT是如何提升线上推理的速度。
这里咱们需要理解FastBERT中为啥叫自蒸馏self-distillation。通常的蒸馏是把大模型或者多个模型ensemble学到的知识想办法迁移到一个轻量级的小模型上,一般情况下大模型和小模型可能是不同的。但是FastBERT将主干作为老师模型,然后每层Transformer后添加一个分类器作为学生模型,最后让学生模型去学习老师模型的知识。因为归根结底老师模型和学生模型都来自一个模型,所以叫自蒸馏。这里小伙伴可能有疑问了,为啥不简单干脆点,直接用训练语料去学习学生分类器呢?FastBERT的作者通过实验证明自蒸馏得到的学生分类器比直接用训练语料得到的分类器效果好。下面是实验对比结果:
图3 实验对比自蒸馏和直接用训练语料得到的学生分类器效果
要解读上面的实验结论,这里需要明白论文中的speed概念。FastBERT通过调整参数speed值来动态调整线上推理的速度。Speed值越小,说明调整线上推理的速度越慢。这里通过极端例子说明,如果我们设置speed值为0,那么相当于我们调整线上推理速度最慢,其实就相当于使用原生的BERT模型来进行文本分类,也就是每条样本都会经过全部的Transformer层。反过来speed值越大,那么我们就希望线上推理的速度快一点,也就是说我们希望样本尽量经过更少的Transformer层。FastBERT通过调节speed值来自动调整线上推理的速度就是这个原理。从这个例子中咱们得到这样一个关联:speed值越小经过的Transformer层数越多,相反speed越大经过的Transformer层越少。明白了speed值的含义,在Book review数据集上设置speed值为0.2时自蒸馏的准确率为86.98%,没有自蒸馏的准确率仅为86.22%。其他数据类似,通过这个实验证明自蒸馏的方式的确能提升模型的效果指标。
小结下,FastBERT模型更像是一种一个师傅带十一个徒弟守关的任务。有文本数据来会分别经过前面十一个徒弟的关卡和最后的师傅关卡。简单的数据可能直接被守在前面的徒弟搞定,稍微复杂的可能被中间或者后面的徒弟拦截,只有最难的数据才会由师傅亲自处理。这样相比于厚重模型需要经过全部的Transformer层和轻量模型无法解决复杂数据来说的确能又好又快的提升模型线上推理能力。
2.3 模型训练和推理流程
上面分析了FastBERT模型的整体结构,下面看看它是如何进行模型训练和推理的。总体来说主要分成以下几个步骤:
预训练Pre-training。这和BERT模型是一样的。对大多数人来说直接把谷歌、Facebook等大厂训练好的预训练权重拿来用就好。这里推荐中文文本分类场景下使用RoBERTa-wwm-ext模型,具体原因分析可以查看我之前写的这篇文章:《广告行业中那些趣事系列18:RoBERTa-wwm-ext模型为啥能带来线上效果提升?》
主干精调Fine-tuning for backbone。使用下游任务的训练数据对模型进行微调,获得一个效果尽可能好的老师分类器。你训练的老师分类器效果有多牛基本上能决定学生分类器的能力;
分支自蒸馏Self-distillation for branch。上面两个步骤其实和常规BERT文本分类是一样的,而分支自蒸馏步骤则是FastBERT的不同之处。自蒸馏的时候可以使用无标注的数据,主要目的是把老师分类器学到的知识传授给学生分类器。使用KL散度衡量分布距离, KL散度公式如下所示:
同时使用所有分支分类器和主干分类器的KL散度之和作为损失函数loss,具体公式如下:
最后是自适应推理Adaptive inference。根据样本的复杂度来自适应的调整使用Transformer的层数,简单的样本浅层的分支分类器直接给出结果,复杂的样本则继续流入深层的分支分类器。FastBERT用预测结果的熵来衡量不确定性指标,具体公式如下所示:
公式中的N代表分类数量,Ps(i)代表分支分类器预测为类别i的概率。
2.4 FastBERT中重要的实验结论
(1)不确定性和模型效果的关系
因为FastBERT的自适应推理基于LUHA假设,LUHA假设认为不确定性越低,那么准确性就会越高。这个假设也比较好理解,不确定性越低,证明模型越有把握,那么得到的结果准确率应该越高。为了验证这个假设,FastBERT论文中得到了如下的实验结果:
图7 不确定性和模型效果的关系
上图中发现无论是老师分类器还是学生分类器不确定性较低时模型的准确率较高,相反不确定性较高时模型的准确率较低,相当于证明了不确定性越低准确性越高这个LUHA假设。但是通过上图很容易让我们产生一个误解:老师分类器的准确率还不如学生分类器,因为相同的不确定性老师分类器的准确率比学生分类器低。针对这个问题,FastBERT通过下图说明老师分类器的效果是优于学生分类器的:
上图是不同speed下不同层的不确定性分布情况。咱们详细解读下这个实验。前面说过,speed值越低,说明使用的Transformer层数越多,如果使用原生基础BERT模型,设置speed为0时就使用全部十二层Transformer,其实就对应FastBERT中的老师分类器,对应上面左上角的a图。可以看出老师分类器中样本在前两层的不确定性均匀分布,但是后面的层中基本分布在不确定性较低的区间内,也就是说分类器的效果较好。但是当speed值为0.8时,使用的Transformer层数较少,各学生分类器样本基本分布在不确定性较高的区间内,整体分类的效果较差。所以通过上面两个图得到两个重要的结论:老师分类器的效果是优于学生分类器的,不确定性越低准确率越高。
(2)FastBERT线上推理耗时分析
FastBERT相比于常规BERT在每层Transformer后面添加了学生分类器,这里需要验证Transformer的运算量要比分类器大的多。下图是对比FastBERT不同操作下的FLOPS值:
首先需要明确FLOPS是啥。FLOPS(Floating-point operations)是一种度量模型复杂度的指标,表示模型在单个过程需要执行的浮点运算量。通俗的理解就是FLOPs值越大,需要计算的量越大,推理的时间就越长。上图中发现一个Transformer的总FLOPS是1809.9M,而分类器的总FLOPS仅为46.1M,Transformer运算量基本上是分类器的39倍。通过这个实验说明模型线上推理的耗时主要还是在Transformer上,所以根据样本复杂度来动态调整需要经过的Transformer层数的确可以有效的提升线上推理的速度。
(3)不同speed对线上推理速度的影响
FastBERT通过调节speed值来动态的调整模型的线上推理速度。下图是对比Book review数据集下不同speed下各层处理的样本比例:
图10 不同speed下各层处理的样本比例
从上图中发现speed设置为0.8时,也就是使用更少的Transformer层数,第一个学生分类器就处理了将近60%的样本。而speed值为0.3时,第一个学生分类器只处理了25%左右的样本。这个对应到咱们线上推理服务时,当我们需要线上推理的速度非常快,那么我们会设置FastBERT的speed值很高,同时相当于告诉模型可用的Transformer层数很少,前面的学生分类器能处理的尽量处理掉,这样需要经过多层Transformer的样本数量就会少一点,那么线上推理的速度就会提升了。
(4)不同speed对模型分类效果的影响
这个实验需要验证不同speed对模型分类效果的影响。Speed值越大,模型的线上推理速度越快,FastBERT会让模型尽量在低层的学生分类器尽量把样本处理了,经过的Transformer层数少了,对应的效果可能会差一点。下面是实验对比图:
图11 对比不同speed下模型的分类效果
上图中红色方框内是对比了不同数据集下设置不同speed值对FastBERT模型效果的影响和运算量,可以发现speed值越小,FastBERT模型的分类效果越好,同时计算量越大。
(5)自蒸馏对FastBERT准确率和推理速度的影响
自蒸馏是FastBERT的关键步骤,也是最核心的创新点。作者通过下面的实验证明了自蒸馏可以有效提升模型的线上推理速度,具体如下图所示:
图12 自蒸馏可以有效提升FastBERT的线上推理速度
FastBERT在Book review数据集上首先进行三轮微调,然后进行五轮自蒸馏。从图中可以发现随着微调模型的准确率有巨大提升,同时随着自蒸馏模型的计算量大幅降低。通过这个实验可以说明FastBERT中的自蒸馏可以有效提升模型线上推理速度。
小结下,因为NLP数据集中样本的识别难易程度是不同的,所以需要经过Transformer的层数也不同。厚重模型对简单样本进行过度计算,而轻量模型无法识别复杂样本。针对这些问题,FastBERT模型提出了一种根据样本复杂度动态调整使用Transformer层数的样本自适应机制(sample-wise adaptive mechanism),训练阶段使用自蒸馏机制在每层Transformer后添加学生分类器,线上预测时通过设置speed值可以动态调整模型推理速度,从而最终达到了以更少的精度损失获得更高效的推理效率的目的。因为在工业场景中可以根据实际情况自行调整speed值从而调整线上推理的速度,所以非常实用。这里还需要说明FastBERT主要应用于文本分类、句间关系等这一类NLP任务,对于其他NLP任务需要进一步研究。
03 实战FastBERT
上面主要从理论角度分析为啥FastBERT可以又快又准的进行线上推理。下面咱们通过实战的角度来跑一下FastBERT模型。作者已经在github上开源了代码,所以小伙伴们可以通过如下地址去下载并运行FastBERT:https://github.com/autoliuweijie/FastBERT
04 总结
本篇主要分享基于BERT实现线上推理服务的FastBERT模型。首先讲了下为啥要用FastBERT模型。因为NLP数据集中不同样本的识别难度不同,厚重的模型会存在过度计算的问题,而轻量模型则很难识别复杂样本,FastBERT模型提出了一种根据样本复杂度动态调整使用Transformer层数的样本自适应机制(sample-wise adaptive mechanism),从而可以又快又好的提供线上推理服务;然后重点分析了FastBERT模型的来龙去脉,包括FastBERT模型的引入和优化思路、模型整体结构、模型训练和推理流程以及FastBERT论文重要的实验结论,从理论到实验论证了FastBERT模型可以又快又好的提供线上推理服务的原理;最后基于作者开源的代码实战了FastBERT。对BERT线上推理服务感兴趣的小伙伴可以一起了解下FastBERT模型。
参考资料
[1]论文:FastBERT: a Self-distilling BERT with Adaptive Inference Time
[2]github源码:github.com/autoliuweijie/FastBERT
最新最全的文章请关注我的微信公众号:数据拾光者。