语言模型是机器理解和处理人类语言的一种手段。2017年的Transformer模型标志着语言模型开始脱离RNN和LSTM的建模方法。BERT模型通过更大的模型和更多的数据,显著提升了自然语言处理(NLP)任务的基准。GPT则在自回归的道路上不断推进,XLNet将GPT和BERT的优点结合起来,通过更大的数据集超越了BERT。不久之后,RoBERTa通过更大的数据集进一步击败了XLNet。然而,当BERT模型达到一定规模后,硬件资源成为瓶颈。谷歌通过矩阵分解和参数共享技术,缩小了BERT的模型大小。此时,ALBERT采用了与BERT相同的参数量,但推理能力得到了提升。最近几个月,我对几种代表性NLP模型进行了研究,并将其理解记录下来。
在2017年以前,语言模型主要依赖RNN和LSTM。虽然这些模型可以捕捉上下文信息,但它们无法并行化,导致训练和推理效率低下。因此,论文提出了Transformer,这是一种完全基于注意力机制的模型,可以并行化处理数据,从而大幅提升训练和推理速度。Transformer成为了后续强大NLP预训练模型的基础。
Transformer的输入数据经过填充处理,大小为[批次大小, 最大序列长度]。初始嵌入矩阵通过查找表将输入映射为token嵌入,大小为[批次大小, 最大序列长度, 嵌入维度],然后乘以嵌入维度的平方根。接下来,通过正弦和余弦函数创建位置编码,并将其添加到token嵌入中,随后进行dropout处理。在多头注意力机制中,输入token嵌入通过全连接层生成Q、K、V,大小为[批次大小, 最大序列长度, 嵌入维度]。然后按照第2维分割成多个头,并按照第0维拼接,生成新的Q、K、V,大小为[头数*批次大小, 最大序列长度, 嵌入维度/头数]。
接下来,将K的第1维和第2维转置,然后与转置后的K进行点积,结果大小为[头数批次大小, 最大序列长度, 最大序列长度]。将结果除以隐藏维度的平方根,完成缩放操作。将填充的点积结果设为一个极小值(-2^32+1),完成掩码操作。接着对掩码后的结果进行softmax操作。将softmax的结果与V进行点积,得到注意力的结果,大小为[头数批次大小, 最大序列长度, 隐藏维度/头数]。将注意力的结果按照第0维分割成多个头,并按照第2维拼接,生成多头注意力的结果,大小为[批次大小, 最大序列长度, 隐藏维度]。
将token嵌入和多头注意力的结果相加,并进行层归一化。然后通过两层全连接层,其中第1层激活函数为ReLU,第2层激活函数为None。最后,将结果与嵌入矩阵转置后的点积生成大小为[批次大小, 最大序列长度, 词汇表大小]的结果。将结果进行softmax操作,生成当前时刻预测的下一个token在词汇表上的概率分布。
计算当前时刻预测的下一个token在词汇表上的概率分布与实际下一个token的one-hot编码的交叉熵,并将非填充token的交叉熵相加以作为损失函数,使用Adam优化器进行训练。
论文并未解释原因,但从代码来看,嵌入矩阵的初始化方式为Xavier初始化,其方差为1/嵌入维度。因此,乘以嵌入维度的平方根可以使嵌入矩阵的方差回归到1,有利于嵌入矩阵的收敛。
由于注意力机制是位置无关的,无论句子顺序如何,经过注意力机制计算的token嵌入都是相同的,这显然不符合人类的认知。因此,需要一种方法在模型中表示token的位置信息。Transformer通过固定的位置编码表示token在句子中的相对位置信息。
以数组为例,两个长度相同、均值为0、方差为1的数组点积会生成长度相同、均值为0、方差为长度的数组。方差过大会导致softmax的输入趋向于正无穷或负无穷,梯度趋近于零,不利于训练收敛。因此,除以长度的平方根可以使数组方差回归到1,有利于训练收敛。
类似于ResNet中的残差学习单元,有助于缓解网络退化问题。
多头注意力相当于将一个大空间划分为多个互斥的小空间,分别计算注意力,虽然每个小空间的注意力计算结果不如大空间准确,但多个小空间并行计算有助于捕捉更多信息,类似于CNN中的通道。
类似于CNN中的卷积块和全连接层交替衔接,FFN可以提高整个块的非线性变换能力。
自然语言生成任务是自回归的,在推理时只能根据之前的token生成当前时刻的token。为了保持训练和推理的一致性,训练时也不能使用后续的token生成当前时刻的token。这种方法也符合人类在自然语言生成中的思维模式。
Transformer最初发布时,我认为它更多的是一个噱头,在小模型上并没有太大优势。直到后来出现的BERT,证明了在大规模模型和大量数据的情况下,自我注意力机制在并行化和长距离建模方面优于传统的RNN和LSTM。Transformer已经成为许多代表性NLP预训练模型的基础,BERT系列使用了Transformer的编码器,GPT系列则使用了Transformer的解码器。在推荐系统领域,Transformer的多头注意力机制也被广泛应用。
在BERT之前,预训练嵌入的应用方式主要有两种:特征提取和微调。但这些方法都无法直接学习上下文信息。例如,ELMo通过分别学习上下文信息并将其拼接来表示上下文信息,而GPT只能学习上文信息。因此,作者提出了一种基于Transformer编码器的预训练模型,可以直接学习上下文信息,称为BERT。BERT使用了12个Transformer编码块,在13GB的数据上进行了预训练,是NLP领域大力出奇观的代表。
BERT基于Transformer编码器进行改进,因此在整体流程上与Transformer编码器相似,但在嵌入、多头注意力和损失计算方面有所不同。
BERT的嵌入由三部分组成:token嵌入、段嵌入和位置嵌入。而Transformer的嵌入由token嵌入和位置嵌入两部分组成。BERT在嵌入后先进行层归一化,再进行dropout处理。此外,BERT还在token序列前添加了一个特殊的token“[CLS]”,用于分类任务。如果是句子对任务,则在两个句子之间插入特殊token“[SEP]”。
BERT不仅会将注意力的结果拼接,还会将前N-1个编码块的注意力结果都拼接在一起。此外,BERT在Transformer之后增加了一个线性层。
BERT的预训练损失由两部分组成:NSP损失和MLM损失。NSP损失用于判断两个句子之间的关系,MLM损失用于预测被掩码的token。而Transformer的损失是在解码阶段计算的。
BERT的预训练任务之一是判断两个段落之间的关系,因此需要嵌入中包含当前token属于哪个段落的信息。而BERT通过创建一个段嵌入矩阵来表示当前token属于哪个段落的信息。
层归一化有助于嵌入矩阵的收敛,而dropout用于防止过拟合。
这是为了增加模型的信息融合能力,类似于集成学习的思想,可以获取更多的信息。
15%的概率是经过实验得出的最佳概率,既保证了足够的掩码样本进行学习,又不至于使段落信息损失过多。被掩码的token有80%的概率用[MASK]表示,10%的概率随机替换为其他token,10%的概率保留原token。这些百分比是多次实验得出的最佳组合,可以减少预训练和微调之间的不一致性。
相比于那些理论优越但在实际场景中表现不佳的论文,BERT真正地影响了学术界和工业界。无论是GLUE还是SQuAD,榜单上的高分方法大多是在BERT的基础上改进而来。在我的工作中,BERT的应用效果也超出了预期。BERT在NLP领域的地位类似于CV领域的Inception或ResNet,这些模型在几年前就已经超越了人类的标注准确率。然而,BERT也不是万能的,它更适合解决自然语言理解问题,而不适合处理自然语言生成问题。因此,如何将BERT改造成适用于处理机器翻译和文本摘要等生成任务的框架,是一个值得研究的方向。
目前,语言预训练模型主要有两种形式:自回归模型和自编码模型。自回归模型根据之前所有时刻的token预测下一个token,而自编码模型随机掩码掉句子中的某些token,然后根据上下文预测被掩码的token。自回归模型在训练过程中只能使用上文信息,但不会出现训练和推理的差距;自编码模型在训练过程中可以使用上下文信息,但会出现训练和推理的差距。因此,作者提出了一种基于Transformer-XL的模型,融合了自回归模型和自编码模型的优势。
因子分解序是一种句子token的随机排列方式。XLNet通过因子分解序将上下文信息引入自回归损失中。例如,句子1->2->3->4->5,在自回归损失中,预测token 2只能使用token 1的信息,而不能使用token 2/3/4/5的信息。而在因子分解序1->4->2->3->5中,预测token 2可以使用token 1/4的信息,但不能使用token 3/5的信息。在计算注意力结果时,XLNet通过对multi-head attention进行适当的掩码处理来实现这一目标。
XLNet使用了Transformer-XL的框架,并在其基础上引入了双流注意力机制。双流注意力机制分为查询流g和内容流h两个流。h类似于标准的Transformer中的multi-head attention,计算第t个时刻的注意力结果时使用因子分解序中前t个位置的位置信息和token信息。g在Transformer的multi-head attention基础上进行了修正,计算第t个时刻的注意力结果时只使用因子分解序中前t个位置的位置信息和前t-1个位置的token信息。在预训练过程中,XLNet只计算因子分解序最后1/6或1/7的token的g,并将其融合到自回归损失中进行训练,同时训练h。预训练完成后,放弃g,只使用h进行下游任务的微调。
因子分解序创新地将上下文信息融入自回归损失中。只要模型的预训练将句子的所有因子分解序都训练一遍,模型就能准确地捕捉句子中每个token与上下文之间的联系。然而,实际情况下,一个句子的因子分解序数量呈指数增长,因此在实际训练中只使用了句子的某个或某些因子分解序。
普通的Transformer无法将因子分解序和自回归损失结合起来。例如,两个不同的因子分解序1->3->2->4->5和1->3->2->5->4,第1个句子的4和第2个句子的5在自回归损失下的注意力结果是一样的,因此它们在词汇表上的预测概率分布也是一样的,这显然是不合常理的。为了达到目的,XLNet使用了双流的multi-head attention+feed-forward network,查询流g使用因子分解序中前t个位置的位置信息和前t-1个位置的token信息计算第t个位置的输入信息,而内容流h使用因子分解序中前t个位置的位置信息和token信息计算第t个位置的输入信息。在预训练过程中,使用g计算自回归损失,并最小化损失值,同时训练h。预训练完成后,放弃g,只使用h进行下游任务的微调。
尽管我没有实际使用过XLNet,但它在BERT之后的一篇论文中脱颖而出,因为它在Transformer框架下将上下文信息和自回归损失结合在一起。然而,XLNet是否真的比BERT优秀,这仍然是一个疑问。XLNet使用了126GB的数据进行预训练,比BERT的13GB数据大了一个数量级。在XLNet发布后不久,RoBERTa通过160GB的数据预训练再次击败了XLNet。
增大预训练模型的规模通常可以提高推理能力,但当模型增大到一定程度后,会受到GPU/TPU内存的限制。因此,作者在BERT的基础上引入了两项减少参数的技术,减少了BERT的大小,并修正了BERT的NSP损失。在与BERT相同参数量的前提下,ALBERT具有更强的推理能力。
在BERT及其改进版本中,嵌入维度等于隐藏维度,但这不一定是最优的。因为BERT的token嵌入是上下文相关的,而经过multi-head attention+feed-forward network后的隐藏嵌入也是上下文相关的,BERT预训练的目的是提供更准确的隐藏嵌入,而不是token嵌入,因此token嵌入没有必要与隐藏嵌入一样大。ALBERT将token嵌入进行了分解,首先降低嵌入维度的大小,然后通过一个全连接层将低维的token嵌入映射回隐藏维度的大小。这样可以减少模型的参数量,同时保持推理能力。