← 课程库
语言模型与架构模块三 · Transformer 结构组成公式代码18 分钟

位置编码与 RoPE 旋转位置编码

注意力本身「看不见顺序」,位置信息得额外注入

学完这节你能:
  • 解释为什么需要位置编码
  • 区分绝对/相对/旋转位置编码
  • 看懂 RoPE 的旋转公式与代码填空

注意力对输入是「置换等变」的——打乱词序,每个词算出的注意力不变。所以必须额外告诉模型每个 token 在第几位。这就是位置编码。

  • 绝对位置编码:给每个位置一个向量(正弦函数或可学习),加到词向量上。
  • 相对位置编码:直接建模两个 token 的相对距离,外推更好。
  • RoPE(旋转位置编码):把 Q、K 按位置「旋转」一个角度,用乘法把位置融进注意力,是当前主流(Llama、DeepSeek 等在用)。
对每一对维度 (even, odd),角度 θ=posfreq\theta = \text{pos}\cdot \text{freq}
xeven=xevencosθxoddsinθx'_{\text{even}} = x_{\text{even}}\cos\theta - x_{\text{odd}}\sin\theta
xodd=xevensinθ+xoddcosθx'_{\text{odd}} = x_{\text{even}}\sin\theta + x_{\text{odd}}\cos\theta
这就是二维旋转矩阵——RoPE 本质是把每对分量按位置旋转一个角度。
RoPE 核心(对应样题的代码填空:符号别写反)
freqs = 1.0 / (10000 ** (torch.arange(0, d, 2).float() / d))
angles = pos.unsqueeze(-1) * freqs        # (seq, d/2)
cos, sin = angles.cos(), angles.sin()
q_even, q_odd = q[..., 0::2], q[..., 1::2]
# 旋转:注意第一行是减号、第二行是加号
q_rot_even = q_even * cos - q_odd * sin
q_rot_odd  = q_even * sin + q_odd * cos
✗ 常见误解✓ 正确理解
RoPE 是加法操作(像绝对位置编码那样把位置向量加上去)。RoPE 是乘法/旋转操作;加法是绝对位置编码的做法。
旋转公式第一行用加号。第一行是减号(even·cos − odd·sin),第二行是加号(even·sin + odd·cos)。
把一对分量旋转 90°
取一对分量 (x_even, x_odd) = (1, 0),某位置角度 θ=90°:cos=0, sin=1
x'_even = 1×0 − 0×1 = 0
x'_odd = 1×1 + 0×0 = 1
→ (1,0) 旋转成 (0,1);不同位置转不同角度,位置信息就编进了 Q、K
自测RoPE 对 Query 的旋转变换,正确的是:
自测变式(换数字再算):把一对分量 (x_even, x_odd)=(0, 1) 旋转 90°(cos=0, sin=1),结果是:
// 带走一句话

注意力看不见顺序→需位置编码;RoPE 用「按位置旋转 Q/K」的乘法注入位置,公式=二维旋转(先减后加)。

独立练习

做到这些,才算完成本节

  1. 手算一个二维向量旋转 0、π/2、π 后的结果,并检查范数是否保持。
  2. 实现 RoPE 后验证相对位置相同的两组 token 得到一致的相对旋转关系。
  3. 不看正文重写公式,并逐项说明符号、形状和单位。
  4. 改变一个条件,判断公式结果应增大、减小还是不变。
  5. 在本地运行代码,打印每个关键张量的 shape。
  6. 关闭示例后从空文件重写核心部分,并补一个异常或边界测试。
学习记录

记录本节阅读进度

建议先独立回答自测。读完记录不等于已经掌握。

继续下一课
← 上一课
多头注意力:并行看多个子空间
下一课 →
Transformer 全貌:一层里到底有什么

学完不要急着赶进度:去练习自测,或完成对应的阶段实验