位置编码RoPE介绍及其优化

绝对位置编码

传统的BERT中的实现方法,假设模型最大输出长度是512,向量纬度是768,需要先初始化一个512*768的位置编码矩阵。在每个位置,将位置编码直接加到token向量上。

  • 问题:
  1. 不具备长度外推性。

正弦曲线(Sinusoidal)位置编码PE

是谷歌在Transformer中提出的绝对位置编码, 计算公式如下:

P E ( k , 2 i ) = s i n ( k 1000 0 2 i d ) P E ( k , 2 i + 1 ) = c o s ( k 1000 0 2 i d ) PE(k,2i) = sin(\frac{k}{10000^{\frac{2i}{d}}}) \\ PE(k,2i+1) = cos(\frac{k}{10000^{\frac{2i}{d}}}) PE(k,2i)=sin(10000d2ik)PE(k,2i+1)=cos(10000d2ik)

其中,d表示向量纬度,k表示第k个位置,2i和2i+1表示位置向量的分量索引,如 P E ( k , 2 i ) PE(k,2i) PE(k,2i)表示位置k向量第2i个分量。

正弦曲线位置编码每个分量都是正弦或者余弦函数,具有周期性 。如下图,每个分量都有周期性,而且越靠后的分量,波长越大,频率越低。

