深蓝黑背景封面。左侧圆形图解:虚线圆内从圆心引出向量,灰色虚线标注「原始 q」,经弧形箭头「旋转 mθ」后变为青色实线向量 q_m(位置 m),另一条紫色实线向量标注 k_n(位置 n),两向量间圆弧标注「夹角 = (m−n)θ」。下方卡片写「q_m · k_n 只依赖相对距离 m − n,位置信息随旋转自动注入,无需额外参数」,底部一行注「旋转角随距离拉大,内积自然衰减 —— 远程衰减由此而来」。右侧大标题「RoPE 旋转位置编码,凭什么成为主流」,副标题「不加位置向量,只在注意力前把 q、k 按位置旋转」。左上标注「面试经典 · 位置编码」,右上胶囊标注「Day 03 · 11-20」。 图 1:RoPE 的核心动作就一件事——按位置把 q、k 向量转一个角度。

先说明属性:这是一篇经典题解析,题目出自本地历史面试资料整理,与任何公司的当前招聘无关。RoPE(旋转位置编码)是当下主流开源大模型几乎标配的位置编码方案,面试官问它,考的往往不是背公式,而是「为什么转一转就能带上位置信息」这层直觉。

先搞清楚问题:注意力本身是「位置盲」的。

自注意力的核心运算是 q 和 k 的内积。如果什么都不加,同一个词不管出现在句子第 3 位还是第 300 位,算出来的注意力分数一模一样——模型根本不知道语序。所以必须想办法把位置信息注入进去。早期做法是加性注入:给每个位置准备一个位置向量,直接加到词向量上再送进网络。这条路走得通,但位置信息混在输入里,经过层层变换后还剩多少相对位置语义,并不好说。

RoPE 的思路:不加向量,转角度。

RoPE 由 2021 年的 RoFormer 论文提出。它设定了一个目标:让位置 m 的 q 与位置 n 的 k 做内积时,结果只依赖词向量本身和相对距离 m−n,绝对位置彻底消失。实现方式出人意料地干净——在算完 Q、K 之后、算注意力分数之前,把每个 q、k 向量按自己的位置旋转一个角度:位置 m 的向量旋转 mθ。

为什么旋转能达标?看二维情形最直观:两个向量的内积等于模长相乘再乘夹角余弦。把 q 转 mθ、把 k 转 nθ 之后,两者的夹角从原来的 θ₀ 变成 θ₀+(m−n)θ——绝对位置 m、n 各自消失了,留在内积里的只有相对距离 m−n。这就是「用绝对位置的旋转,实现相对位置的编码」。

高维怎么办?把 d 维向量切成 d/2 对,每对看作一个二维平面独立旋转,第 i 对的旋转频率是 θ_i = 10000^(−2i/d):前面的对转得快(高频、管近距离分辨),后面的对转得慢(低频、管远距离趋势)。多组频率叠加,既能分清相邻位置,又能感知长距离。

用代码验证两条关键性质。

下面用 Node.js 22.15.0 实测(node rope.js)。第一组实验固定同一对词向量,只改它们的绝对位置、保持相对距离不变:

const d = 8, base = 10000;
const theta = i => Math.pow(base, -2 * i / d);
function rope(v, m) {                      // 配对旋转
  const out = v.slice();
  for (let i = 0; i < d / 2; i++) {
    const ang = m * theta(i), c = Math.cos(ang), s = Math.sin(ang);
    const [x, y] = [v[2 * i], v[2 * i + 1]];
    out[2 * i] = x * c - y * s;
    out[2 * i + 1] = x * s + y * c;
  }
  return out;
}
const dot = (a, b) => a.reduce((s, x, i) => s + x * b[i], 0);
const x = [0.8, -0.3, 1.2, 0.5, -0.7, 0.9, 0.2, -1.1];
const y = [0.4, 1.0, -0.6, 0.8, 1.1, -0.2, 0.7, 0.3];
for (const [m, n] of [[3, 1], [103, 101], [505, 503]])
  console.log(`m=${m}, n=${n}: 内积 = ${dot(rope(x, m), rope(y, n)).toFixed(6)}`);

输出:

m=3, n=1: 内积 = -0.390216
m=103, n=101: 内积 = -0.390216
m=505, n=503: 内积 = -0.390216

只要 m−n 相同,不管绝对位置挪到哪里,内积分毫不差——相对位置性质成立。第二组实验让 q 和 k 是同一个词向量,逐渐拉开距离,内积从 5.47 一路振荡衰减:距离 16 时降到 3.62,距离 128 时只剩 1.79。这就是远程衰减——相距越远的 token 天然互相影响越弱,符合语言「近处依赖强、远处依赖弱」的直觉。

深蓝黑背景概念图。左侧三步推导链自上而下以箭头串联:第一步「维度两两配对」,d 维向量切成 d/2 个二维子空间,每对独立旋转;第二步「各对按频率旋转」,第 i 对旋转角为 m·θ_i,θ_i = 10000^(−2i/d),高频转得快、低频转得慢;第三步青色高亮「内积汇合」,q_m·k_n 的每一项只含 (m−n)θ_i,绝对位置消失、只剩相对距离。右侧坐标系:横轴为相对距离 |m−n|,纵轴为内积贡献,一条振幅渐减的振荡曲线从高位起伏收敛到零轴,标注「近距离:内积大」「距离拉大:振幅衰减 → 远程衰减」。底部总结条:优点是相对位置天然成立、不增加可训练参数、配合位置插值可外推更长上下文;另注对比:绝对位置编码把位置向量加到输入上,RoPE 在注意力内积里改角度。 图 2:配对旋转三步推导与远程衰减曲线,绝对位置在内积中彻底消失。

为什么它能成为主流。

把优点摆在一起看:其一,相对位置在内积层面天然成立,不是事后拼凑;其二,不引入任何可训练参数,没有位置嵌入表要存要学;其三,序列长度灵活,训练时没见过的位置也能直接算(真正的长上下文外推还要配合位置插值等手段,那是另一个话题);其四,远程衰减这个归纳偏置白捡。代价几乎为零——只是两次逐对的旋转,可以融合进注意力计算里。

面试作答建议的结构:先说注意力位置无关这个动机,再给出「内积只保留相对距离」的设计目标,然后用二维旋转解释为什么转动能达成目标,高维情形一句「逐对旋转、频率递减」带过,最后落到四条优点。能讲清「绝对位置怎么在内积里消掉」这一步,就超过了大多数背公式的回答。你第一次接触 RoPE 时,卡在旋转矩阵的推导上,还是卡在「为什么是相对位置」的直觉上?欢迎聊聊你的理解路径。