长上下文技术演进:从位置编码到环形注意力

长上下文技术演进:从位置编码到环形注意力

引言 早期Transformer模型受限于 $O(n^2)$ 的注意力复杂度,上下文长度被限制在512或2048 tokens。到2026年,主流模型已普遍支持128K-1M tokens的上下文窗口。这一进步并非单一技术的产物,而是位置编码、注意力机制、架构设计等多个方向协同演进的结果。本文将系统梳理这一技术演进脉络。 位置编码:长上下文的基石 Absolute Position Embedding 原始Transformer使用learned absolute position embedding: $$ h_i = x_i + \text{PE}[i], \quad \text{PE}[i] \in \mathbb{R}^{d_{\text{model}}} $$ 问题:无法外推到训练长度之外的位置,泛化性差。 Sinusoidal Position Encoding $$ \text{PE}(pos, 2i) = \sin\left(\frac{pos}{10000^{2i/d_{\text{model}}}}\right) $$ $$ \text{PE}(pos, 2i+1) = \cos\left(\frac{pos}{10000^{2i/d_{\text{model}}}}\right) $$ Sinusoidal编码具有相对位置信息,但外推能力仍然有限。 RoPE:旋转位置编码 RoPE(Rotary Position Embedding)是当前主流的位置编码方案,通过旋转矩阵实现相对位置编码: $$ f_q(x_m, m) = R_\Theta^m q(x_m), \quad f_k(x_n, n) = R_\Theta^n k(x_n) $$ 其中: $$ R_\Theta^m = \begin{pmatrix} \cos m\theta_1 & -\sin m\theta_1 & 0 & 0 \ \sin m\theta_1 & \cos m\theta_1 & 0 & 0 \ 0 & 0 & \cos m\theta_2 & -\sin m\theta_2 \ 0 & 0 & \sin m\theta_2 & \cos m\theta_2 \ \vdots & \vdots & \vdots & \vdots \end{pmatrix} $$ ...

2026-06-30 · 3 min · 566 words · 硅基 AGI 探索者
positional encoding comparison

位置编码深度对比:RoPE vs ALiBi vs NoPE 实测分析

位置编码:让模型理解顺序的关键 Transformer 架构本身是置换不变的(permutation-invariant),这意味着如果没有位置编码,模型无法区分 “我吃苹果” 和 “苹果吃我”。位置编码为序列注入位置信息,是理解文本顺序和层级结构的关键。 本文深入对比 2026 年主流的三种位置编码方案:RoPE(旋转位置编码)、ALiBi(注意力线性偏置)、以及 NoPE(无显式位置编码),通过数学推导和实测数据揭示它们的长文本外推能力。 一、位置编码的设计目标 好的位置编码应满足: 唯一性:每个位置有唯一的表示 位置关系可推导:模型能学习到相对位置关系(如"第5个词距离第2个词3个位置") 外推能力:在训练时未见过的长度上仍能工作 计算效率:不显著增加计算开销 与注意力机制兼容:不破坏注意力的数学性质 二、RoPE(旋转位置编码) 2.1 数学原理 RoPE 的核心思想是将位置信息编码为旋转操作。对于位置 $m$ 的 Query 和位置 $n$ 的 Key,注意力分数为: $$\text{Attn}(q_m, k_n) = \text{Re}(q_m k_n^*) = \text{Re}(r_m e^{im\theta} \cdot r_n e^{-in\theta})$$ 关键性质:相对位置 $m - n$ 决定旋转角度差,因此 RoPE 自然编码了相对位置: $$q_m^T k_n = f(m - n)$$ 2.2 实现细节 # RoPE 伪代码 def apply_rotary_pos_emb(x, pos, theta=10000): """ x: [seq_len, dim] pos: [seq_len] 位置索引 theta: 基础频率 """ # 将维度分成 pairs dim = x.shape[-1] freqs = 1.0 / (theta ** (torch.arange(0, dim, 2) / dim)) angles = pos[:, None] * freqs[None, :] # 应用旋转 cos, sin = angles.cos(), angles.sin() x_rot = rotate_half(x) return x * cos + x_rot * sin 2.3 外推挑战与解决方案 RoPE 的外推能力受限于训练时的最大长度。如果训练长度为 4096,推理时扩展到 128K 会导致: ...

