选词表前,先跑一次零参数哨兵
词表选型人人都看 BPB,但未训练模型上的 BPB 摆动超过 6 bits/byte,两条「分词器无关」指标去掉混杂变量后一致性从 0.989 跌到 0.057。把零参数哨兵写进扫描流程,是五分钟能接入的防线。
指标在模型学东西之前,就已经在打分了
分词器是预处理链路的第一环:模型还没见到任何数据,词表已经决定了数据以什么面目出现。11 月 3 日挂上 arXiv 的《Tokenizer Myths and Where To Find Them》(Schaeffer & Koyejo,abs、HTML)用一个非常便宜的实验设计掀了行业的桌子:拿未训练的模型来跑分词器评测指标。逻辑很朴素——一个真正只衡量「模型学得好不好」的指标,在模型什么都没学的时候,对所有分词器应该一视同仁。实测结果:不一视同仁。今日的数据工程观察内参已经覆盖了这条新闻的事件层,这篇文章补上内参没展开的部分:BPB 到底为什么不抵消、数字摆到多大,以及怎么把这个「未训练哨兵」变成自家管道里的固定检查。
三条被零参数实验推翻的常识
论文逐条检验了分词器文献里的三条通说,全部被推翻:
第一条通说:BPB(bits-per-byte)与分词器和词表大小无关,可以跨模型公平比较。这条说法在 2020 到 2026 年的论文里反复出现,原文引了 8 处(Gao et al. 2020、Slagle 2024、Pagnoni et al. 2025、Kallini et al. 2025、Liu et al. 2025 到 Karpathy 2026),措辞从「invariance to different tokenization schemes」到「a tokenizer-agnostic version of perplexity」不等。作者用未训练模型对比 BPE 和 SuperBPE(后者在超过转移点 t=262K 后跨 pre-token 边界合并,转移点之前与 BPE 完全重合),语料是 FineWeb-Edu-dedup 英文版:从转移点到扫描上限 1.05M 的每一个词表大小上,未训练的 SuperBPE 组合 BPB 都更低,上限处差距拉到 1.74 BPB。模型一个参数没学,「分词器—模型」组合已经分出了高下。
第二条通说:BPB 更低意味着模型更好。未训练模型在标准基准上的 BPB 摆动超过 6 bits/byte,精度却停在随机水平。均匀分布参考模型下,正确答案的码长随词表大小在 4.34 到 10.00 bits/byte 之间移动;在答案文本上,BPB 的最小值出现在 8K 到 39K 词表之间,而精度全程平坦,从未在 BPB 最小值处出现过峰值。真模型一侧,TokEval 里在同一数据上训练的模型,BPB 与下游指标在多数基准上只呈弱相关。
第三条通说:不同「分词器无关」指标给出的结论一致。Tao et al. (2024) 报告的 unigram-normalized loss 与 BPB 相关性高达 0.989,被拿来支撑「指标可靠」的论证。论文指出这个 0.989 来自一个混杂变量,把它控制住之后相关性跌到 0.057;更麻烦的是,同一份数据里,这两个指标在每个模型尺寸上选出的最优词表都不一样。
BPB 为什么不抵消
第一条通说的裂缝来自一个看似抵消的乘积。BPB 的定义是「每 byte 的 token 数」乘「每 token 的比特数」:
BPB = (tokens / byte) × (bits / token)
直觉上,词表越大,每 byte 的 token 数越少,但每个 token 要编码的信息越多,两项应该互相抵消。实际情况是两项的消长速率不同步:bits/token 里含着 log V(词表大小的对数),tokens/byte 的下降速度并不恰好跟上它。论文给了一个极端情形的推导:词表取 256^k 且 k 整除所有文档长度时,未训练的均匀模型组合恰好打出 8 bits/byte——任何「公平」的指标都不该在零参数状态下给出这种系统性偏置。
这组数字对应的工程场景很具体:扫词表大小是每个预训练项目的例行操作,32K、64K、128K 各训一个小模型,拿 BPB 最低点定词表。按上面的实测,未训练状态本身就能贡献超过 6 bits/byte 的摆动——训练后观测到的 BPB 差距,有多少来自学习、多少来自度量工具本身,光看最终数字是分不出来的。而且最低点跟着语料走:同一套 BPE 管线,英文 FineWeb-Edu-dedup 上最低点在 24K 附近,法语、俄语、代码、印地语四个语料上从 504 到 117K 不等,中文扫到 1.05M 都不出现最低点——拿别家经验里的最优词表直接照抄,连度量的起点都对不齐。
零参数哨兵:五分钟接入扫描流程
论文的实验设计可以直接搬进管道,成本几乎为零。做法是在正式扫描之前,先对所有候选分词器跑一遍未训练基线:
import torch, math
from transformers import AutoTokenizer, GPT2LMHeadModel, GPT2Config
def untrained_bpb(tokenizer_name, texts, seed=0):
"""零参数哨兵:随机初始化的模型 + 指定分词器,测指标本身的偏向"""
tok = AutoTokenizer.from_pretrained(tokenizer_name)
cfg = GPT2Config(vocab_size=tok.vocab_size, n_layer=2, n_embd=256)
torch.manual_seed(seed)
model = GPT2LMHeadModel(cfg).eval() # 从不训练,只做前向
nll, ntok, nbyte = 0.0, 0, 0
with torch.no_grad():
for t in texts:
ids = tok(t, return_tensors="pt").input_ids
if ids.shape[1] < 2:
continue
loss = model(ids, labels=ids).loss # 平均每 token 负对数似然
nll += loss.item() * ids.shape[1]
ntok += ids.shape[1]
nbyte += len(t.encode("utf-8"))
return nll / math.log(2) / nbyte # bits per byte
base = untrained_bpb("gpt2", texts)
alt = untrained_bpb("Qwen/Qwen2.5-0.5B", texts) # 只借用它的词表,模型仍是随机初始化
spread = abs(base - alt)
print(f"哨兵摆动: {spread:.3f} bits/byte")
判读规则一句话:训练后两个候选的 BPB 差距,小于哨兵摆动幅度的,按度量噪声处理,改用精度侧指标裁决。哨兵本身不告诉你哪个词表好,它告诉你的是手里的尺子在这个比较里还剩多少有效刻度。种子多跑几个(0、1、2),摆动幅度取稳定值——随机初始化本身也有方差。
把哨兵写进配置
落到扫描配置里,就是在 vocab_sweep 前面挂一个 sentinel 段,并强制三指标分开上报:
vocab_sweep:
candidates: [bpe-32k, bpe-64k, bpe-128k, unigram-64k]
sentinel: # 每次扫描必须随附的零参数基线
init: random
seeds: [0, 1, 2]
report: bpb_spread_per_candidate_pair
trained_stage:
seeds: [0, 1, 2]
metrics: # 三者分开报,不许合并成一个数
- bpb
- tokens_per_byte
- downstream_acc
decision_rule: >
训练后 BPB 差 < 哨兵摆动幅度的候选对,标记为 metric_noise,
词表决策改用 downstream_acc 与 tokens_per_byte(成本侧)联合裁决
这份配置里最容易偷懒被砍掉的是 sentinel 段——它的产出不进任何决策表,看起来是纯开销。但它正是那条 6 bits/byte 摆动唯一的自动化来源:没有哨兵,词表扫描报告里的 BPB 曲线无法区分「学习带来的改善」和「分词器带来的偏置」。
三难定理:选指标就是选研究问题
论文最后把这组现象收进一个定理:任何度量都无法一次性满足三条性质——只给真分布打最高分(P1)、给相同的预测序列打相同的分(P2)、让所有未训练模型打平(P3)。三者必弃其一。工程上的读法是:不存在「选出来就完事」的万能指标,挑指标等于挑你愿意回答哪个问题。盯 P1 的人在做密度估计研究,盯 P3 的人在做跨分词器比较,两套诉求在数学上就不可兼得,争论哪个指标「更对」没有出口。
对词表选型这个具体决策,可操作的收口是三条:跨分词器比较模型优劣时,永远不要只报 BPB,把 tokens/byte 单独列出来;词表扫描报告里附上哨兵摆动幅度,小于它的差距不作为决策依据;最终裁决放到精度侧的小规模验证上,哪怕只是几个下游任务的零样本成绩。
这与站内反复强调的验收原则是同一件事的不同切面:验收指标本身也需要验收。10 月初写数据管道三道验收时,结论是「坏数据活到第几步被发现,决定了管道的真实单价」;这次轮到尺子本身——指标在零参数状态下的偏向,决定了词表决策里有多少比例其实是在掷硬币。零参数哨兵是这两件事交汇处最便宜的一道闸:不训模型、不改数据,只是让指标先在空模型上亮一次底牌。