Transformer 位置编码:从数学原理到工程实战

5 阅读1分钟

1. 核心公式与参数图谱

位置编码(PE)的本质是为模型提供一套坐标系。我们将 pospos(词位置)和 ii(维度索引)作为变量,通过不同的函数注入向量。

A. Sinusoidal PE (叠加式 - Transformer 原版)

PE(pos,2i)=sin(pos100002id),PE(pos,2i+1)=cos(pos100002id)PE_{(pos, 2i)} = \sin\left(\frac{pos}{10000^{\frac{2i}{d}}}\right), \quad PE_{(pos, 2i+1)} = \cos\left(\frac{pos}{10000^{\frac{2i}{d}}}\right)

  • 结合方式xfinal=xinput+PEx_{final} = x_{input} + PE
  • 物理意义:为每个位置生成唯一的“指纹”向量。
  • 缺陷:改变了向量模长,且无法处理训练长度以外的位置(外推性差)。

B. RoPE (旋转式 - 现代大模型标配)

将维度 dd 两两配对,每对维度 (x1,x2)(x_1, x_2) 视为复平面上的点,进行角度为 mθm\theta 的旋转:

(x~1x~2)=(cos(mθi)sin(mθi)sin(mθi)cos(mθi))(x1x2)\begin{pmatrix} \tilde{x}_1 \\ \tilde{x}_2 \end{pmatrix} = \begin{pmatrix} \cos(m\theta_i) & -\sin(m\theta_i) \\ \sin(m\theta_i) & \cos(m\theta_i) \end{pmatrix} \begin{pmatrix} x_1 \\ x_2 \end{pmatrix} 其中频率参数为:θi=100002id\theta_i = 10000^{-\frac{2i}{d}}

  • 参数 ii:决定旋转频率。ii 小则旋转快(秒针),捕捉近距离;ii 大则旋转慢(时针),感知大局。
  • 模长不变性:旋转只改变向量方向,不改变能量分布,词义表达更纯粹。

2. 长度扩展:缩放插值 (Scaling & Interpolation)

当需要模型处理超出训练长度的任务时(如 4k → 128k),原始位置向量表失效,需重构“刻度”。

2.1 Linear Scaling (线性插值)

  • 逻辑pospos/spos \leftarrow pos / s
  • 本质:等比例挤压。模型在 32k 长度下看到的角度分布与 4k 时完全一致,但分辨率下降。

2.2 NTK-Aware Scaling (非均匀插值)

  • 逻辑:不直接除以 ss,而是通过增大基数 100001,000,00010000 \to 1,000,000
  • 本质:高频维度(秒针)几乎不动,保持近距离精度;低频维度(时针)大幅缩放,扩展远距离视野。

2.3 YaRN (混合/分段插值)

目前最先进的方案,将 d/2d/2 个维度进行分段处理:

  • 不缩放区:波长远小于训练长度的维度,完全不插值。
  • 线性缩放区:波长远大于训练长度的维度,进行完整线性插值。
  • 过渡区:中频维度平滑过渡。
  • 温度修正:引入修正因子防止注意力分布随长度增加而变稀释。

3. 工程实践:从冷启动到查表推理

在工业级推理框架(如 vLLM, TensorRT-LLM)中,位置编码的工程路径遵循 “一次预算,全局查表” 的原则。

3.1 启动阶段:Cache 的重构与冷启动

服务启动(Loading)时,系统会根据配置文件中的 max_position_embeddingsscaling_factor 执行以下步骤:

  1. 参数固化:计算缩放因子 ss。如果是 YaRN,则预先算好每个维度的特定缩放掩码(Mask)。
  2. 构建全量表:在显存中开辟空间,根据 Scaling 后的公式一次性算出从 00max_lenmax\_len 的所有 sin\sincos\cos 值。
  3. 向量表形态:生成的 cos_cachedsin_cached 形状为 [max_seq_len, d/2]
    • 注:这张表是全局共享的,不随 Batch Size 增加而重复。

3.2 推理阶段:O(1) 复杂度的算子实现

推理时(Forward),位置编码的操作被精简为极速的内存访问:

  1. 索引定位:根据当前 Token 的位置索引 mm,从缓存表中直接读取第 mm 行(长度为 d/2d/2)。
  2. 维度广播:将读取的 d/2d/2 个值通过 .repeat(1, 2).interleave() 扩展到词向量的 dd 维空间。
  3. 高效算子 (Kernel):使用专用的 CUDA Kernel 执行 apply_rotary_emb
    • 核心操作out = (input * cos) + (rotate_half(input) * sin)
    • rotate_half 实现:将向量 xx 拆分为 [xleft,xright][x_{left}, x_{right}],变换为 [xright,xleft][-x_{right}, x_{left}]

3.3 内存与性能权衡

  • 预计算 vs 实时计算:YaRN 等复杂公式若实时计算会拖慢 TTFT(首字延迟)。预计算虽额外占用几百 MB 显存,但换取了极高的推理吞吐。
  • 动态 Scaling 的挑战:若要在推理中途动态修改长度(如从 32k 突增到 128k),需要重新触发 3.1 中的冷启动重构表,这会导致短暂的推理停顿。

4. 总结:位置编码的逻辑闭环

环节关键结论
位置计算位置 pospos 是“幅度”,维度 ii 是“频率”。所有维度共同构成一套“多焦镜片”。
表征形式位置信息不独立存在,而是“拧”进了词向量的相位中。
Scaling 本质通过数学插值,让模型在更长的序列里,依然能找到训练时熟悉的“旋转步长感”。
工程核心复杂度被封印在“启动预计算”中。推理开销恒定,真正的长文本瓶颈在于 KV Cache。