2026-06-28 · 3 min · 559 words · 硅基 AGI 探索者
positional encoding

位置编码深度解析:从绝对位置到 RoPE 与 ALiBi

1. 为什么需要位置编码 Transformer 的 Self-Attention 机制是置换不变(permutation invariant)的:打乱输入序列的顺序,注意力输出不变。这意味着 Transformer 本身没有序列顺序的概念。 位置编码的使命就是:为模型注入位置信息。 没有位置编码: Attention("猫追狗") == Attention("狗追猫") # 灾难! 有位置编码: Attention("猫追狗") ≠ Attention("狗追猫") # 位置区分了主语和宾语 位置编码的设计需要满足几个理想特性: 唯一性:每个位置应有唯一的编码 相对性:能表达位置间的相对距离 外推性:能处理比训练时更长的序列 周期性:能捕捉位置的模式规律 2. 绝对位置编码 2.1 Sinusoidal(正弦余弦)位置编码 Vaswani 等人 (2017) 提出的正弦余弦位置编码: $$PE_{(pos, 2i)} = \sin\left(\frac{pos}{10000^{2i/d}}\right)$$ $$PE_{(pos, 2i+1)} = \cos\left(\frac{pos}{10000^{2i/d}}\right)$$ import torch import torch.nn as nn import math class SinusoidalPositionalEncoding(nn.Module): def __init__(self, d_model=512, max_len=5000): super().__init__() pe = torch.zeros(max_len, d_model) position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1) # 频率: 1 / (10000^(2i/d)) div_term = torch.exp( torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model) ) pe[:, 0::2] = torch.sin(position * div_term) pe[:, 1::2] = torch.cos(position * div_term) pe = pe.unsqueeze(0) # (1, max_len, d_model) self.register_buffer('pe', pe) def forward(self, x): # x: (batch, seq_len, d_model) return x + self.pe[:, :x.size(1), :] 关键性质:对于固定的偏移 $k$,$PE_{pos+k}$ 可以表示为 $PE_{pos}$ 的线性函数: ...

2026-06-25 · 8 min · 1683 words · 硅基 AGI 探索者
positional encoding guide

位置编码全解:绝对/相对/RoPE/ALiBi

1. 为什么 Transformer 需要位置编码? Self-Attention 本质上是一个集合操作:$\text{Attention}(Q, K, V)$ 对输入的顺序不敏感(permutation equivariant)。如果打乱输入顺序,输出只是相应打乱,不包含任何顺序信息。但语言有明确的顺序语义(“猫追狗” ≠ “狗追猫”),必须注入位置信息。 2. 绝对位置编码 2.1 Sinusoidal (原版 Transformer) Vaswani 等人提出用正弦/余弦函数生成位置编码: $$ PE_{(pos, 2i)} = \sin\left(\frac{pos}{10000^{2i/d_{model}}}\right) $$ $$ PE_{(pos, 2i+1)} = \cos\left(\frac{pos}{10000^{2i/d_{model}}}\right) $$ 性质: 不同维度对应不同频率的正弦波,从 $2\pi$ 到 $10000 \times 2\pi$ 对于任意偏移 $k$,$PE_{pos+k}$ 可以表示为 $PE_{pos}$ 的线性变换:$\sin(pos + k) = \sin(pos)\cos(k) + \cos(pos)\sin(k)$ 理论上可以外推到比训练时更长的序列,但实际效果不佳 import torch import math def sinusoidal_pos_encoding(max_len, d_model): pe = torch.zeros(max_len, d_model) position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1) div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model)) pe[:, 0::2] = torch.sin(position * div_term) pe[:, 1::2] = torch.cos(position * div_term) return pe 2.2 Learned Positional Embedding BERT、GPT-2 使用可学习的位置 Embedding: self.position_embeddings = nn.Embedding(max_position_embeddings, hidden_size) # 前向传播 position_ids = torch.arange(seq_len).unsqueeze(0) embeddings = token_embeddings + self.position_embeddings(position_ids) 优点:灵活,模型可以学习最优的位置表示。缺点:硬编码了最大长度,无法外推。 ...

2026-06-25 · 3 min · 534 words · 硅基 AGI 探索者
鲁ICP备2026018361号