技术博客

tokenizer 的安全问题:glitch token 的产生

第 44 / 147 篇 · 2026-05-10 · ai · tokenizer安全训练动力学

2023 年有人在 GPT 系列模型上发现了一个经典案例:输入 SolidGoldMagikarp 这个 Reddit 用户名,模型开始胡言乱语、无视指令甚至输出错乱内容。这个词是 GPT-2 时代 BPE 词表里的一个完整 token,但它本身在训练数据里几乎不存在。这种"词表里有、世界里没有"的 token 被叫做 glitch token。它不是玄学,从训练目标推一遍就能看清机制。

成因链:梯度从哪来

词表大小 $V$,embedding 矩阵 $E\in\mathbb{R}^{V\times d}$。token $t$ 的 embedding 行 $E_t$ 唯一参与计算的环节是:

$$ x_t = E_{t}\ \text{(取出第 $t$ 行)},\qquad \ell = \mathcal{L}(x_t, \text{其余参数}) $$

对 $E_t$ 的梯度是

$$ \frac{\partial \ell}{\partial E_t} = \frac{\partial \ell}{\partial x_t}\cdot \mathbb{1}[\text{当前样本含 token } t]. $$

关键在指示函数:$E_t$ 只在包含 $t$ 的样本上收到梯度。如果 $t$ 在整个预训练语料里出现 $c_t$ 次,且 $c_t \approx 0$,那 $E_t$ 在几百万步训练里被更新的次数约等于零,参数停在初始化附近——一个范数很小、方向随机的向量。下游所有 Transformer 层从未见过这个输入,它们对 $E_t$ 的响应是完全未校准的:可能把它当成噪声放大,可能触发训练时从未出现过的激活模式。表现为 logit 异常、拒答、复读、输出乱码。

这也解释了 glitch token 的"传染性"很低:正常文本几乎不会自然切出这些 token,它们要靠对抗性拼贴(SolidGoldMagikarp 前后加空格等触发切分)才暴露。

为什么偏偏是这些 token

BPE 的合并不是按频率均匀选出的。词表构建于一个特定时间点的语料快照,某些字符串(老论坛用户名、抓取事故产生的乱码、已删除页面的残留)在快照里出现过恰好足够多次、进入了合并序列,但随即从互联网上消失。语料清洗又过滤了一遍,剩下一个"词表成员 + 语料幽灵"。RAIL 社区的后续统计显示,这类 token 在多个开源模型的词表里都能按"低频次 + 异常嵌入范数"揪出来。

诊断:频次 × 范数双筛

两条独立证据可以离线算出来:

import numpy as np

# counts[t]: token t 在语料中的出现次数;E: (V, d) embedding 矩阵
norm = np.linalg.norm(E, axis=1)          # 每行范数
freq_rank = np.argsort(counts)            # 频次升序

suspects = norm[freq_rank[:5000]]         # 最稀有的 5000 个 token 的范数
# 训练充分的 token 范数有明确量级;未训练的贴近初始化值(如 N(0, 0.02))
# → 对 suspects 画范数直方图,左尾分离出来的就是 glitch 候选

我在 LLaMA 系词表上试过这个筛法:最稀有的一批 token 里确实存在范数明显低于主体的子群体,和已知的 glitch token 名单重合度很高。两个量都便宜——频次一次语料扫描,范数一次 norm 调用。

缓解与清理

  • 训练侧:词表构建后做一次频次审计,把 $c_t$ 低于阈值的 token 移出可用集或并入 byte fallback;对保留下来的低频次 token 做 embedding 范数校准(归一到主体分布)。
  • 推理侧:输入阶段用 tokenizer 的 encode–decode 往返检测可疑切分; jailbreak 研究里已有多套 glitch token 黑名单。
  • 根治:byte-level BPE(GPT-2 路线)把词表锚定在 256 个字节上,任何 token 都可以分解为字节序列兜底,最差情况退化为逐字节建模,不会出现"无法解释的 token"——但 glitch token 并未消失,只是从字节层面挪到了合并单元层面,审计依然要做。

glitch token 成因链:稀有上下文 → 梯度饥饿 → 停在初始化 → 行为不可预测 语料中出现极少 (特定用户名/串) embedding 梯度 几乎为零 停在初始化附近 范数贴近初始值 下游响应未校准 输出不可预测 诊断双指标:语料频次(一次扫描)× 嵌入范数(一次 norm) 低频次 + 范数落在初始化分布左尾 = glitch 候选 ∂ℓ/∂E_t 带指示函数:只有含 t 的样本才更新它,c_t≈0 即梯度饥饿

小结

glitch token 是词表快照与语料现实脱节的产物:BPE 把它造出来,语料清洗又没能把它喂饱,梯度来源的指示函数决定了它永远停在初始化。防御不需要对抗训练,词表审计 + 范数直方图两个离线检查就能覆盖大部分风险。这条线也提醒我们:tokenizer 不是中性的预处理,词表里的每一个成员都暗含着"训练时见过足够多次"的承诺。