Transformer 里的位置编码Positional Encoding / Positional Embedding解决的是一个非常根本的问题Self-Attention 本身只看 token 之间的内容关系不天然知道 token 的先后顺序。也就是说如果没有位置编码Transformer 看到的更像是一袋 token而不是一句有顺序的话。例如我 喜欢 你你 喜欢 我这两句话包含相同的 token但意思完全不同。如果模型只知道出现了“我”“喜欢”“你”却不知道顺序它就无法区分这两句话。所以位置编码的目标是Token 表示 词的含义 位置的信息更具体地说embedding(我) position(第 1 位)embedding(喜欢) position(第 2 位)embedding(你) position(第 3 位)这样模型既知道“这个 token 是什么”也知道“它在第几个位置”。1. 为什么 Transformer 天然不知道顺序RNN 按顺序读取文本第 1 个 token → 第 2 个 token → 第 3 个 token → 第 4 个 token因为它本身就是一个接一个处理所以顺序自然进入了计算过程。但 Transformer 的 Self-Attention 是并行看的token1 ↔ token2 ↔ token3 ↔ token4每个 token 会和所有 token 计算注意力关系。这个机制非常强但它本身没有“先后”的概念。如果只看 Self-Attention 的核心计算Attention(Q, K, V) softmax(QK^T / sqrt(d)) V这里的计算只比较向量之间的相似度没有任何变量天然表示这个 token 是第 1 个那个 token 是第 5 个两个 token 相隔 4 个位置所以必须把位置信息额外加入模型。2. 最朴素的想法给每个位置一个编号最直接的做法是第 1 个 token → 位置 1第 2 个 token → 位置 2第 3 个 token → 位置 3...然后把这个编号加到 token 表示里。但直接使用整数编号有问题位置 1位置 2位置 1000位置 100000这些数字尺度差异太大直接加入神经网络并不稳定。并且一个简单整数表达不了复杂的位置关系例如相邻关系远近关系周期结构不同尺度的距离超出训练长度后的泛化所以位置编码通常不是简单把 1, 2, 3, 4塞进去而是把位置变成一个向量。position 1 → [0.84, 0.54, 0.01, 0.99, ...]position 2 → [0.91,-0.42, 0.02, 0.99, ...]position 3 → [0.14,-0.99, 0.03, 0.99, ...]这样每个位置都拥有一个和 token embedding 维度相同的位置向量。3. 绝对位置编码告诉模型“我在第几个位置”绝对位置编码的核心思想是给序列中的每个绝对位置分配一个向量。流程是token embedding position embedding ↓输入 Transformer例如第 1 位embedding(我) pos(1)第 2 位embedding(喜欢) pos(2)第 3 位embedding(你) pos(3)图示文本我 喜欢 你 ↓ ↓ ↓Token: e1 e2 e3位置p1 p2 p3 ↓ ↓ ↓输入e1p1 e2p2 e3p3这样模型输入的每个向量都同时包含token 的语义token 的位置绝对位置编码有两种常见形式1. 固定公式生成例如原始 Transformer 的 sin/cos 位置编码。2. 可学习位置向量例如很多模型直接学习一个位置 embedding 表。4. Sin/Cos 位置编码原始 Transformer 的经典逻辑原始 Transformer 使用的是固定的正弦/余弦位置编码。它的大致公式是PE(pos, 2i) sin(pos / 10000^(2i / d_model))PE(pos, 2i1) cos(pos / 10000^(2i / d_model))这里pos表示 token 的位置。i表示向量中的第几个维度。d_model表示模型隐藏向量维度。偶数维用 sin。奇数维用 cos。你可以先不纠结公式先抓住直觉每个位置被编码成一组不同频率的波形值。4.1 为什么用 sin 和 cossin/cos 的特点是周期性变化sin:位置增加 → 数值按波形变化cos:位置增加 → 也是波形变化但相位和 sin 不同示意位置0 1 2 3 4 5 6sin: 0 .84 .91 .14 -.76 -.96 -.28cos: 1 .54 -.42 -.99 -.65 .28 .96这让模型可以从波形组合中识别位置。4.2 为什么用不同频率如果所有维度都用同一个频率位置表达能力很有限。原始位置编码使用很多不同频率低维度变化快适合表达短距离差异高维度变化慢适合表达长距离差异可以类比成钟表秒针变化快区分短时间分针变化慢区分中等时间时针变化更慢区分长时间位置编码也是类似逻辑快速波区分相邻位置慢速波区分远距离位置多种波叠加同时表达近距离和远距离结构图示位置0 1 2 3 4 5 6 7 8高频维 ~\/\/\/\/\/\/\/\/\/\/\/~中频维 ~~\____/~~\____/~~\____低频维 ~~~~~~~~\______________组合后每个位置都有独特的多尺度波形指纹4.3 sin/cos 的一个重要性质相对位置可推导sin/cos 不只是为了“好看”。它有一个关键数学性质sin(a b), cos(a b)可以用 sin(a)、cos(a)、sin(b)、cos(b)表示。这意味着位置 posk 的编码可以通过位置 pos 的编码和相对偏移 k 的关系表示出来。这让模型不仅能知道“绝对第几个位置”也更容易学到“两个位置相隔多远”。5. 可学习位置编码让模型自己学位置向量另一种常见方法是直接给每个位置一个可学习向量。pos(1) 一个参数向量pos(2) 一个参数向量pos(3) 一个参数向量...训练时这些位置向量和模型其他参数一起更新。优点简单。模型可以自己学习最适合任务的位置表示。在固定最大长度内效果通常很好。缺点超出训练过的位置后泛化可能较差。如果只学到了 2048 个位置直接扩到 8192 可能不自然。不显式表达相对距离关系。所以早期 GPT 类模型常使用可学习绝对位置 embedding但长上下文模型逐渐转向更适合外推的位置方法例如 RoPE、ALiBi 或它们的变体。6. 相对位置编码模型更关心“相隔多远”绝对位置问的是这个 token 在第几个位置相对位置问的是这个 token 和那个 token 相隔多远例如第 10 个 token 关注第 8 个 token距离 -2第 100 个 token 关注第 98 个 token距离 -2虽然绝对位置不同但相对关系一样。这很符合语言直觉形容词通常修饰附近名词括号通常和附近符号配对代码变量通常和附近定义有关相对位置编码会在 attention 计算里加入距离信息attention_score(token_i, token_j) 内容相似度 相对位置偏置 (distance i - j)这样模型不仅看内容是否相关也看两个 token 的距离是否合理。7. RoPE旋转位置编码的核心逻辑RoPE全称 Rotary Position Embedding中文常叫旋转位置编码。它是很多现代大语言模型的重要位置编码方法。Llama、Qwen、DeepSeek 等模型都使用了 RoPE 或其变体。RoPE 的核心思想是不把位置向量简单加到 token embedding 上而是根据位置把 Q 和 K 向量旋转一个角度。7.1 先理解“旋转”二维平面里一个向量可以旋转原向量 →旋转后↗再旋转↑如果位置越靠后旋转角度越大pos 0: 不旋转pos 1: 旋转一点pos 2: 再多旋转一点pos 3: 继续旋转RoPE 把高维向量拆成很多二维小平面然后在每个二维平面里按位置旋转。向量维度[x1, x2, x3, x4, x5, x6, ...]拆成二维对(x1, x2), (x3, x4), (x5, x6), ...每一对按不同频率旋转7.2 RoPE 怎么把位置放进 AttentionSelf-Attention 里会计算Q · KRoPE 做的是Q_pos rotate(Q, pos)K_pos rotate(K, pos)attention_score Q_pos · K_pos也就是说位置不是额外加进去的而是进入了 Q/K 的方向关系。图示token_i 的 Q 向量 ↓ 按位置 i 旋转rotated Q_itoken_j 的 K 向量 ↓ 按位置 j 旋转rotated K_jrotated Q_i · rotated K_j ↓注意力分数自然包含 i 和 j 的相对位置信息7.3 RoPE 最妙的地方点积里自然出现相对位置RoPE 的关键性质是rotate(Q, i) · rotate(K, j)这个结果会依赖i - j也就是两个 token 的相对距离。这非常重要因为语言模型在很多场景里更关心相对位置当前 token 前一个是什么当前 token 往前 10 个有没有定义当前括号和哪个括号配对当前变量最近在哪里出现所以 RoPE 同时具有绝对位置信息每个位置旋转角度不同。相对位置信息Q/K 点积能体现位置差。7.4 RoPE 和 sin/cos 的关系RoPE 本质上也使用 sin/cos因为二维旋转矩阵就是[ cos θ -sin θ ][ sin θ cos θ ]如果一个二维向量是[x, y]旋转 θ 后是x x cos θ - y sin θy x sin θ y cos θ所以 RoPE 不是抛弃 sin/cos而是把 sin/cos 用在“旋转 Q/K 向量”这件事上。7.5 怎么把 sin/cos 曲线图和旋转图联系起来一个容易卡住的点是sin/cos 曲线是在横轴上画波形旋转是在二维平面里转向量它们看起来像两张不同的图但本质上是同一件事的两种视角。关键关系是sin/cos 曲线其实就是“一个点在圆上旋转时横坐标和纵坐标随位置变化的展开图”。假设一个点在单位圆上旋转当前角度是 θ这个点的坐标就是(cos θ, sin θ)也就是说cos θ 旋转点的横坐标sin θ 旋转点的纵坐标如果这个点一直旋转θ 0°θ 30°θ 60°θ 90°...从圆的视角看它是一个点在绕圆转从曲线的视角看把每个角度对应的横坐标、纵坐标画出来就得到θ - cos θθ - sin θ所以圆上旋转sin/cos 曲线的来源区别只是观察方式不同圆形图看点在二维平面里怎么转曲线图看这个点的 x/y 坐标怎么随位置变化如果把横轴换成 token 位置 m某一组频率是 θ那么位置 m对应的旋转角度就是mθ在 sin/cos 曲线上取值就是cos(mθ), sin(mθ)这两个数正好就是“旋转 mθ这个角度”所需要的参数。RoPE 接下来的动作是拿这两个数去旋转 Q/K 的一对维度。假设 Q 向量里有一对维度(q1, q2)RoPE 根据位置 m做旋转q1 q1 * cos(mθ) - q2 * sin(mθ)q2 q1 * sin(mθ) q2 * cos(mθ)也就是说sin/cos 曲线 ↓ 在位置 m 取值得到 cos(mθ), sin(mθ) ↓ 放进旋转公式旋转 Q/K 的二维向量这就是两张图之间的桥。如果只看一个简单例子假设θ 30°m 2那么当前位置对应的旋转角度是mθ 2 * 30° 60°在 sin/cos 曲线上取值cos(60°) 0.5sin(60°) ≈ 0.866如果原始向量是(1, 0)旋转 60° 后就是(0.5, 0.866)这说明 cos(60°)和 sin(60°)不是抽象的两个数字它们就是二维旋转后的横纵坐标。再回到高维 RoPE。高维 Q/K 会被拆成很多二维对(q1, q2), (q3, q4), (q5, q6), ...每一对维度使用一组不同频率(q1, q2) 使用 cos(mθ1), sin(mθ1)(q3, q4) 使用 cos(mθ2), sin(mθ2)(q5, q6) 使用 cos(mθ3), sin(mθ3)...所以可以把 RoPE 理解成位置 m ↓在很多组 sin/cos 曲线上取值 ↓得到很多组 cos(mθ), sin(mθ) ↓分别旋转 Q/K 的很多二维维度对和原始 sin/cos 位置编码相比差别在于sin/cos 绝对位置编码把 [sin, cos, sin, cos, ...] 拼成位置向量加到 token embedding 上RoPE把 [sin, cos, sin, cos, ...] 当成旋转参数用来旋转 Q/K因此曲线图和旋转图不是两套方法sin/cos 曲线负责提供“转多少”的参数RoPE 负责把 Q/K 真的旋转过去8. RoPE 为什么适合大语言模型RoPE 的优势主要有几个8.1 它把相对距离自然放进 attention语言和代码中大量关系依赖相对距离。前一个词最近的主语附近的括号当前函数里的变量定义RoPE 让 attention score 自然感知 i - j。8.2 它不需要单独学习一张位置表RoPE 通过公式生成旋转角度不是简单查表。因此它比固定长度的位置 embedding 更容易做长度外推。8.3 它适合 Decoder-only 自回归生成GPT 类模型每生成一个 token都要用当前位置和历史位置计算 attention。RoPE 可以很自然地用于 Q/K让每一步的注意力都带上位置信息。8.4 它和长上下文扩展兼容很多长上下文技术会在 RoPE 上做缩放或插值例如位置插值NTK scalingYaRNLongRoPE这些方法本质上是在调整旋转频率或位置映射让模型在更长上下文里不至于“转得太快”或遇到训练外位置时失效。9. ALiBi用距离惩罚表达位置ALiBi全称 Attention with Linear Biases。它的思路更直接在 attention 分数里加入一个和距离有关的线性偏置。简化表示attention_score(i, j) content_score(i, j) - slope * distance(i, j)距离越远分数被扣得越多。直觉是离我近的 token 默认更重要离我远的 token 默认需要付出更多证据才能被关注图示当前位置 i距离 1: 扣一点距离 10: 扣更多距离 100:扣很多ALiBi 的优点是简单。不需要位置 embedding 表。对长度外推比较友好。缺点是表达方式较粗糙。强行加入“越远越不重要”的倾向不一定适合所有任务。10. 位置编码和长上下文的关系长上下文不是只把窗口长度改大。位置编码会直接影响模型能否处理更长文本。如果模型训练时只见过 4K 长度推理时突然给它 128K训练时position 0 .. 4095推理时position 0 .. 131071模型可能遇到没学过或没适配过的位置范围。不同位置方法面对长上下文的表现不同方法长上下文挑战可学习绝对位置 embedding超过训练长度后没有对应位置向量sin/cos 绝对位置编码可外推但高频维度可能带来周期混淆相对位置编码对距离泛化更自然但实现复杂度更高RoPE适合 LLM但长距离下需要 scaling 或插值ALiBi外推友好但位置表达较简单所以很多长上下文模型不只是“扩大 max context length”还会改造位置编码、attention、训练数据和推理系统。11. 位置编码不是简单编号常见误解是位置编码 给 token 标上第几个更准确地说位置编码 把顺序、距离和相对关系转成模型能计算的向量或 attention 偏置不同方法表达的信息不同绝对位置我在第几个相对位置我和你相隔多远RoPE通过旋转让 Q/K 点积感知相对距离ALiBi通过距离惩罚影响 attention 分数12. 一个完整对比图文本我 喜欢 你无位置编码embedding(我)embedding(喜欢)embedding(你)模型知道 token 内容但顺序不明确绝对位置编码embedding(我) pos(1)embedding(喜欢) pos(2)embedding(你) pos(3)模型知道每个 token 在第几个位置相对位置编码attention(我,你) 加入 distance -2attention(你,我) 加入 distance 2模型知道 token 之间的距离和方向RoPE:Q(我) 按 pos 1 旋转K(你) 按 pos 3 旋转Q·K 里自然体现 pos 1 和 pos 3 的差ALiBi:距离越远attention 分数默认扣得越多13. 位置编码在大模型里的实际意义位置编码影响的不只是“第几个 token”还会影响模型能处理多长上下文。模型能不能正确理解代码缩进、括号、作用域。模型能不能区分前后因果。模型能不能处理长文档引用。模型能不能在很远的位置找回信息。模型在长对话中是否稳定。例如代码里function add(a, b) { return a b;}模型需要知道return在函数体内部。{和 }是一对。a、b的定义在前面。两边的变量位置。这些都和位置关系有关。14. 最小总结Transformer 需要位置编码是因为 Self-Attention 本身不天然知道顺序。最核心的逻辑可以总结为Token embedding 负责表达“是什么”Position encoding 负责表达“在哪里”Attention 负责表达“和谁有关”几种主要方法绝对位置编码给每个位置一个向量告诉模型 token 在第几个位置。sin/cos 位置编码用不同频率的波形表示位置兼顾近距离和远距离。可学习位置编码让模型自己学习每个位置的向量。相对位置编码直接表达两个 token 相隔多远。RoPE通过旋转 Q/K 向量让 attention 点积自然包含相对位置信息。ALiBi通过距离惩罚让模型默认更关注近处 token。一句话位置编码不是简单编号而是把“顺序”和“距离”变成神经网络可以计算、可以泛化、可以参与注意力判断的数学结构。