正弦曲线位置编码还具有远程衰减的性质:对两个相同的词向量,如果他们之间的距离越近,相对位置就越小,则他们的内积分数就越高,反之越小。

  • 代码实现:

    ini 复制代码
      class SinPositionEncoding(nn.Module):
          def __init__(self, max_sequence_length, d_model, base=10000):
              super().__init__()
              self.max_sequence_length = max_sequence_length
              self.d_model = d_model
              self.base = base
    
          def forward(self):
              pe = torch.zeros(self.max_sequence_length, self.d_model, dtype=torch.float)  # size(max_sequence_length, d_model)
              exp_1 = torch.arange(self.d_model // 2, dtype=torch.float)  # 初始化一半维度,sin位置编码的维度被分为了两部分
              exp_value = exp_1 / (self.d_model / 2)
    
              alpha = 1 / (self.base ** exp_value)  # size(dmodel/2)
              out = torch.arange(self.max_sequence_length, dtype=torch.float)[:, None] @ alpha[None, :]  # size(max_sequence_length, d_model/2)
              print(out)
              embedding_sin = torch.sin(out)
              embedding_cos = torch.cos(out)
              print(embedding_sin)
    
              pe[:, 0::2] = embedding_sin  # 奇数位置设置为sin
              pe[:, 1::2] = embedding_cos  # 偶数位置设置为cos
              return pe

旋转位置编码RoPE

  • 实现原理

    • 前面的PE是绝对位置编码,模型只能感知每个词所处的绝对位置,而无法感知两个词语之间的相对位置。
    • RoPE的出发点:通过绝对位置编码的方式实现相对位置编码。
    • 我们需要定义一个位置编码函数,可以对词向量q添加绝对位置信息m,得到 q m = f ( q , m ) q_m = f(q,m) qm=f(q,m)
    • RoPE希望 q m q_m qm和 k n k_n kn之间的点积能够带有相对位置信息(m-n)。
    • 建模目标:找到一个函数 f ( q , m ) f(q,m) f(q,m),使得如下关系成立: f ( q , m ) ⋅ f ( k , n ) = g ( q , k , m − n ) f(q,m)·f(k,n)=g(q,k,m-n) f(q,m)⋅f(k,n)=g(q,k,m−n)
  • 二维情况

实现方法:采用旋转矩阵 ,当一个二维向量左乘一个旋转矩阵时,该向量即可实现弧度为 θ \theta θ的逆时针旋转。

通过推理也可以得到(m-n)的信号量。

一个例子:

  • 多维情况:

分而治之的思路:我们把高维向量,两两一组,分别旋转。

  • 实现远程衰减性:

当我们把 θ \theta θ设为1,随机初始化两个向量q和k,将q固定在0位置,k的位置从0开始逐渐增大,依次计算q和k之间的点积。下图中反映了点积的震荡图,最大的问题是:缺乏了远程衰减性!

因此借鉴Sinusoidal位置编码,将每个分组的 θ \theta θ设置为不同的常亮,从而引入远程衰减的性质:

在原始的RoPE中,沿用了Sinusoidal的设置, θ \theta θ赋值为 1000 0 − 2 i / d 10000^{-2i/d} 10000−2i/d,10000为base,实际上base的值对注意力远程衰减程度有影响:

因此对base数值的研究,与大模型的外推能力有关系,如NTK感知插值、动态NTK感知插值等长度外推方法,本质上都是通过改变base值,影响每个位置对应的旋转角度,进而影响模型的位置编码信息。

  • 周期性:

每组向量的旋转具有周期性,旋转一周的弧度是 2 π 2\pi 2π,也就是转一圈又会回到原点,所有RoPE中的向量旋转就像时钟一样,具有周期性,如下图1。但是我们发现,越靠后的分组,向量的旋转速度越慢,如下图当位置位于500时,第0组分量已经旋转500弧度了,第8组分量才旋转158弧度。

下面图2展示了上述旋转弧度对应的正弦函数,我们可以直观看到:越靠后的分组,它的旋转速度越慢,正弦函数的周期越大,频率越低。

参考:

  1. 图解RoPE旋转位置编码及其特性

RoPE的优化-位置插值法

做法很直接:缩小每个位置的旋转弧度,让向量旋转的慢一点,每个位置的旋转弧度变为原来的 L L ′ \frac{L}{L'} L′L ,长度扩大几倍,旋转弧度就缩小几倍。

  • 问题点:

如图一,如果训练阶段sentence长度是0,2048,那么模型只见过0,2048的旋转弧度,没见过2048,4096的旋转弧度,模型难以理解新的旋转弧度,无法正确引入位置信息,导致模型性能下降。

  • 优化效果

    • 图二,展示了第0组分量旋转弧度的变化,位置插值法将0位置的旋转弧度变成原来的一半, 就可以看到2048,4096的旋转弧度了。
  • 频率变化:

图3,经过位置插值之后,向量旋转变慢了,周期变大,频率变慢。

RoPE优化-NTK感知插值

前面介绍过:位置越靠后的分量旋转速度越慢,频率越低,周期越长 ,如下图所示:对于第0组分量,在位置7的时候就已经旋转一周了,而对于第64组分量,在位置2047时,其旋转弧度约为0.2,仍然没有旋转1/4周。我们希望这些高频信息。

NTK感知插值做法:对base加一个缩放因子,进行放大 。保留高频信息,高频分量旋转速度降幅低,低频分量旋转速度降幅高;在高频部分进行外推,低频部分进行内插。

因此,我们将NTK感知插值有效的原因:

  1. 靠前的分组,在训练环节见过非常多的完整的旋转周期,位置信息得到充分训练,具有较强的外推能力。

  2. 靠后的分组,在训练环节无法见到完整的旋转周期,或者见到的非常少,训练不充分,外推性能弱。需要进行位置插值。

RoPE优化-NTK-by-parts Interpolation

做法:不改变高频部分,仅仅缩小低频部分的旋转弧度。

RoPE优化-动态NTK感知插值

做法:推理长度小于等于训练长度时,不进行插值,推理长度大于训练长度时,采用NTK动态插值法动态放大base。

RoPE优化-YaRN

问题:通过NTK办法,本质上都是减小旋转弧度,降低旋转速度 ,来达到长度外推的能力。这将导致两个位置之间的旋转弧度差距变小,词向量之间的距离比原来更大,点乘更大,这破坏了模型原始注意力分布。

参考:

  1. 详解基于调整RoPE旋转角度的大模型长度外推方法
相关推荐
机器人零零壹1 小时前
技术白皮书:iRobotCAM如何解决机器人激光切割应用中的核心难点,从CAD离线编程到双目视觉定位与轨迹自适应提升复杂空间曲线切割的精度、效率与柔性化水平
人工智能·算法
电子元件小说家2 小时前
2026年四季度电感市场观察:AI算力与汽车电子驱动下的选型与验证建议智能硬件
人工智能·汽车
网易CodeWave-小码哥2 小时前
支持多模块 Java 微服务导入|CodeWave SDD V4.5.10 上线
人工智能
AI你一生一世3 小时前
不止于时序存储:DolphinDB 如何成为 AI 时代的工业智能底座
java·人工智能·struts·时序数据库·工业智能·dolphindb·ai基础设施
belldeep3 小时前
python:genesis-world, Genesis-Embodied-AI
人工智能·python·ai·genesis
怕浪猫3 小时前
小程序成长计划上新:10 亿 Token 现在能调 DeepSeek-v4.1-flash了
人工智能·aigc·ai编程
ss2733 小时前
为什么PostgreSQL适合AI场景?
数据库·人工智能·postgresql
七牛云行业应用4 小时前
DeepSeek Super-agent 与 Grok Bot:开源 Agent 架构和聊天助手有何区别
人工智能·ai·架构·ai编程
ZhengEnCi4 小时前
大模型的分类到底怎么分:从基础模型到推理模型、从通用到专用的完整图谱
算法
维克兜率天4 小时前
【维克】趋势型CTA:期货市场的“追涨杀跌“
人工智能·python