位置编码是 Transformer 感知顺序的机制,但它感知的不是"第几个字符",而是"第几个 token"。这一层间接关系经常被忽略:词表粒度一改,同样一段文字的位置结构就变了,RoPE 的波长、上下文窗口的字符容量、外推曲线的形状全部跟着动。本文把这个隐性耦合摆到明面上。
位置是 token 的属性,不是字符的
先钉死基本事实。位置编码 $p_i$ 的下标 $i$ 是 token 序列中的序号:第 $i$ 个 token 拿到位置 $i$,与它代表多少个字符无关。一个词表里,unbelievable 可以是 1 个 token(占 1 个位置),也可以是 4 个 token(占 4 个位置)——两种切法下,模型看到的不是"同一句话的不同编码",而是两个位置结构不同的序列。
由此得到一个直接推论:上下文窗口的字符容量是词表的函数。$n$ 个位置的窗口能装的字符数 $= n \times (\text{字符/token})$。粗粒度词表下每个位置打包更多字符,窗口的"字符容量"更大;细粒度词表下位置数被字节碎片吃掉,容量缩水。厂商标称的 128k 上下文,对英文是约 60 万字符,对韩语在烂词表下可能只有 15 万字符(3070 算过这笔账)。跨语言比较"长上下文能力"而不控制 token 化,结论是不可比的。
粒度如何改写 RoPE 的经验
RoPE 给每个维度对 $k$ 分配旋转频率 $\theta_k = \text{base}^{-2k/d}$,低频维度对应超长波长,承担远距离相对位置的编码(5010 那篇推过这个结构)。词表粒度影响的是位置刻度的语义密度:粗粒度词表下,位置每 +1 跳过 10 个字符,远距离依赖在 token 空间里"距离更近",更容易落进 RoPE 可分辨的区间;细粒度词表下同样的依赖隔着更多位置,超出了训练时见过的相对位置分布。
一个具体的对照:英文长文依赖通常在几十到几百 token 的距离上,中文同样语义的依赖在旧词表下被拉成 2–3 倍距离。RoPE 外推的灾难性失效发生在相对位置超出训练分布时——也就是说,中文文本会更早触发外推失效,失效点按 token 数看相同,按字符数看提前了 2–3 倍。这解释了部分"模型中文长文能力弱于英文"的表象:部分是编码层的差异,不全是表示能力的差异。
token 化噪声:同一文本的不同位置指纹
更微妙的是 token 化本身的不稳定性。BPE 编码对前缀敏感:同样的子串在句首和句中可能切法不同(unbelievable 和 unbelievable 是不同 token)。于是同一段文字换个上下文,token 数变了、位置对齐变了,RoPE 喂给每个语义单元的旋转角度整个重排。位置编码没有"字符坐标"这个概念可以锚定,它对 token 化噪声是完全敞开的。
packing 场景把这个效应放大:多条文档拼接进一个序列时,后段文档的位置序号取决于前段文档的 token 数——前段换个切法,后段所有位置平移。如果注意力隔离做得不干净(没有文档掩码),跨文档的位置串扰会污染位置统计;即使隔离干净,同一篇文档在 packing 里出现的位置也带随机性,位置编码学到的是平均意义上的分布。
对评测的连锁影响
这个耦合让两类常见评测都要打问号。一是大海捞针类测试:针的内容按 token 数算距离,词表粒度不同,同样的"字符距离"对应的 token 距离不同,跨模型比较时粗细粒度模型不在同一条标尺上。二是困惑度对比:细粒度词表的序列更长、条件分布更局部,平均困惑度天然偏低(token 越碎越容易预测),跨 tokenizer 比较困惑度没有意义——这就是各家报分必须配自家 tokenizer 的原因。
训练侧的对策已经出现:长上下文模型在预训练后期混入长文档时,同步把词表向目标语种优化,让位置刻度和目标分布对齐;评测侧则开始报"字符等效上下文"而非裸 token 数。这两个方向都是在还同一个债:位置编码的刻度是 token 化的产物,而 token 化从来不该是隐形变量。
小结
位置编码与 tokenizer 的耦合链条:粒度决定每位置的字符数,字符数决定窗口的真实容量和外推的失效点,token 化噪声决定位置指纹的稳定性。谈"长上下文能力"而不谈词表粒度,等于谈速度而不谈单位。下一篇回到工程实现,看两个主流分词库怎么把这些约束落进代码。