一文搞懂位置编码:固定、旋转、无位置编码
一文搞懂位置编码:固定、旋转、无位置编码,以及上下文窗口扩充
本文不讲数学公式,只讲大白话。目标是让你看完就能跟人聊明白位置编码是怎么回事。
先说清楚:为什么需要位置编码?
Transformer 架构有个"先天缺陷"——它天生不知道词的顺序。
打个比方:你把一句话拆成一个个词,扔进一个袋子摇匀,Transformer 看到的东西和按顺序看是一模一样的。对它来说,"猫追狗"和"狗追猫"如果不加任何标记,它分不清谁在前谁在后。
这显然不行。语言是有顺序的,"我打你"和"你打我"意思完全不同。
位置编码就是给每个词贴一个"位置标签",告诉模型:“这个词在第 1 个位置”“那个词在第 5 个位置”。这样模型就能区分顺序了。
具体的做法有三大流派:固定位置编码、旋转位置编码、无位置编码。下面一个一个说。
一、固定位置编码(Absolute/Fixed Positional Encoding)
一句话概括
给每个位置分配一个固定的、独一无二的"身份证号",词在哪个位置就拿哪个号。
大白话解释
想象一栋楼,每个房间门口钉了一个门牌号:101、102、103……你只要说出门牌号,就知道是哪个房间。固定位置编码就是干这个事——位置 1 有一个固定的编码向量,位置 2 有另一个固定的编码向量,以此类推。
这个编码是提前算好的,不随训练变化。原版 Transformer 论文里用的是正弦/余弦函数生成的一组数字,但你现在不用管它怎么算的,只需要知道:
- 每个位置对应一个固定的向量(一串数字)
- 不同位置的向量不一样
- 这个向量直接加到词向量上(词向量 + 位置向量 = 输入模型的东西)
优点
- 简单粗暴,实现容易,加一下就完事了
- 理论上可以泛化到训练时没见过的更长位置(因为正弦函数可以无限延伸)
缺点
- 绝对位置思维:它编码的是"这是第 5 个位置",而不是"这个位置和那个位置差了 3 步"。但在语言里,很多时候重要的是相对距离而非绝对位置
- 外推能力差:虽然理论上正弦函数可以延伸到任意长度,但实际用起来,训练时只见过 512 个位置,推理时给它第 2000 个位置,效果会明显变差。模型没有真正"学会"怎么处理远处的位置
- 加法操作有信息干扰:位置信息和词义信息直接相加,有时候会"打架",词义信息被位置信息干扰
谁在用
- 原版 Transformer(2017)
- BERT(用的也是固定编码,但是可学习的版本——每个位置的向量是训练出来的,不是公式生成的)
- GPT-2
一个重要区分:固定 vs 可学习
固定位置编码里又分两种:
- 完全固定:用公式提前算好,永不改变(原版 Transformer)
- 可学习:每个位置的向量是随机初始化的参数,跟着模型一起训练(BERT 就是这种)
可学习的版本效果通常略好一点,但有个硬伤——最大长度在训练时就定死了。你训练时设了 512 个位置向量,那推理时就最多只能处理 512 个词,第 513 个位置根本不存在对应的向量。这比公式生成的版本还不灵活。
二、旋转位置编码(Rotary Position Embedding,RoPE)
一句话概括
不给位置贴静态标签,而是根据位置把词向量"旋转"一个角度,位置不同旋转角度不同,两个词的相对距离自然就编码进了它们的内积里。
大白话解释
先说它要解决的核心问题:我们其实更关心相对位置,而不是绝对位置。
"我爱你"这句话,"我"在第 1 个位置还是第 101 个位置不重要,重要的是"我"在"爱"前面、相隔 1 步。模型需要知道的是词和词之间的距离关系。
旋转位置编码的思路很巧妙:
想象每个词向量是一个箭头(从原点指向某个方向)。RoPE 做的事情是——根据这个词在第几个位置,把这个箭头旋转一定角度。位置越靠后,旋转角度越大。
关键妙处在于:当模型计算两个词的注意力(就是两个词向量的内积/点积)时,两个箭头之间的夹角只取决于它们的位置差。位置 3 的箭头和位置 5 的箭头之间的夹角,跟位置 103 和位置 105 之间的夹角是一模一样的。
这就意味着:
- 模型不需要显式存储"我在第 3 个位置"这种绝对信息
- 它天然就能感知"这两个词隔了 2 步"这种相对信息
- 相同的相对距离,不管出现在序列的哪个位置,注意力模式是一样的
再打个比方
固定位置编码像 GPS 坐标——告诉你"你在经度 116.4、纬度 39.9"。
旋转位置编码像指南针——不管你在哪,它告诉你"目标在你前方 30 度方向"。你只关心相对方向,不关心绝对坐标。
优点
- 天然编码相对位置:这是最大的优势,符合语言的本质需求
- 乘法操作,不干扰词义:不是把位置信息"加"到词上,而是"旋转"词向量,词的长度不变,语义信息保真度更高
- 理论上支持任意长度:只要旋转角度能继续算下去,理论上可以处理任意长的序列
- 实际外推效果好:配合一些技巧(如 NTK-aware scaling),可以从短序列外推到长序列,效果比固定编码好很多
缺点
- 实现比固定编码复杂一点(但也就复杂一点,不算大问题)
- 直接外推到远超训练长度的位置时,效果仍会下降(但可以通过缩放技巧缓解,后面会讲)
谁在用
- LLaMA 系列(Meta,目前最主流的开源大模型架构)
- Qwen 系列(阿里通义千问)
- ChatGLM
- Mistral
- GPT-NeoX、GPT-J
- 基本上 2022 年之后的主流开源大模型,大部分都用 RoPE
可以说,RoPE 是目前大模型位置编码的事实标准。
三、无位置编码(No Positional Encoding / NoPE)
一句话概括
干脆不编码位置,让模型自己通过注意力机制去"悟"出顺序。
大白话解释
你可能觉得这听起来离谱——前面不是说 Transformer 天生不知道顺序吗?不给位置编码,那不就乱套了?
先别急。研究发现一个有趣的事情:标准的 Transformer 注意力机制,如果配合因果掩码(causal mask,就是让每个位置只能看到它前面的位置),其实已经隐含了位置信息。
因为有了因果掩码,第 1 个位置只能看到自己,第 5 个位置能看到前 5 个词。这种"能看到的范围不同"本身就是一种位置信号——模型可以通过"我能看到几个词"来推断自己在什么位置。
所以 NoPE 的逻辑是:与其手动注入可能有偏差的位置信息,不如让模型自己学。
但有个重要前提
NoPE 的隐式位置感知能力,跟模型层数有关:
- 深层模型(几十层以上):层数够多,模型有足够的机会从因果掩码中"推断"出位置信息,不加位置编码也能work
- 浅层模型(几层):层数太少,模型来不及学会推断位置,效果会明显变差
优点
- 极简:什么都不加,实现最简单
- 没有外推问题:因为没有显式的位置编码,理论上不存在"超出训练长度就失效"的问题。这一点对长上下文很有吸引力
- 不引入归纳偏置:不假设位置信息必须以某种特定形式存在,让模型自由学习
缺点
- 对模型深度有要求:浅了不行
- 位置信息可能学得不够好:隐式学到的不如显式编码的精确
- 实际效果有争议:学术界对 NoPE 是否真的能媲美 RoPE 还有争论,不同实验结论不一
谁在用
- 目前主要在学术研究中探索
- 部分小模型实验中使用
- 尚未成为主流大模型的选择
四、三种方式对比一览
| 维度 | 固定位置编码 | 旋转位置编码(RoPE) | 无位置编码(NoPE) |
|---|---|---|---|
| 编码方式 | 给每个位置一个固定向量,加到词向量上 | 根据位置旋转词向量 | 不编码,靠因果掩码隐式感知 |
| 位置类型 | 绝对位置 | 相对位置 | 隐式位置 |
| 与词向量的操作 | 加法 | 乘法(旋转) | 无操作 |
| 外推能力 | 差 | 较好(配合缩放技巧更好) | 理论上无限制 |
| 实现复杂度 | 简单 | 中等 | 最简单 |
| 主流应用 | BERT、GPT-2 等早期模型 | LLaMA、Qwen、Mistral 等当前主流大模型 | 学术研究阶段 |
| 最大长度限制 | 可学习版本有硬限制 | 理论无限制,实际需配合技巧 | 理论无限制 |
五、位置编码与上下文窗口扩充的关系
这是很多人关心的问题:为什么大模型的上下文窗口能从 2K 扩展到 32K、128K 甚至 1M?位置编码在其中扮演了什么角色?
先搞清楚:什么是上下文窗口
上下文窗口就是模型一次能处理的最大文本长度。GPT-3 是 2048 个 token,GPT-4 可以到 128K,Claude 能到 200K。
上下文窗口越大,模型能"记住"的对话历史越长,能处理的文档越大。但扩大窗口不是简单地把参数表里的"max_length"改个数字就行——位置编码是最大的拦路虎之一。
问题出在哪
固定位置编码的问题
如果你用的是可学习的固定位置编码,训练时定义了 2048 个位置向量,那模型就只能处理 2048 个 token。第 2049 个位置没有对应的向量,模型直接懵了。
即使用正弦公式生成的固定编码(理论上可以无限延伸),模型在训练时也只"见过"前 2048 个位置的模式。超出这个范围,虽然编码能算出来,但模型没学过怎么处理,效果急剧下降。
RoPE 的问题
RoPE 虽然理论上可以无限延伸,但实际问题在于:训练时只见过 0~2047 范围内的旋转模式,推理时遇到 5000、10000 的位置,旋转角度超出了训练时见过的范围,模型的注意力计算就"失准"了。
打个比方:你练篮球只在三分线内投篮,突然让你从中场投,虽然篮筐还是那个篮筐,但距离差太多,你的肌肉记忆不顶用了。
怎么解决:位置编码的"外推"技巧
为了扩大上下文窗口,研究者们发明了一系列位置编码外推技术。核心思想都是:让模型在短序列上训练,在长序列上推理时仍然有效。
1. 位置插值(Position Interpolation, PI)
大白话:把长序列"压缩"到模型见过的范围内。
模型训练时见过 0~2047 的位置。现在来了个 8192 长度的序列,怎么办?把 8192 个位置等比例缩放到 0~2047 的范围内——原来位置 0 还是 0,原来位置 8192 变成 2047,中间的位置按比例插值。
相当于把一把 8192 厘米的尺子,压缩成 2047 厘米的尺子来读。刻度还是那些刻度,只是间距变密了。
- 优点:实现简单,只需改一行代码(位置索引除以缩放因子)
- 缺点:压缩后相邻位置之间的间隔变小,模型对"相邻"的感知变模糊,短序列性能可能下降
2. NTK-aware Scaling
大白话:不是简单地把所有刻度等比压缩,而是高频部分保持不变,低频部分才缩放。
为什么这样做?在 RoPE 中,不同维度的旋转频率不同——高频维度旋转快,负责精细的局部位置感知;低频维度旋转慢,负责粗略的全局位置感知。
NTK-aware 的思路是:高频部分(局部精度)不需要改,因为短距离关系在长序列中依然适用;低频部分(全局范围)需要扩展,让它覆盖更大的范围。
打个比方:手表的秒针不用动(短时间精度够用),只需要让时针转更多圈(覆盖更长时间范围)。
- 优点:比简单插值效果好,短序列性能损失小
- 缺点:实现稍复杂
3. YaRN(Yet another RoPE extensioN)
大白话:NTK-aware 的升级版,对不同频率维度做更精细的差异化处理,并配合额外的微调。
YaRN 把 RoPE 的维度按频率分成几段,每段用不同的策略——有的插值、有的外推、有的混合。相当于"因材施教",不同维度用不同方法。
- 优点:目前效果最好的 RoPE 扩展方法之一
- 缺点:需要少量长序列微调
4. 长度外推微调(Continual Training / Fine-tuning)
大白话:光改位置编码算法不够,还得让模型在长序列上"练一练"。
不管用哪种外推技巧,模型从短序列跳到长序列,都需要一个适应过程。做法是:用外推后的位置编码,在长序列数据上做少量微调(几千步),让模型适应新的位置范围。
这就是为什么很多模型发布时会特别标注"经过长上下文微调"——不是位置编码改了就立刻能用,还得练。
NoPE 在上下文扩展中的特殊地位
NoPE 有一个被关注的优势:因为它没有显式的位置编码,自然就不存在外推问题。
不用插值,不用 NTK,不用 YaRN——位置长度想多长就多长。理论上 NoPE 在长上下文场景下有天然优势。
但目前的问题是:NoPE 的整体效果还不如 RoPE,所以"没有外推问题"这个优势暂时还没兑现成实际产品。这是一个值得关注的研究方向。
一张图看懂上下文扩展的核心逻辑
训练时:模型在 0~L 的位置范围内学习
↓
推理时:需要处理 0~4L 甚至 0~64L 的位置
↓
位置编码怎么办?
↓
┌───────────────┼───────────────┐
↓ ↓ ↓
固定编码 RoPE NoPE
没见过=直接废 旋转角度超出 天然无限制
训练范围=失准 (但效果待验证)
↓
┌─────┬───┴───┬──────┐
↓ ↓ ↓ ↓
PI NTK-aware YaRN 长序列微调
等比缩放 高低频分化 分段策略 适应性训练
现实中的上下文窗口扩充路线
主流大模型的上下文扩展,通常是组合拳:
- 基础架构选 RoPE(当前最主流)
- 应用外推技巧(NTK-aware / YaRN / PI 之一或组合)
- 长序列微调(在长文本数据上继续训练)
- 工程优化(如 Flash Attention、Ring Attention 等让长序列在显存和速度上可行)
以 LLaMA 为例:
- LLaMA 2 原始上下文 4K
- 通过 RoPE 缩放 + 微调 → 扩展到 32K、64K
- 社区方案(如 Code Llama)用到 16K、100K
六、总结:三句话记住核心
-
固定位置编码:给每个位置发个固定身份证号,简单但死板,超出训练范围就废。适合"短平快"的任务,BERT 那一代的标配。
-
旋转位置编码(RoPE):不贴标签,而是按位置旋转词向量,天然编码相对距离,配合外推技巧可以扩展到很长的上下文。当前大模型的事实标准。
-
无位置编码(NoPE):啥也不加,靠因果掩码让模型自己悟,理论上没有外推问题,但效果和适用性还在研究中。
而上下文窗口的扩充,本质上就是在问:“位置编码能不能优雅地覆盖比训练时更大的范围?” RoPE + 外推技巧 + 长序列微调,是目前最成熟的答案。
本文面向概念理解,不涉及数学推导。如需深入了解公式和实现细节,建议参考原始论文。
更多推荐

所有评论(0)