承接上一篇BERT预训练流程的文章,今天主要通过在Transformer架构上进行改造来提升BERT训练效果,具体为:使用attention残差机制改造Transformer。其实现参考自去年年底谷歌发表的一篇论文:RealFormer: Transformer Likes Residual Attention 。之所选择这个方法来改造BERT,主要还是在于它的思想和实现都非常简洁,且有一定的理论依据。苏神在去年年底已经写过有关该论文的解读,个人觉得其对于RealFormer的背后机理的分析已经很透彻,大家可以前往阅读:RealFormer:把残差转移到Attention矩阵上面去 (https://spaces.ac.cn/archives/8027)。本文会更多得从实操和结果分析来验证RealFormer的效果。实际上,我从实验的结果中确实找一些比较有趣的结论,拉出来给大家探讨一下。
RealFormer原理简析
postLN的缺陷
RealFormer被提出的引子来源于Transformer中的post layer normalization(以下简称postLN)的缺陷。原始的Transformer的架构中,将layer Normalization放在了Add操作(也就是残差计算)的后面,相当于是layerNorm(x f(x)),原本在Transformer每一层内部引入残差机制是为了防止信息在传递过程被遗忘而产生梯度的消失。然而加了layerNorm的归一化操作后,残差的作用被大大削弱了,信息并没有走快速通道直接到达下一层。postLN的信息传递如下图所示:
以梯度传递的角度来看,postLN的梯度计算如下所示:
其中,
表示模型的loss,
表示最靠近输出层的output,图中两个红框分别表示layer normalization的梯度传递和残差计算的梯度传递。可以看到,本层的梯度信息并没有以残差概念中的直通路传递到其他层,而是要经过layer normlization的影响。当模型层数非常深的时候,有较大风险会发生梯度消失。如苏神的文章中所说,训练初始阶段,前面的层梯度很小,后面的层梯度很大。因此在训练这种架构的时候,通常需要增加warm-up机制,降低模型学习的难度。
如果将layer normalization放在残差前面,就变成了preLN,其信息传递的流程如下图所示:
其梯度计算如下所示:
可以看到,此时前层的信息是可以通过残差机制直接传递到下一层中的,并不会被layer normalization影响。
上述分析均可以从两篇论文中得到,分别为On Layer Normalization in the Transformer Architecture 以及Learning Deep Transformer Models for Machine Translation
那么preLN是否可以解决上述问题呢?从理论上应该是可以的,但是从很多论文的实验来看,似乎并非如此。虽然preLN能提升模型在训练上的效率,但是最终模型的训练效果仍然会比postLN会差一点。关于这一点,苏神在文章中给出了分析,不过目前也没有实际的理论来解释验证这个现象。
RealFormer的实现原理
RealFormer为了解决postLN的梯度问题,同时又为了保留postLN的模型训练效果,设计了一种非常简单高效的优化方法,即在原始的postLN的基础上,在旁路增加一条额外的通路,将attention的信息进行残差的传递,其流程如下图所示:
RealFormer架构
注意到,多出来的这条通道中传递的信息是attention信息而不是之前的token embedding信息。具体来说就是在计算多头注意力分数时,使用softmax归一化之前的那个attention score:
至于RealFormer为什么会比postLN和preLN两个方法更好呢?苏神在其文章中有一番分析,个人还是比较认同这个分析,因此就不赘述了,大家可以去其博客中阅读。
RealFormer实现
RealFormer的实现也是非常简单的,github上有其pytorch的版本,不过我之前BERT使用的版本都是基于tensorflow的谷歌官方版本,因此需要自行在原版bert代码中添加一些代码来实现,具体主要在modeling.py中的两个地方进行改动:
1、attention layer的实现代码中,添加attention score residual的计算机制,引入上一层的prev_attens来保存记录之前上一层传递下来的attention分数。
代码语言:javascript复制def attention_layer(...,prev_attens=None):
....
attention_scores = tf.multiply(attention_scores,
1.0 / math.sqrt(float(size_per_head)))
if prev_attens != None:
attention_scores = prev_attens
prev_attens = attention_scores
....
return context_layer,prev_attens
2、在transformer_model中,添加一个变量存储prev_attens,并记录更新后的prev_attens.
代码语言:javascript复制prev_attention = None
for layer_idx in range(num_hidden_layers):
....
attention_head,prev_attention = attention_layer(...,prev_attens=prev_attention)
RealFormer的验证效果
我将RealFormer运用在了Bert的领域内post-training中。训练的设置跟上一篇中介绍的大致相同,采用4-GPU的多卡训练,每张卡的batch size为12,梯度累积步数为8,在15G左右的金融领域的新闻语料上进行训练,训练时并不是从头开始初始化模型权重,而是基于的哈工大的Chinese-wwm-roberta预训练权重来训练,由于在transformer的架构上做了修改,因此训练的mlm_loss曲线与之前的继续训练不太一样,如图所示:
刚开始的loss还是很高的,从8左右的数值开始下降,而之前采用相同的transformer架构进行继续训练,loss直接从2开始下降。另外,使用了realformer 继承Chinese-wwm-roberta预训练权重后,模型在初期的loss下降速度就比较快,在1000步左右的时候就降到了2以下。
除了训练的效率之外,当然是关注预训练的效果。我在两个公开的文本分类数据集(thuc的中文文本分类部分数据集,取自github.com/649453932/Be,是一个20多分类任务;另外一个是IFLYTEK' 长文本分类,取自CLUEbenchmark/CLUEDatasetSearch,是一个119多分类任务),以及两个内部的文本数据集(一个是三分类的情感分类任务,另一个是140多分类任务)上进行了验证。验证主体包含三个预训练模型:
1、原始的Chinese-wwm-roberta预训练模型。
2、采用原始的transoformer架构,在1/3数据量上训练了30万步的小模型hf-bert-temp
3、使用了realformer的架构,在完整数据量上训练了300万步的模型realformer-bert-temp2
目前Bert的预训练优化计划还在进行中,且训练还未结束,因此,该结果只是一个大致的参考,不一定准确。
通过上述实验后,对于realformer,我得到了一些结论,有些在意料之中,然而有些却出乎意料,下面我就分享一下供大家参考:
1、在分类标签较少的场景下,realformer最终的效果视情况而定,具体如下:
(1)在thuc的训练效果上,三个模型在测试集上的准确率等指标都差不多,并没有明显的差距:
原始roberta(thuc)
hf-bert-temp(thuc)
realformer-bert-temp2(thuc)
这个数据集上由于文本内容较为通用,且本身roberta对于该数据集已经有不错的效果,因此使用金融领域语料 realformer架构进行in-domain pretraining后,不会在该数据集上有明显提升。
相对的,在金融领域下的高难度任务下,金融领域语料 realformer架构还是有一定的提升,例如在我们内部的篇章级情感分类任务上,三个模型在测试集上的效果如下:
原始roberta(情感分类)
hf-bert-temp(情感分类)
realformer-bert-temp2(情感分类)
2、在类别比较多的多分类问题上,realformer架构似乎出现了很大的问题。在我们自己的多分类数据集上,realformer-bert-temp2在训练初期阶段就过早陷入了局部极小loss的陷阱,其模型对于最后测试集的预测结果几乎都是同一个类别,很明显是模型学习学偏了,其训练的loss曲线如下:
realformer-bert-temp2 loss曲线
可以看到在整个训练阶段,其loss都在某个局部区域内震荡。而使用非realformer架构的hf-bert-temp,其训练曲线则是比较正常的:
hf-bert-temp loss曲线
而在公开的iflytek的文本分类任务上,虽然realformer-bert-temp2的训练过程相对于上面的来说是正常下降的,但是最后收敛时的loss还是比hf-bert-temp的高,如图所示:
realformer-bert-temp2 loss曲线(iflytek)
hf-bert-temp loss曲线(iflytek)
最后在测试集上的效果也是与上述现象一一对应,如下所示:(类别过多,就不截每个类别的指标了,三个指标分别对应precision ,recall,F1)
原始roberta(iflytek)
hf-bert-temp(iflytek)
realformer-bert-temp2 (iflytek)
可以看到realformer-bert-temp2 还是不如hf-bert-temp,甚至跟原始roberta相比,效果也是降低的。
那么到底是否是因为分类类别较多的原因导致realformer反而拖累了模型效果呢?我后续又从两个方面取做了验证:
1、将分类的标签数量减少是否能提升realformer-bert-temp2的效果?由于iflytek的数据量本身就比较少,不适合在减少数据量训练,因此我选择了内部的自定义的140分类数据集来做该实验。分别随机采样了20,50,80,100个标签数量,分别做finetune的实验。最后可以发现realformer-bert-temp2在20,50,80的标签数量上可以很好的cover,但是再增加标签数量的时候,就一直会遇到上述的训练震荡问题。即使是使用20,50,80的标签数量进行训练,其训练曲线也比较奇怪,会在训练初期阶段有一段loss不变的时段,其后在某个点会正常的下降,下图为50分类时的训练曲线:
realformer-bert-temp2 50class
下面再贴一张hf-bert-temp在50分类任务上的训练曲线,以作对比:
hf-bert-temp 50class
2、增加realformer-bert-temp2的预训练步数,让模型更充分的学习是否能提升realformer-bert-temp2的效果?我分别在模型的100万步和300万步取了两个阶段的预训练权重,并分别在15,20,30,80个标签数量上进行对比实验,最后发现100万步的模型在20标签分类任务上就不能正常学习了,而300万步在80标签分类任务上仍然可以正常学习。
从上述实验来可以看出,realformer-bert-temp2对于一定标签数量的多分类任务还是具有一定的学习能力,但是当标签数量超过一定的数量时,其效果就会大打折扣,甚至在某些数据集上无法正常学习。
抛出问题
在得出上述的结论时,我是很困惑的。检查了自己的代码后发现并没有实现上的错误,同时realformer-bert-temp2对于小规模标签数量分类任务的效果还是很符合直觉的,因为在原始论文中,实验针对的数据是GLUE,其分类任务的标签数量都比较少,因此效果都不错。
那么,到底是什么原因导致了realformer不能cover标签数量较多的分类任务呢?一方面是标签多的分类任务也通常更难学,另一方面上述数据集中或多或少存在分类不均衡的问题。但是这些都不是核心原因,因为原始的bert并没有遇到上述问题。在读了论文,以及参考了苏神的博客后,我仍然没有得到一个合理的解释,以我现在的知识暂时还是没办法解决这个问题。因此,我在最后把这个问题抛出来,希望感兴趣的同学可以研究研究。
总结
本文是我不懂BERT系列的第二篇,主要描述了如何使用RealFormer改进Transformer的架构,并将其应用到BERT的预训练的过程。通过一系列的实验对比,最后发现了一些有趣的结论,包括:
1、realformer在标签数量较少的分类任务上有一定的提升效果,提升的幅度与数据集和任务难度有关,一般越难的任务提升的幅度越大。
2、realformer在标签数量达到一定的数值时,其效果便会大打折扣,在某些数据集上甚至会无法学习。
关于第二个结论,目前尚无合理的解释。当然,在应用realformer时可以参考本文中的结论,对于标签数量不是太多的分类任务可以尝试使用,大概率会有效果提升。
- END -