引言
在NLP任务中(后续拓展为多模态任务),顺序信息至关重要,例如: 我借给你300块与你借给我300块具有完全不同的含义。
对于Transformer模型来说,由于Attention模块的无序性(无法区分不同位置的Token),必须加入额外的信息来记录顺序,这里引入了位置编码。位置编码在后续基于Transformer架构的文章中有很多不同的实现方式,尤其是在大语言模型大行其道的现在,在面对长token的输入时,挑选合适的位置编码也会提升训练的效果。本文整理主流模型的位置编码实现方式,并用torch实现以加深理解。
位置编码从实现方式上大致可以分为2类:
- 绝对位置编码: 将位置信息融入到输入中
- 相对位置编码: 微调Attention结构,使其可以分辨不同位置的Token
绝对位置编码
在输入的第k个向量中加入位置向量变为,其中只依赖于位置编号k。实现方式类似下图:
正弦曲线(sinusoidal)位置编码
这种位置编码是Transformer原始论文中实现的位置编码,实现公式如下:
其中,PE表示位置编码矩阵,pos表示token的位置,2i表示embedding中的偶数位置,2i+1表示奇数位置(这个最好结合下面的代码进行理解,在下面的代码实现中,exp_value实现了指数部分,变量out实现了三角函数内的值 ),d-model表示每个token向量化后的维度。
原论文中还提及作者实验了可学习的位置编码,并且这两种位置编码具有接近的结果,之所以选择正弦曲线位置编码是因为它可以允许模型序列外推到比训练期间遇到的序列更长的位置。(it may allow the model to extrapolate to sequence lengths longer than the ones encountered during training)
1 | class SinPositionEncoding(nn.Module): |
正弦位置编码不需要进行学习,是初始化时直接根据如上公式赋值的常量, 因此有一定的外推性。
又由于位置的向量可以表示成位置α和位置β的向量组合,表明正弦编码可以表达相对位置信息。
可学习位置编码
这种位置编码是Bert、GPT、ViT等架构的实现方式,直接将位置编码当作可训练参数,让它随着训练过程更新。实现方式简单,交给模型进行自学习,大力出奇迹。
1 | class TrainablePositionEncoding(nn.Module): |
相对位置编码
相对位置并没有完整建模每个输入的位置信息,而是根据Attention中K,V矩阵的偏移量产生不同的Embedding,计算Attention时考虑当前位置与被Attention位置的相对距离。相对位置编码几乎都是在Softmax之前的Attention矩阵上进行操作的
经典相对位置编码
相对位置编码起源于Google的论文《Self-Attention with Relative Position Representations》,华为开源的NEZHA模型也用到了这种位置编码。
1 | class RelativePosition(nn.Module): |
旋转位置编码
在Llama及Llama2,QWen等模型中,使用了这种位置编码的方式。在论文RoFormer: Enhanced Transformer with Rotary Position Embedding中有详细的解释。
Rope是将绝对位置编码与相对位置编码进行结合,通过绝对位置编码的方式实现相对位置编码。
这部分内容可以参考文末的参考文章,写的非常详细。
Rope有不同的实现方式,这里是Llama源码中的实现:
1 | # 生成旋转矩阵 |