字幕组双语原文:NLP 模型压缩方法综述
英语原文:A Survey of Methods for Model Compression in NLP
翻译:wiige
近年来,基于Transformer的语言模型在神经机器翻译,自然语言推理,and 其他一揽子自然语言理解任务中取得了实质性进展。 采用不同语言建模损失进行自监督的预训练,意味着模型可以通过大规模语料来提高许多下游任务的性能。 然而,海量参数和长计算轨迹意味着BERT及其变体在生产部署中困难重重。 值得庆幸的是,在过去2年里,我们看到了各种各样的技术的发展,它们缓解了这些苦痛并加速了模型的预测。具体来说,本文将重点介绍下面这套模型预训练后可以打的组合拳,这能降低模型预测的计算成本:
数值精度缩减: 通过减少浮点数精度和量化来加快速度
操作合并: 在计算图中合并所选节点
剪枝: 识别并删除网络中的非必要部分
知识蒸馏: 训练高效的小规模学生模型,以模仿表达力更强、开销更大的老师模型
模块替换: 通过替换来降低模型的复杂性或深度
数值精度缩减:
数值精度缩减可能是加速模型预测的最通用方法。在过去的几年里,GPU硬件对16位浮点数运算的支持不佳,这意味着降低权重和激活函数的计算精度往往效果适得其反,但带有Tensor Core的NVIDIA Volta和Turing 架构的引入意味着现代GPU现在已经具备了高效的16位浮点数运算能力。
浮点数的表示
浮点类型存储三种的数值信息——符号、指数和分数。 传统的32位浮点数表示法用8位表示指数,用23位来表示尾数。 而传统的16位浮点数表示法(即NVIDIA硬件使用的格式)将32位表示法中的指数和尾数位差不多减少了一半。TPU则用了一种名为 bfloat16 的表示方法,它将部分比特位从尾数移至指数,以部分精度为代价换取了更大的数值表示能力。
图源: https://cloud.google.com/tpu/docs/bfloat16
Transformer网络大部分的都可以简单地转换为16位浮点权重和激活,而不会产生精度问题。 而网络剩下的一小部分——特别是softmax操作这部分——必须坚持使用32位浮点数。 这是因为大量小数值(对数计算产生的)的和可能会累积出很大误差。因为同时使用了float16和float32,这种方法通常被称为"混合精度".
较不精确的数值表示法能够从两个方面加速计算:
原生半精度指令
更紧凑的表示使得批尺寸(batch size)更大
NVIDIA已经发布了一套与浮点精度缩减相关的通用基准——在实践中,这种方法可以实现高达3倍的加速。
整型量化
将32位浮点值量化为8位整型值也是可能的,但应用起来颇为微妙。 特别是,为了确保8位整型值的计算尽可能地接近32位浮点值的计算,训练后必须要增加一个校准步骤.
如果你知道一个网络的激活值可能在什么样的区间内,你可以把这个区间划分成256个离散的块,并将每个块分配给一个整数。 只要你记得了缩放因子和区间范围,就可以用整数近似值进行矩阵乘法,并在输出的时候结果恢复为浮点值.
图源: Szymon Migacz's 演讲, "8-bit Inference with TensorRT"
简单来说,你可以选择一个缩放比例和偏移量,使得一组校准输入上的全部浮点数激活都不会被映射到8位整型值表示范围(-128,127)的端点值上。 然而,在这样做的过程中,为了适应极端的值我们牺牲了一些精度。 相反,像TensorRT这样的框架会选择规模和偏移值,来最小化32位浮点版本和8位整型版本的模型激活输出之间的KL散度,这使得我们原则上可以权衡好范围和精度。 由于KL散度就是不同编码下的信息损失量,所以它完美符合计算需求.
下面的资料介绍了如何使用NVIDIA的TensorRT将8位整型值量化应用到自己的模型中:
Tensorflow TensorRT 用户指南
Tensorflow TensorRT Github
网络层合并与计算图优化
除了浮点缩减和量化,操作合并也为更高效的预测提供了一个实用而通用的选择。合并的基本原理是将一些网络层执行的操作结合起来,以更高效少次地访问设备内存。 通过将多种操作合并到一个核(kernel)中,可以大幅提高访问内存的速度。
上图中,将跳连的求和操作与层标准化的缩放和偏置合并.
软件优化还可以让我们重组一些矩阵乘法,以更好地利用并行性。 特别是,这可以将自注意力层的查询、键和值投影合并到一次矩阵乘法中去。
图片由NVIDIA开发者博客提供
不幸的是,关于这种图优化所带来的速度提升幅度的细节很少,但我的乐观估计是,这种改进是渐进但不可忽视的——它会在吞吐量上提升10%。
剪枝
除了纯粹地提高软件效率之外,还有一些方法可以用来剪枝神经网络,去除那些对最终模型贡献最小的权重。 许多剪枝方法(如Fan等人的 "Reducing Transformer Depth on Command With Structured Dropout")需要在预训练期间对网络进行修改,以产生足够稀疏的模型,而且可在训练后再进行剪枝。 剪枝相关的其他文献则侧重于理解习得的连接模式的稀疏程度,而没有以高效预测为目的(例如,Gorden等人的 "Compressing BERT: Studying the Effects of Weight Pruning on Transfer Learning")。
虽然所有这些方法本身都很意思(结构化层丢弃在实际应用中表现出巨大的前景),但我对那些可以在部署应用并仍然提升性能的方法更感兴趣。这类方法通常基于"模型中只有一部分是解决具体任务所必需的"这一事实。
为获得性能提升而进行剪枝对结构化的稀疏性有所要求。 简单地将奇异权重归零并不能有效产生性能提升,因为我们没有实际的方法来利用这种稀疏性。 所以我们必须剪掉网络中更大的部分,才能产生实际的性能提升。
注意力头剪枝
"Are Sixteen Heads Really Better than One?", 一文中,Paul Michel、Peter Levy和Graham Neubig迭代地从BERT中减少头的数量. 他们使用基于梯度检测的方法(梯度是在下游任务上估计出来的)来估计每个头的重要性,并以头剪枝百分比作为性能的函数来评估模型对头剪枝的鲁棒性。
在实践中,作者发现20 - 40%的头可以剪枝,它们对精度的影响可以忽略不计。
资料来源 https://arxiv.org/abs/1905.10650
通过门控在精调过程中剪枝
在J.S. McCarley和Rishav Chakravarti以及Avirup Sil的"Structured Pruning of a BERT-based Question Answering Model"中,作者探索了一种更通用的模型剪枝方法。 作者没有只关注注意力头,还对每一层的输入以及每个BERT层的前馈层的激活进行了门控。
他们探索了几种机制来选出要剪枝的网络元素——包括Michel等人提出的方法——最终确定了一种L0正则化项,它可以用在精调期间,提高模型的稀疏性。为了使这个L0正则化项可微,他们采用了类似于变分自编码器中的重参数化技巧。
图片来自 Christos Louizos, Max Welling和Diederik P. Kingma的文章 "Learning Sparse Neural Networks Through L0 regularization"。
他们实验发现, 用稀疏性惩罚来微调比"Are 16 Heads Really Better than 1 "中提出的重要性估计方法更胜一筹,并且他们发现可以多去掉近50%的前馈激活,而对短问题回答任务(他们视之为基准任务)的性能影响可以忽略不计。
在Squad 2.0上剪枝模型的注意力头和前馈激活的鲁棒性。
为进一步加速模型,作者的还推荐使用下一个技术——"知识蒸馏"
知识蒸馏
基本概念
知识蒸馏是由Geoffrey Hinton, Oriol Vinyals, 和Jeff Dean在2015年的工作"Distilling the Knowledge in a Neural Network"中提出的, 知识蒸馏是指到将一个网络("教师")中包含的知识通过特定的修正损失迁移到另一个网络中去("学生")。
首先想象一下,我们有一大堆无标记的样本。如果我们信赖教师模型的预测,但其模型太过庞大或计算成本太高而无法在实际环境中使用,那我们就用教师模型来分类无标记的样本,并将这些分类信号作为监督信号馈给学生模型。 然而如果不将对应类别的最大似然作为最终目标,而是在所有可能的类别上产生一个概率分布,那么学生模型就可以获得信息更丰富的监督信号。
直觉上,学生模型所犯的某些错误比其他错误更合理——把勺子的图认成哈士奇明显就走远了,但把哈士奇误分为一只阿拉斯加就比较想得通了。所以损失函数应该反映出错误的严重程度。通过惩罚教师预测和学生预测之间的差异(鼓励对数匹配),学生可以从教师网络也觉得可能的类别中学习有用信息。作者认为,在原任务上用仅3%的训练数据就可以几乎实现教师网络的性能。
有样东西和两个完全不一样。图源维基百科,遵循CC BY-SA 3.0协议发布。
有证据表明,大量参数可能是样本利用率高的关键,而且在同样时长内把大型语言模型训练到某个困惑度也可能比训练一个等效的紧凑模型更有高效,因此,高效地将这些习得的知识迁移到紧凑的学生模型上的方法拥有光明的未来。
相似模型架构的蒸馏
在前面讨论的"Structured Pruning of a BERT-based Question Answering Model" 中作者利用知识蒸馏方法,将未剪枝的教师模型中的知识迁移到剪枝后的学生模型上。 在中立问题数据集(Natural Questions)上,教师模型在长回答和短回答上的F1值分别为70.3和58.8。剪枝50%左右的注意力头和前馈激活后,F1分别下降为67.8和55.5 ——平均下降了约2.5。 如果在微调过程中用蒸馏损失来代替交叉熵损失,F1则可以恢复1.5到2个点,F1分别达到了69.3和58.4。
知识蒸馏有助于恢复剪枝过程中丢失的信息。
Hugging Face的Victor Sanh、Lysandre Debut、Julien Chaumond和Thomas Wolf在其文章"DistilBERT, a distilled version of BERT: smaller, faster, cheaper and lighter"中, 在对遮罩语言模型的二次预训练中将知识从基线BERT模型蒸馏到了6层BERT学生模型上。 令人印象深刻的是, (以任务无关的方式训练的)学生模型保留了97%的GLUE性能,同时还减少了60%的预测时间.
在 TinyBERT: Distilling BERT for Natural Language Understanding一文中, Xiaoqi Jiao, Yichun Yin, Lifeng Shang, Xin Jiang, Xiao Chen, Linlin Li, Fang Wang 和 Qun Liu 将一个BERT教师模型蒸馏到了一个隐层大小为312的4层Transformer学生模型上。 模型在预训练时和微调时都进行了知识蒸馏,得到的学生模型GLUE得分实现了BERT基线模型的96%,而软件尺寸减小了7.5倍,推理速度快了近10倍。
在 "Patient Knowledge Distillation for BERT Model Compression"这篇文章里, Siqi Sun, Yu Cheng, Zhe Gan, Jingjing Liu 在12层BERT老师模型和6层BERT学生模型的许多中间表征上加入了知识蒸馏损失,相较于仅对模型logits应用知识蒸馏损失的基线模型,这样能在5/6个GLUE评分项上实现更高的准确率。
相异模型架构的知识蒸馏
在前文提到的论文中,教师模型和学生基本架构是相似的,教师模型的权重通常用来初始化学生模型的权重。然而,即使在教师模型和学生模型架构差异巨大的情况下,也可以应用知识蒸馏损失。
在 "Training Compact Models for Low Resource Entity Tagging using Pre-trained Language Models"一文中, 英特尔AI实验室的Peter Izsak、Shira Guskin和Moshe Wasserblat将一个在命名实体识别任务上训练的BERT教师模型(约330M个参数)蒸馏成了一个明显更紧凑高效的CNN-LSTM学生模型(约3M个参数)。 这样的学生模型以最少的精度损失在CPU硬件上的提速高达2个数量级。
"Distilling Transformers into Simple Neural Networks with Unlabeled Transfer Data"中,Subhabrata Mukherjee和Ahmed Hassan Awadallah将BERT-Base和BERT-Large教师模型蒸馏成了一个BiLSTM学生模型,在4个分类任务(Ag News、IMDB、Elec和DBPedia)中用少量参数(13M)就战平了教师模型。 他们还发现,知识蒸馏使得样本利用效率有了很大的提升,每个任务只需要500个带标注样本就能达到(使用大量无标注数据的)教师模型性能。
在 "Distilling Task-Specific Knowledge from BERT into Simple Neural Networks" 中,Raphael Tang、Yao Lu、Linqing Liu、Lili Mou、Olga Vechtomova和Jimmy Lin通过少于1M参数的单层BiLSTM在各种句对任务(QQP、MNLI等)上的实验得出了类似结论。
在 "Attentive Student Meets Multi-Task Teacher: Improved Knowledge Distillation for Pretrained Models" 中, Linqing Liu, Huan Wang, Jimmy Lin, Richard Socher, 和Caiming Xiong 将多任务学习与知识蒸馏方法结合起来,将Transformer教师模型的知识迁移到一个带注意力机制的深度LSTM中。 他们发现,知识蒸馏的好处与多任务学习框架的泛化优势融合得很好,预测速度提升至耐心知识蒸馏(Patient Knowledge Distillation)模型的30倍,TinyBERT的7倍。
知识蒸馏近来风靡各界的原因很好懂——基于Transformer的语言模型参数量涨个没完——而可能就是专治参数爆炸的灵丹妙药。 如果我们要让这些吃显存的无底洞切实落地,就需要像知识蒸馏这样的方法来保持模型预测的高吞吐量。
模块替换
列表中的最后一篇文章有两个与众不同之处:一是采用了比较新颖的模型压缩方法,二是文章里有这么张图:
这图要是做成T-shirt的话我必穿来游街。
BERT-of-Theseus: Compressing BERT by Progressive Module Replacing 是Canwen Xu, Wangchunshu Zhou, Tao Ge, Furu Wei, and Ming Zhou发表的文章。 忒修斯BERT(BERT-of-Theseus)没有单独训练一个学生模型来最小化知识蒸馏的损失,而是在模型精调时,每批(batch)以一定的概率用 "后辈模块"(新模块的块)随机替换一个 "先辈模块"(即原模型的块)。
忒修斯BERT是对 "忒修斯之船 "悖论的延展,这个悖论探讨的是一艘船在经过不断的细小维修和升级后,是否还是那一艘船。 忒修斯BERT将这种渐进式替换的思想应用在了模型压缩上。
后辈模块是低配的先辈模块——下图这种情况里,单个Transformer的层替换掉了一个双层Transformer组成的块 。 与知识蒸馏不同的是,模块替换中没有使用损失来鼓励后辈模块模仿先辈模块。 实际上,是通过后辈和先辈模块的互换使用来鼓励后辈学习模仿先辈的行为。
ArXiv论文中的"忒修斯BERT"图示
这种隐式行为的一个好处是,我们不再需要选择如何用目标模型损失来加权各种知识蒸馏损失——因为它通常需要一个超参数α,使得模型损失的形式为L=αLKD+(1-α)LCEL=αLKD+(1-α)LCE。 与TinyBERT不同的是,它没有二次预训练这一步——模型压缩与下游精调是同时进行的。 最后,渐进式模块替换方法也适用于模型架构不同的情况——它在设计中就没有利用Transformer的任何具体特征。
论文作者用线性学习率进行了实验,他发现随着时间的推移,线性增加模块的替换率比恒定的替换率效果要好。
忒修斯BERT的替换率的比较实验
为了测试其方法的鲁棒性,作者在GLUE跑分的时候在BERT-base上用了"忒修斯压缩",这轻松超越了几种基于知识蒸馏方法的性能,在将原始模型压缩到50%大小的情况下,仅仅落后BERT-base不到1个百分点。
忒修斯BERT的GLUE跑分结果
我觉得渐进式模块替换方法十分诱人的部分原因是,它打开了用实验方法提高其他模型吞吐量的大门, 而以前模型通常需要从零开始重新训练。 对于独立研究员和小公司来说,从头开始重新训练Transformer模型的成本通常是难以承受的,所以哪些提出了更高效模型的好点子但没发布预训练模型的论文就很难复现。
我很想看看忒修斯BERT提出的渐进式模块替换, 是否能够很好地替换
"Fast Transformer Decoding: One Write-Head is All You Need"中提出的共享键和值的预训练注意力模块,或者换成一个稀疏的注意力等价模块。更一般地讲,我们需要继续开发一些方法,这些方法既能利用语言模型预训练过程中的大量计算开销,又能让我们事后进行修改,以便使这些昂贵的模型能适应特定任务。我对未来研究如何在这一的基石上向前发展翘首以盼。
延伸阅读
如果您有兴趣了解更多关于基于BERT模型的更高效预测方法,您可能会喜欢下面的:
Prakhar Ganesh , Yao Chen , Xin Lou , Mohammad Ali Khan, Yin Yang , Deming Chen , Marianne Winslett , Hassan Sajjad and Preslav Nakov的Compressing Large-Scale Transformer-Based Models: A Case Study on BERT
Michael Gordon的一篇博文:All the Ways You Can Compress BERT,我写这篇文章的时候经常引用之。
雷锋字幕组是一个由AI爱好者组成的翻译团队,汇聚五五多位志愿者的力量,分享最新的海外AI资讯,交流关于人工智能技术领域的行业转变与技术创新的见解。
团队成员有大数据专家,算法工程师,图像处理工程师,产品经理,产品运营,IT咨询人,在校师生;志愿者们来自IBM,AVL,Adobe,阿里,百度等知名企业,北大,清华,港大,中科院,南卡罗莱纳大学,早稻田大学等海内外高校研究所。
如果,你也是位热爱分享的AI爱好者。欢迎与雷锋字幕组一起,学习新知,分享成长。
雷锋网版权文章,未经授权禁止转载。详情见转载须知。