Press "Enter" to skip to content

抛开约束,增强模型:一行代码提升 ALBERT 表现

©PaperWeekly 原创 · 作者|苏剑林

 

单位|追一科技

 

研究方向|NLP、神经网络

 

本文标题看起来有点“标题党”了,不过所作改动放到 bert4keras 框架 [1] 下,确实是一行代码的变动,至于是否有提升,这个笔者不敢打包票,不过测了几个算是比较有代表性的任务,均显示持平甚至有提升,所以标题说的也基本是事实。

 

那究竟是什幺改动呢?其实一句话也能讲清楚:

 

在下游任务中,放弃 ALBERT 的权重共享的约束,也就是把 ALBERT 当 BERT 用。

 

 

ALBERT是什幺

 

这个改动是专门给 ALBERT 设计的,所以要理解这个改动,需要先知道 ALBERT  是什幺。在此还是花点篇幅对 ALBERT 进行个简单的科普。这里假设读者已经对 BERT 有了一定的了解,所以主要是比较 ALBERT 跟 BERT 的异同。

 

低秩分解

 

首先是 Embedding 层,以中文版 bert base 为例,token 总数大约是 20000,而 Embedding 层维度是 768,所以 Embedding 层的总参数量大约是 1500 万,大概占了总参数量的 1/6。

 

ALBERT 第一个开刀的部分就是 Embedding 层,它把 Embedding 层弄成 128 维了,然后再通过 128 × 768 的矩阵变换矩阵变回 768 维,这样 Embedding 层参数量就只有原来的 1/6 了,这就是所谓的低秩分解。

 

参数共享

 

其次是 transformer 部分。在以 BERT 为代表的 transformer 架构模型中,其核心是由 self attention、layer norm、全连接(核大小为 1 的一维卷积)等组成的模块,这里称之为“transformer block”,而 BERT 模型就是多个 transformer block 的堆叠。

 

如下面左图实际上是 bert base 的示意图,它堆叠了 12 个 transformer block:

 

 

▲ 图1. BERT base & AL BERT base 极简示意图

 

注意到,在 BERT 的设计里边,每个 transformer block 的输入和输出形状是一样的,这意味着当前 block 的输出作为当前 block 的输入也是合理的。这也告诉我们,同一个 block 其实是可以重复使用来迭代的,而不至于每一层都使用一个新的 block。

 

ALBERT 里边使用了一种最简单直接的方案:所有层都公用同一个 transformer block(如上面右图所示)!这样一来,在 ALBERT base 中,transformer block 这部分参数量直接降低为原来 BERT base 的 1/12 了。

 

简单评述

 

除了以上两点之外, ALBERT 与 BERT 的一个显着不同之处是在预训练阶段将 NSP (Next Sentence Prediction) 任务改为了 SOP (Sentence-Order Prediction) 任务,但是这并不属于模型架构上的,所以并不是本文要关心的,读者自行找相关资料了解即可。

 

总的来说, ALBERT 是一个为了降低参数量而设计的模型,并且希望这个参数量的降低能带来一定的正则化作用,从而降低过拟合风险,提升最终表现。但最后的结果是否如作者所愿呢?从“战绩”来看, ALBERT 问世之时用它最大版本的模型刷新了 GLUE 榜单,所以应该算是达到了作者的期望。但是, ALBERT 并非总是那幺理想, ALBERT 也不是我们想象中的小模型。

 

对于一个模型来说,我们比较关心的是速度和效果两个指标。从上面两个 BERT 和 ALBERT 的图其实就可以看到,在预测阶段(前向传播),其实 ALBERT 跟 BERT 没啥差别,所以同一规格(比如大家都是 base 版本)下的 ALBERT 和 BERT  的预测速度是一样的,甚至更严格地说, ALBERT 还更慢些,因为 ALBERT 的 Embedding 部分还多了一个矩阵运算。换句话说, ALBERT 不能带来预测速度的提升!

 

那同一规格的 ALBERT 和 BERT,哪个效果好呢?其实 ALBERT  论文已经给出了答案:在 large 以内的版本, ALBERT 效果比 BERT 更差,只有在 xlarge、xxlarge 的版本时 ALBERT 效果才开始优于  BERT 。

 

但是 roberta 方式的预训练部分弥补了 BERT 的缺点,所以真正可以说稳定优于 bert/roberta 的 ALBERT 版本就只有 xxlarge。然而  ALBERT xxlarge 是一个很庞大的模型,以至于我们很难把它跑起来。

 

所以,基本上可以说:(在大多数人能搞起的前提下) 在同样的预测速度下, ALBERT 效果更差; 在同样的效果下, ALBERT 更慢。

 

那训练阶段呢?前面还没有提到的一点是, ALBERT 的参数共享设计其实有很强的正则化作用,所以 ALBERT 去掉的 dropout。参数共享和去掉 dropout 这两点确实可以省一些显存,并且提高训练速度,但是笔者的评测是幅度只有 10%~20% 左右。也就是说,就算 ALBERT 参数降低到 BERT 的 1/10 甚至更多,并不意味着它的显存占用量能降低到 1/10,也不意味着训练速度能提高 10 倍,相反,它只有小幅度的提升。

 

 

抛开共享约束

 

从前面的讨论中,我们能理解到几个事实:

 

