旋转位置编码RoPE

wen IT资讯 20

旋转位置编码(Rotary Position Embedding, RoPE)

核心思想

RoPE 的核心思想是通过旋转矩阵对词向量进行变换,从而将位置信息编码到词向量中,与传统的位置编码方式不同,RoPE 不是简单地给词向量加上位置向量,而是通过旋转操作来编码位置信息。

旋转位置编码RoPE

数学原理

1 基本公式

对于位置 $m$ 处的词向量 $\mathbf{x} = [x_1, x_2, ..., x_d]$,RoPE 将其变换为:

$$f(\mathbf{x}, m) = \mathbf{R}_m \cdot \mathbf{x}$$

$\mathbf{R}_m$ 是旋转矩阵:

$$\mathbf{R}_m = \begin{pmatrix} \cos m\theta_1 & -\sin m\theta_1 & 0 & 0 & \cdots & 0 \ \sin m\theta_1 & \cos m\theta_1 & 0 & 0 & \cdots & 0 \ 0 & 0 & \cos m\theta_2 & -\sin m\theta_2 & \cdots & 0 \ 0 & 0 & \sin m\theta_2 & \cos m\theta2 & \cdots & 0 \ \vdots & \vdots & \vdots & \vdots & \ddots & \vdots \ 0 & 0 & 0 & 0 & \cdots & \cos m\theta{d/2} \end{pmatrix}$$

2 分块处理

通常将词向量分为 $d/2$ 个二维子空间,每个子空间应用一个旋转矩阵:

$$\begin{pmatrix} x{2k} \ x{2k+1} \end{pmatrix}' = \begin{pmatrix} \cos m\theta_k & -\sin m\theta_k \ \sin m\theta_k & \cos m\thetak \end{pmatrix} \begin{pmatrix} x{2k} \ x_{2k+1} \end{pmatrix}$$

$\theta_k = 10000^{-2k/d}$ (与Transformer中的正弦波编码类似)

关键特性

1 相对位置编码

RoPE 最显著的特性是能够自然地表示相对位置关系

$$f(\mathbf{x}, m)^T f(\mathbf{y}, n) = f(\mathbf{x}, 0)^T f(\mathbf{y}, n-m)$$

这意味着两个位置 $m$ 和 $n$ 的词向量点积只依赖于它们的相对位置差 $(n-m)$。

2 旋转不变性

$$f(\mathbf{Rx}, m) = \mathbf{R}_m \cdot \mathbf{R} \cdot \mathbf{x} = \mathbf{R} \cdot \mathbf{R}_m \cdot \mathbf{x} = \mathbf{R} \cdot f(\mathbf{x}, m)$$

旋转操作与位置编码可以交换顺序。

实现方式

1 前向传播中应用

def apply_rotary_pos_emb(x, cos, sin):
    # x: [batch_size, seq_len, num_heads, head_dim]
    # 将x分成两半并交错旋转
    x_rotated = x * cos + rotate_half(x) * sin
    return x_rotated
def rotate_half(x):
    x1 = x[..., :x.shape[-1]//2]
    x2 = x[..., x.shape[-1]//2:]
    return torch.cat([-x2, x1], dim=-1)

2 预计算cos和sin

def precompute_freqs_cis(dim, max_seq_len, theta=10000.0):
    freqs = 1.0 / (theta ** (torch.arange(0, dim, 2)[: (dim // 2)].float() / dim))
    t = torch.arange(max_seq_len, device=freqs.device)
    freqs = torch.outer(t, freqs)  # [max_seq_len, dim//2]
    freqs_cos = torch.cos(freqs)
    freqs_sin = torch.sin(freqs)
    return freqs_cos, freqs_sin

在Transformer中的应用

1 自注意力机制中的使用

def attention_with_rope(query, key, value, cos, sin):
    # 在Q和K上应用RoPE
    query = apply_rotary_pos_emb(query, cos, sin)
    key = apply_rotary_pos_emb(key, cos, sin)
    # 计算注意力分数
    attn_weights = torch.matmul(query, key.transpose(-2, -1)) / math.sqrt(d_k)
    attn_weights = F.softmax(attn_weights, dim=-1)
    # 注意力输出
    output = torch.matmul(attn_weights, value)
    return output

2 位置编码的计算时机

  • 在输入层不需要添加位置编码
  • 仅在每一层的自注意力计算中应用RoPE
  • 不同层可以共享相同的旋转矩阵预计算结果

优势与局限性

1 优势

  1. 相对位置编码:自然地捕获相对位置关系
  2. 外推能力:比绝对位置编码更好的长度外推性能
  3. 计算效率:只需额外的几个逐元素操作,计算开销小
  4. 兼容性:可以方便地集成到现有的Transformer架构中

2 局限性

  1. 非因果位置关系:没有明确区分过去和未来的位置
  2. 维度限制:需要在偶数维度上工作
  3. 长距离衰减:虽然理论上有,但实践中可能需要特殊处理

应用实例

1 LLama系列

class LlamaAttention(nn.Module):
    def __init__(self, config):
        super().__init__()
        self.config = config
        self.hidden_size = config.hidden_size
        self.num_heads = config.num_attention_heads
        self.head_dim = self.hidden_size // self.num_heads
        self.q_proj = nn.Linear(self.hidden_size, self.num_heads * self.head_dim, bias=False)
        self.k_proj = nn.Linear(self.hidden_size, self.num_heads * self.head_dim, bias=False)
        self.v_proj = nn.Linear(self.hidden_size, self.num_heads * self.head_dim, bias=False)
        self.o_proj = nn.Linear(self.num_heads * self.head_dim, self.hidden_size, bias=False)
    def forward(self, hidden_states, attention_mask, position_ids):
        # 计算Q、K、V
        query_states = self.q_proj(hidden_states)
        key_states = self.k_proj(hidden_states)
        value_states = self.v_proj(hidden_states)
        # 应用RoPE
        cos, sin = self.rotary_emb(query_states, position_ids)
        query_states = apply_rotary_pos_emb(query_states, cos, sin)
        key_states = apply_rotary_pos_emb(key_states, cos, sin)
        # 标准注意力计算
        attn_output = self._attention(query_states, key_states, value_states, attention_mask)
        return self.o_proj(attn_output)

与其他位置编码的比较

特性 RoPE 绝对位置编码 相对位置编码
外推能力
计算效率
内存占用
实现复杂度

参数选择建议

  1. 基础频率 $\theta$:通常使用10000.0
  2. 缩放因子:对于长序列,可以使用更大的缩放因子
  3. 维度分配:确保维度为2的倍数

RoPE 通过旋转矩阵优雅地解决了位置编码问题,在保持相对位置关系的同时,实现了高效的并行计算和良好的外推能力,已成为现代Transformer架构中的标准方案之一。

上一篇ALiBi偏置

下一篇相对位置编码

抱歉,评论功能暂时关闭!