点关注,不迷路,定期更新干货算法笔记~
今天给大家介绍ACL 2022的一篇文章bert2BERT: Towards Reusable Pretrained Language Models(ACL 2022),这是一篇研究如何将small-size的预训练语言模型的参数迁移到large-size模型的工作。小模型和大模型相比,每层的参数矩阵维度不一样,模型的深度也不一样,如何才能实现迁移呢?这篇文章会重点介绍bert2BERT,也会顺带梳理一下小模型向大模型迁移的历史工作。
论文题目:bert2BERT: Towards Reusable Pretrained Language Models(ACL 2022)
下载地址:https://arxiv.org/pdf/2110.07143.pdf
bert2BERT这篇论文的标题很有意思,第一个bert是小写的,第二个BERT是大写的。它的含义是如何将一个small预训练语言模型(bert)作为large预训练语言模型(BERT)的初始化,以此提升大模型的收敛速度,节省大模型训练资源,让训练好的小模型不被浪费,可以说是非常环保了。如果能实现小模型向大模型进行迁移,那么我们在训练BERT Large的时候,就可以不再from scratch训练了,而是利用BERT Small的参数作为初始化,提升训练效率。
如何用一个小模型的参数去初始化大模型参数?这个过程面临的最大问题是两个模型的参数尺寸不同,没有办法直接把小模型的参数拿过来初始化大模型参数。对于每层的参数矩阵,小模型的尺寸都比大模型的尺寸小;对于网络整体来说,小模型的层数也比大模型层数少。在网络尺寸和网络深度上,之前都有过类似的从小迁移到大的工作,下面我们先来回顾一下历史工作,而bert2BERT也是建立在这些工作基础之上。
1
Net2Net和StackingBERT
这里主要回顾两篇历史工作,一篇是Net2Net: ACCELERATING LEARNING VIA KNOWLEDGE TRANSFER(ICLR 2016),主要解决的是如何将一个参数矩阵从小到大的迁移方法;另一篇是Efficient Training of BERT by Progressive Stacking(ICML 2019),主要解决的是如何让BERT的网络层数从浅到深。
Net2Net这的核心思路是用小模型初始化大模型,使得大模型不再需要从0开始训练,而是利用小模型的参数初始化,加快大模型收敛速度。两者的对比如下图。
Net2Net的核心思路是,当我们有一个小模型(teacher model)和一个大模型(student model)时,我们将参数矩阵进行扩展,并且保证原来的函数不变。网络中的每一层都可以视为由某个参数矩阵定义的函数,小模型到大模型对每层来说,就是将参数矩阵从m*n变成更大的M*N的过程,并且保证这个函数的输入输出不变。
如下面这张图所示,左右两侧的模型虽然中间的hidden state尺寸不一样,但是输入和输出是完全相同的,可以看成是尺寸更大的相同函数。
另一篇文章提出的是BERT深度上的优化StackingBERT。就像这个方法的名字一样,这篇文章的核心是,先训练浅层的BERT,再通过堆叠的方式不断套娃,逐渐提升BERT深度。这样我们不再需要直接训练一个大BERT,而是先训练浅层BERT,减小训练开销。
文中首先分析了Transformer不同深度的层Attention的分布图像。从图像中可以看出,低层的Attention和高层的Attention分布其实非常相似,表明不同层的注意力机制可能是非常相似的函数。并且之前的一些工作也有过类似的结论,认为Transformer可以实现不同层之间的参数共享。
基于这个发现,作者认为我们可以将一个已经训练好的L层BERT,通过参数复制堆叠的方式,生成一个2L层的BERT。这样的好处在于,我们可以先训练一个浅层BERT,然后逐渐加深BERT的尺寸,提升训练的效率。
StackingBERT的核心算法如下所示,首先初始化一个浅层BERT,然后不断的进行训练、堆叠、再训练的过程,让BERT模型越来越深。
2
bert2BERT核心做法
bert2BERT的核心思路很大程度上基于上面介绍的两篇历史工作。首先,为了解决参数矩阵大小不一样的问题,本文提出了Function Preserving Initialization(FPI)方法,核心思路是通过对小模型参数矩阵的扩展,生成适配大模型的参数矩阵。一个简单的示意图如下,先让矩阵的输入维度(din)相等,再让矩阵的输出维度(dout)相等。
在进行参数矩阵扩展的时候,文中提到的一个原则是,在参数矩阵扩展前后能保证原始的输出是不变的。下图是一个简单的2层网络FPI例子。原始的输入维度和输出维度都是2,目标模型的输入维度和输出维度都是3。第一步让输入维度扩展为3。第一步让输入维度din变成3,从前面两列的参数中均匀采样,比如这里采样到了第一列填到第三列。为了让下一层输出保持不变,需要把这两列的参数都设置为原来的一半。接下来需要在dout上进行扩展,这里直接复制h2一份,对应的参数矩阵把最后一行参数进行复制。经过上述操作,保证了最终模型的输出不变。
为了进一步提升大模型收敛速度,文中在FPI的基础上提出了Advanced Knowledge Initialization(AKI),在进行参数扩展时不仅考虑当前层的参数,还会考虑下一层的参数。以前的工作表明Transformer中的相邻层会学习到相同的函数,并且根据Net2Net这篇工作,打破对称性对模型收敛有一定好处。因此,AKI在扩展参数的时候会同时考虑当前层和下一层的参数。具体实现方法如下,在对当前层和下一层进行din扩展后,使用下一层的参数对当前层进行dout扩展。虽然AKI方法和FPI中保证扩展前后输出不变的原则矛盾,但是通过后续的实验作者表明AKI是可以提升收敛速度和模型效果的。
在完成每个参数矩阵的扩展后,下一步就是对网络的层数进行扩展,让模型变得更深。这里采用了Efficient StackingBERT中的方法,假设原始模型层数为L,新模型第L i层的参数使用第i层的参数进行初始化。
为了减小训练开销,本文使用两阶段的训练方法。第一阶段先对大模型中的子结构进行训练,每次从大模型Transformer中选择一个substructure,搭配上一个共用的分类层,每次训练只更新这个substructure和分类层。相比于直接大模型整体训练,这种方法可以降低开销,能够让每个substructure先达到一个接近收敛的状态,提升后面模型整体训练的收敛速度。在第二阶段,在第一阶段的基础上,对模型整体进行训练。
文中最后通过一些实验对比了不同训练方法的收敛速度、训练开销和最终效果。在收敛速度上,bert2BERT的收敛速度要快得多。在训练开销上,bert2BERT的两阶段训练方法可以节省45.2%的计算资源。
3
总结
通过bert2BERT这篇文章,我们了解了如何将一个小模型,从模型参数和模型深度两个维度进行扩展,用于初始化一个大的模型参数。这类工作让我们可以在训练大模型的时候不再from scratch,而是建立在已经训练好的小模型基础上,提升了大模型的收敛速度,减小了模型训练的开销,在实际工作中值得一试。
END