详解大模型的位置编码-positional encoding
一、TL;DR
- 为什么要有位置编码:需要知道绝对位置信息、相对位置信息和不同位置之间的距离
- 位置编码需要有什么特点:用来表示绝对位置、相对位置和距离、表示从没看过的句子长度
- 有什么类型的位置编码:绝对位置编码、相对位置编码ALiBi和旋转位置编码RoPE
- 会遇到什么问题?长度外推问题
- 怎么解决?直接外推、线性内插、进制转换、位置线性内插和NTK-Aware scaled RoPE
二、为什么要有位置编码和作用
2.1 自注意力机制的无序性
Transformer模型在处理序列数据时,自注意力机制通过计算输入序列中每个元素与其他元素之间的关系来提取信息。然而,它本质上是对输入序列中元素的位置不敏感的。也就是说自注意力机制只关注元素之间的关系,而忽略了元素在序列中的位置顺序。
-
例如,对于一个句子“猫在吃鱼”,自注意力机制会计算“猫”与“在”、“吃”、“鱼”之间的关系,但不会区分“猫在吃鱼”和“鱼在吃猫”(假设输入是简单的词嵌入)。这种对位置的不敏感性会导致模型无法理解序列的顺序信息。
2.2 需要额外的序列顺序信息
由于自注意力机制无法区分序列中元素的位置,Transformer模型一种需要方式来引入序列的顺序信息。位置编码的作用就是为模型提供每个元素在序列中的位置信息,使得模型能够区分“猫在吃鱼”和“鱼在吃猫”这样的不同语义。
2.3 位置编码的作用
为每个位置赋予唯一标识
位置编码是一个与词嵌入维度相同的向量,它为序列中的每个位置赋予一个唯一的标识。这些标识向量被加到词嵌入向量上,使得模型能够区分不同位置的词。
- Input = Word Embedding + Position Embedding <绝对位置编码>
-
假设词嵌入维度是 512,位置编码也是一个 512 维的向量。对于句子“猫在吃鱼”,“猫”在位置 1,“在”在位置 2,“吃”在位置 3,“鱼”在位置 4。每个位置都有一个对应的位置编码向量,这些向量被加到对应的词嵌入向量上,从而使得模型能够感知到每个词的位置。
帮助模型理解序列的结构
-
位置编码不仅能够区分不同位置的词,还能帮助模型理解序列的结构。例如,在语言模型中,模型需要知道句子的开头、中间和结尾,位置编码可以帮助模型捕捉到这种结构信息。
-
位置编码的设计通常具有一定的数学性质,例如,某些位置编码的设计可以使得模型能够捕捉到位置之间的相对距离。这对于理解句子的语法结构和语义关系非常重要。
三、位置编码的类型
3.1 绝对位置编码-Sinusoidal位置编码
paper来源:https://dl.acm.org/doi/pdf/10.5555/3295222.3295349
Attention is all your need
特点:
- 能用来表示一个token在序列中的绝对位置(正余弦编码)
- 在序列长度不同的情况下,不同序列中token的相对位置/距离也要保持一致(位置编码差值可以由id的位置插值计算得到)
- 可以用来表示在训练过程中从没有看到句子的长度(长度外推性)
计算公式:
说人话(解释为什么绝对位置编码具备上面的3个优点):
计算公式:
- PEt = [sin(W1*t), cos(W1*t), sin(W2*t), cos(W2*t),... ...,sin(Wd_model/2*t), cos(Wd_model/2*t)];
- 其中wi = 1/(1000^i/(dmodel-1)),pos表示位置,dmodel代表embedding的维度,i代表的是embedding不同位置的索引。
绝对编码的优势:
- 对于相邻位置,位置编码的差异较小,与两者之间的距离成正比。
- 对于相隔较远的位置,位置编码的差异较大,与两者之间的距离成正比。
- 由于正弦和余弦函数的性质,位置编码的差值 PE(i)−PE(j) 将与 i 和 j 之间的差值有关。这意味着通过比较不同位置编码之间的差值,模型可以推断出它们之间的相对位置。
这篇文章写的很好,大家可以参考一下这个:再论大模型位置编码及其外推性(万字长文)
小节结论:
- 具有相对位置表达能力:Sinusoidal可以学习到相对位置,对于固定位置距离的k,PE(i+k)可以表示成PE(i)的线性函数。
- 为什么两个位置向量的内积只和相对位置 k 有关。
缺点:
由于位置编码的点集无向性,当随着input embedding被喂入时会出现距离意识被破坏的现象,即正弦位置编码的相对位置表达能力被投影矩阵破坏掉了,解决方案是采用可学习的位置编码。
- 什么叫距离意识被破坏掉:位置编码可能无法准确地反映序列中元素之间的相对距离关系,从而影响模型对序列结构的理解
- 在某些情况下,正余弦函数具有周期性,周期性可能导致位置编码之间的差异无法准确反映相对距离。例如,当两个位置的距离非常大时,它们的位置编码向量之间的差异可能会变得不明显。
目标检测的应用:
DINO/DETR等transformer的检测器:
改进点:
可学习位置编码:直接将位置编码作为一个可学习的参数,直接随机初始化以后就去学习得到最终的权重
3.2 旋转位置编码RoPE
3.2.1 主要信息总结
paper来源:https://arxiv.org/pdf/2104.09864
ROFORMER: ENHANCED TRANSFORMER WITH ROTARY POSITION EMBEDDING
RoPE的核心思想:
- 二维向量 如果将其绕原点旋转一定的角度, 改变的仅仅是两个向量之间的夹角, 此时, 将旋转后的向量点乘, 其结果一定包含旋转弧度的相对信息
- 相对位置信息只要转化为弧度就可以关联起来
一句话描述:
旋转位置编码不是作用在embedding的输入层,而是作用在与Attention的计算中,对attention的q、k向量通过用复数的形式表示注入了 绝对位置信息,然后用更新的q、k向量做attention的内积引入相对位置信息,内积的值和相对位置关系呈固定的某种函数关系
参考文档:
- 十分钟读懂旋转编码(RoPE)
3.2.2 数学表达-相对位置关系
3.2.3 数学表达-具体怎么引入
首先通过引入复数的变化,复数的实部不影响值,虚部代表旋转的角度(下图中的imθ):
接下来将复数变化转换为旋转矩阵形式(才能够推导):
二维向量绕原点旋转m角度的数学表达:
则Fq就可以理解为相较于绕原点旋转mθ的矩阵变化,进一步求Fq*Fk的关系,则可以求出点集和相对位置之间的关系:
最后拓展到多维(d_model):
3.2.4 paper中的可视化图解
为什么LLM使用相对位置编码?
相对位置编码更符合语言的结构特性(依赖相对关系),且在长序列处理、计算效率和模型性能上均优于绝对位置编码。这使其成为现代 LLM 的首选方案。
为什么选择10000作为编码的频率的底数:
其中,选择底数 10000 主要有以下原因:
-
频率范围的合理分布 位置编码的目的是为模型提供序列中位置的相对信息。通过使用 10000 作为底数,不同维度的正弦 / 余弦函数会以不同的频率振荡,从而覆盖从非常低(长周期)到非常高(短周期)的各种频率范围。这使得模型能够捕捉不同长度的序列依赖关系。
-
数值稳定性 使用 10000 这样的中间值可以避免指数运算产生过大或过小的数值,确保在计算过程中不会出现数值溢出或下溢的问题。
-
经验选择 论文作者通过实验发现,这个值在实际应用中效果良好,能够平衡不同位置之间的表示能力,使得模型在不同长度的序列上都能表现出较好的泛化能力。
-
数学特性 这种设计使得位置编码具有一定的数学特性,例如对于任意固定的偏移量 k,\(PE_{pos+k}\) 可以表示为 \(PE_{pos}\) 的线性函数,这有助于模型学习相对位置关系。
3.3 相对位置编码
略
四、长度外推问题
4.1 什么是长度外推问题
模型训练时的输入token长度和推理时的输入token长度不一致,导致性能会下降
4.2 如何解决
4.2.1 进制表示/直接外推
问题:
- 从下图可以看出来,如果直接使用3维10进制训练模型,但输入变成了2000以上(4维),
解决方案:
- 在训练阶段留一/多个维度值为0,如果输入超过了原来的设定,则推理时直接将维度值修改为对应的输入,但是这样的效果就会比较差(性能下降严重)
4.2.2 线性内插
解决方案:
- 外推改为内插(Interpolation),简单来说就是将2000以内压缩到1000以内,比如通过除以2,1749就变成了874.5,然后转为三维向量[8,7,4.5]输入到原来的模型中。
- 从绝对数值来看,新的[7,4,9]实际上对应的是1498,是原本对应的2倍,映射方式不一致;
- 从相对数值来看,原本相邻数字的差距为1,现在是0.5,最后一个维度更加“拥挤”。所以,做了内插修改后,通常都需要微调训练,以便模型重新适应拥挤的映射关系。
4.2.3 进制转换
三个数字的10进制编码可以表示0~999,16进制最大可以表示4095。只需要转到16进制,如1749变为[6,13,5],那么三维向量就可以覆盖目标范围,代价是每个维度的数字从0~9变为0~16。
问题点:每个维度0-9的表示是正常的,但超过9之后(10~15)可能会有问题,但事实上一般模型也有一定的泛化能力,所以每个维度稍微往外推一些是没问题的。甚至可以换用更小的13进制来表示
进制而不是16进制。
4.2.4 位置线性内插(Position Interpolation,PI)
暂时不写了,喘口气
4.2.5 NTK-Aware Scaled RoPE
暂时不写了,喘口气