一文搞懂位置编码:固定、旋转、无位置编码,以及上下文窗口扩充

本文不讲数学公式,只讲大白话。目标是让你看完就能跟人聊明白位置编码是怎么回事。


先说清楚:为什么需要位置编码?

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-NeoXGPT-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  长序列微调
        等比缩放  高低频分化  分段策略  适应性训练

现实中的上下文窗口扩充路线

主流大模型的上下文扩展,通常是组合拳

  1. 基础架构选 RoPE(当前最主流)
  2. 应用外推技巧(NTK-aware / YaRN / PI 之一或组合)
  3. 长序列微调(在长文本数据上继续训练)
  4. 工程优化(如 Flash Attention、Ring Attention 等让长序列在显存和速度上可行)

以 LLaMA 为例:

  • LLaMA 2 原始上下文 4K
  • 通过 RoPE 缩放 + 微调 → 扩展到 32K、64K
  • 社区方案(如 Code Llama)用到 16K、100K

六、总结:三句话记住核心

  1. 固定位置编码:给每个位置发个固定身份证号,简单但死板,超出训练范围就废。适合"短平快"的任务,BERT 那一代的标配。

  2. 旋转位置编码(RoPE):不贴标签,而是按位置旋转词向量,天然编码相对距离,配合外推技巧可以扩展到很长的上下文。当前大模型的事实标准。

  3. 无位置编码(NoPE):啥也不加,靠因果掩码让模型自己悟,理论上没有外推问题,但效果和适用性还在研究中。

上下文窗口的扩充,本质上就是在问:“位置编码能不能优雅地覆盖比训练时更大的范围?” RoPE + 外推技巧 + 长序列微调,是目前最成熟的答案。


本文面向概念理解,不涉及数学推导。如需深入了解公式和实现细节,建议参考原始论文。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