1. 只看预测的话,ALBERT 跟 BERT 基本一致;

 

2.  ALBERT 的参数共享对效果的作用基本是负面的。

 

既然这样,那幺我们可以尝试一个新鲜的玩法:在针对下游任务进行 finetune 时,我们把参数共享这个约束去掉如何?也就是说,finetune 的时候把 ALBERT 当 BERT 用,相当于每一个 transformer block 的初始化权重都一样的 BERT 。

 

效果评测

 

事不宜迟,测了效果再说话。这里挑了四个任务来测。为了保证可复现性,下面同一个实验都跑了三次,表格里显示的是三次结果的平均值。其中 unshared 版本就是指去掉参数共享后的模型。而训练速度那一列,指的是每个 epoch 所用的训练时间,这是在单卡 TITAN RTX 上跑的时间,仅供相对比较参考。

 

实验用 bert4keras [1] 进行,对 unshared 版本,只需要在 build_bert_model 时加载 ALBERT 权重,并且设置 model=’albert_unshared’ ,这就是标题所说的 “一行代码” 。

 

首先是比较简单的文本情感分类  [2]  任务。

 

 

去掉参数共享后,训练时间略有增加,这是预料之中的,至于模型表现各有优劣。考虑到这个任务准确率本身比较高了,可能显示不出模型间的差距,所以下面继续测试复杂一点的模型。

 

这次我们试试 CLUE 的 IFLYTEK’长文本分类 [3] ,结果如下:

 

 

这时候 unshared 版本的优势开始显示出来了,主要体现在整体上收敛更快(看第一个 epoch 的指标值),small 版本最优效果明显更优,tiny 版本最优效果略差,但通过精细调整学习率后,tiny_unshared 版本的最优效果其实是可以优于 tiny 版的(但这样一来变量太多了,表格显示的是严格的控制变量的结果)。

 

然后试试比较综合的任务:信息抽取 [4] 。结果如下:

 

 

可以看到,在比较综合性的复杂任务,unshared 版本的模型已经稳定超过同规模的原模型。

 

最后一个是用 seq2seq 做阅读理解式问答   [5] ,结果如下:

 

 

这个任务主要目的是测试模型的文本生成能力。可以看到在此任务上,unshared 版本的模型已经明显超过原版模型,甚至 tiny 版的 unshared 模型已经逼近原版 small 模型。

 

分析思考

 

上面的模型都是  ALBERT tiny/small 的实验,其实 base 版也实验过,结论跟 tiny 和 small 版的基本一致,但是 base 版本(自然也包括 large 以及 xlarge 版本)实验时间过长,所以没有做完完整的实验(也没有重复三次),因此就不贴上了。但总的来说,可以感觉到 tiny/small 版本的结果基本上有代表性了。

 

上述实验标明,去掉参数共享后的 ALBERT ,在下游任务中的表现基本能持平甚至超过原版 ALBERT ,这显示了对于很多 NLP 任务来说,参数共享可能并不是一个很好的约束。读者可能纠结于 “为什幺到了 xlarge 甚至 xxlarge 规模的模型时,参数共享的 ALBERT 又开始超过了不参数共享的 BERT 了呢? ” 。这里笔者尝试给出一个解释。

 

从理论上来说,BERT 防止过拟合的手段有 dropout 和权重衰减,其中权重衰减在 ALBERT 也用了,但 dropout 没有出现在 ALBERT 中,所以可以往 dropout 角度思考。很多实验都表明,dropout 确实是一种降低过拟合风险的有效策略,但已有的实验模型基本都远远比不上 BERT xlarge、BERT xxlarge那幺大,所以 dropout 在超大模型下的有效性依然值得商榷。

 

事实上,dropout 存在训练和推断的不一致问题,也就是 “严格来讲训练模型和预测模型并不是同一个模型” ,个人感觉模型变大变深时,这种不一致性会进一步放大,所以个人认为 dropout 对于超大模型并不是一种有效的防止过拟合的方法。而 ALBERT 去掉了 dropout,通过参数共享来引入隐式的正则,使得模型变大变深不至于退化,甚至会表现更好。

 

反过来说, ALBERT 的参数共享性能要好,条件是要足够大、足够深,所以如果我们用的是 base 版本、small 版本甚至是 tiny 版本时,反而不应该用参数共享,因为对于小模型来说参数共享反而是对模型表达能力的不必要的限制,所以这时候去掉参数共享表现反而更好些。

 

 

文章小结

 

本文实验了一个新鲜的玩法:finetune 阶段把 ALBERT 的参数共享去掉,把 ALBERT 当 BERT 用,在几个任务上发现这样做有着持平甚至超过原始  ALBERT 的表现,最后给出了对 ALBERT 以及此现象的个人理解。

 

 

相关链接

 

[1] https://github.com/bojone/bert4keras

 

[2] https://github.com/bojone/bert4keras/blob/master/examples/task_sentiment_albert.py

 

[3] https://github.com/CLUEbenchmark/CLUE

 

[4]  https://github.com/bojone/bert4keras/blob/master/examples/task_relation_extraction.py

 

[5]  https://github.com/bojone/bert4keras/blob/master/examples/task_reading_comprehension_by_seq2seq.py

 

 

Be First to Comment

发表评论

电子邮件地址不会被公开。 必填项已用*标注