知识库 / 数据治理
GitHub
← 数据治理

LAYER 02 / DATA ENGINEERING

Tokenizer、样本格式与 Packing

所属: 数据治理层。本页边界: 文本怎样变成 token ID、训练样本和 loss mask。

#核心公式

x→τ[v1,…,vT],Lmask=−∑t=1Tmtlog⁡pθ(vt∣v<t) x\xrightarrow{\tau}[v_1,\ldots,v_T], \qquad \mathcal L_{\mathrm{mask}} =-\sum_{t=1}^{T}m_t\log p_\theta(v_t\mid v_{<t})

第一部分定义文本到 token ID 的离散化,第二部分说明样本格式如何通过损失掩码决定哪些位置真正产生训练梯度。

#符号说明

符号 含义 单位/条件
xx 原始文本或结构化消息序列 字符/字节序列
τ\tau tokenizer 编码函数 映射
vtv_t 第 tt 个 token ID vt∈{1,…,∣V∣}v_t\in\{1,\ldots,\lvert V\rvert\}
VV tokenizer 词表 有限集合
TT token 序列长度 正整数
mtm_t 第 tt 个位置的损失掩码 通常为 0 或 1
pθ(vt∣v<t)p_\theta(v_t\mid v_{<t}) 模型对下一个 token 的条件概率 [0,1][0,1]
IjI_j Packing 后第 jj 条独立样本的位置集合 位置集合
AabA_{ab} 查询位置与被注意位置分别为 a, ba,\ b 时的可见性 0 或 1
CC 用于评估 tokenizer 的语料集合 文档集合
T(x;τ)T(x;\tau) 文本与 tokenizer 分别为 x, τx,\ \tau 时的 token 数 token 数
bytes⁡(x), ρτ(C)\operatorname{bytes}(x),\ \rho_\tau(C) 文本字节数、语料平均字节/token 比 Byte、Byte/token
a,ba,b Packing 后序列中的查询位置与被关注位置 位置索引

#技术要点

  • BPE 训练本质是贪心搜索最大化压缩效用的合并序列 μ∗\mu^*,而非简单的频率统计。Unigram 从大词表出发逐步剪枝,基于概率模型选择最优分词。
  • 特殊 token 定义文档边界、角色、工具调用和多模态接口。Chat template 是模型输入协议的一部分,模板错位是静默错误——不会抛出异常,但会严重损害性能。
  • Packing 减少 padding,但必须防止跨样本 loss 或注意力泄漏。TRL 的 bfd 策略通过 position_ids 分割实现了无交叉污染的 FlashAttention-2 加速。
  • 多模态 tokenizer 将图像、音频离散化为 token 序列,与文本共享同一个自回归目标。
  • Tokenizer 公平性可通过 Gini 系数和 Single Token Retention Rate(STRR)量化,Parity-aware BPE 可将跨语言 token 成本不平等降低最高 89%。

#原理与演进

#Tokenizer 的基本取舍

粒度 优点 缺点
字符/字节 词表小,几乎无未登录项 序列更长,注意力与 KV 成本增加
词 序列短 词表巨大,低频词处理差
BPE/Unigram 子词 在词表与序列长度间折中 切分依赖训练语料,对语言不完全公平
  • BPE 从小单位出发,反复合并高频相邻片段;Unigram 从候选词表中选择使语料概率较高的分词。两者都把文本映射为有限词表 ID。
  • 相同文本在不同 tokenizer 下 token 数不同,因此按 token 计算的困惑度和成本不能直接跨 tokenizer 比较。

#样本格式与 Packing

页首 Lmask\mathcal L_{\mathrm{mask}} 中的 mtm_t 是损失掩码:例如指令微调可让用户输入位置为 00,仅对助手回答求损失。

  • Packing 把多条短样本放进同一长度窗口,减少 padding;必须显式处理文档边界、位置编号和跨样本注意力,否则会产生“上一样本可以看到下一样本”的伪上下文。
  • Chat template 把角色、消息边界与工具结果编码为 token 序列;模板错位会使同一权重在训练与推理中接收不同输入协议。

关联: 预训练目标解释如何使用 token;工具调用解释结构化输出协议。

#1. 为什么分词是建模选择,不只是预处理

自回归模型对 token 序列建模,不直接对“词”建模。若一个汉字、代码符号或数字被拆成更多 token,同一字符长度就消耗更多上下文和生成步数。

T(x;τ)=∣τ(x)∣,ρτ(C)=∑x∈Cbytes⁡(x)∑x∈CT(x;τ). T(x;\tau)=|\tau(x)|,\qquad \rho_{\tau}(C)=\frac{\sum_{x\in C}\operatorname{bytes}(x)}{\sum_{x\in C}T(x;\tau)}.

ρτ\rho_{\tau} 是某语料上的字节/token 压缩率,必须结合语言和任务看。更高压缩率不保证更好理解:罕见术语合成一个 token 可能缺少共享的子词结构;过细切分则使长程依赖与 KV 缓存成本上升。

#1.1 多语言压缩率的实证差异

不同 tokenizer 在不同语言和领域上的压缩率存在显著差异。以 TokAlign 的实测数据为例:

领域/语言 Gemma LLaMA3 LLaMA2 Qwen2
数学(ArXiv) 2.86 2.78 2.70 2.74
教科书 4.09 4.33 3.65 4.29
Wikipedia 3.18 3.20 2.88 3.03
Python 代码 3.34 4.13 3.01 4.03
Java 代码 3.72 4.49 3.22 4.41
Go 代码 2.93 3.48 2.52 3.39

LLaMA3 在代码上的压缩率显著高于 LLaMA2(Python:4.13 vs 3.01),说明其词表对代码 token 的覆盖更好。但同一 tokenizer 在不同领域的压缩率差异可达 1.5 倍以上,这意味着按 token 计费或按 token 设置上下文长度时,不同领域的实际信息密度并不相同。

压缩率与字符/token 比的区别。 对于 CJK 和泰语等文字系统,压缩率(UTF-8 字节/token)和字符/token 比(CPT)可能给出相反的信号。GPT-2 在中文上的 CPT 为 0.6(即一个 token 对应不到一个汉字),但压缩率为 1.5——说明 tokenizer 虽然在“字符”层面扩张了序列,但在“字节”层面仍然压缩了数据。评估多语言 tokenizer 时,两者都需要报告。

#2. BPE 与 Unigram 的算法细节

#2.1 BPE 的训练过程

BPE 训练本质是贪心搜索最大化压缩效用的合并序列。设文本 tt 应用合并序列 μ\mu 后的压缩收益为:

κt(μ)=∣t∣−∣mμ(t)∣ \kappa_t(\mu)=|t|-|m_\mu(t)|

其中 ∣t∣|t| 是原始文本长度,∣mμ(t)∣|m_\mu(t)| 是应用合并后的序列长度。BPE 在语料上迭代执行合并操作,直到达到目标词表大小。

单步合并规则如下:,其中 f(u)f(u) 为当前语料中相邻片段对 uu 的频次。每步将频次最高的片段对合并为新 token,并更新语料中所有出现该对的位置。

u∗=arg⁡max⁡uf(u) u^*=\arg\max_u f(u)

朴素实现的低效与优化。朴素 BPE 训练对每次合并都遍历所有字节对来找到最频繁的对,非常慢。关键观察是:每次合并后,只有与合并对重叠的对的计数会改变。利用这一性质,可以维护一个优先队列和增量更新机制,将每步复杂度从 O(n)O(n) 降低到 O(log⁡n)O(\log n) 级别。

合并顺序的重要性。BPE 学习到的合并规则是有序的——推理时按创建顺序依次应用合并规则。先学习的合并规则(高频片段)优先于后学习的规则,这保证了高频组合优先被压缩。

#2.2 Unigram 的训练过程

Unigram 的训练方向与 BPE 相反:从大词表出发逐步剪枝。初始化时,词表包含语料中所有出现至少一次的唯一字符,以及通过 BPE 多次迭代或高频子串提取得到的大规模候选 token 集合。

每次迭代:

  1. 为当前词表中的每个 token tt 分配概率如下,其中 freq(t)freq(t) 是 token 在所有可能分词中出现次数。
r(t)=freq(t)/∑t′∈Vfreq(t′) r(t)=freq(t)/\sum_{t'∈V}freq(t')
  1. 计算移除每个 token 后训练集上的损失如下。
∑σ∈C−log⁡(rV∖{t}(σ)) \sum_{\sigma∈C}-\log(r_{V\setminus\{t\}}(\sigma))
  1. 移除使损失最小的 batch(通常为词表大小的 10%–20%)。
  2. 重复直到词表达到目标大小。

推理时,Unigram 使用 Viterbi 算法在最终词表下寻找概率最高的分词:

enc(σ)=arg⁡max⁡s∈V∗,dec(s)=σr(s) \text{enc}(\sigma)=\arg\max_{\mathbf{s}∈V^*,\text{dec}(\mathbf{s})=\sigma}r(\mathbf{s})

BPE 与 Unigram 的实证对比。在受控实验中,Unigram 在词表大小为 32K 时略逊于 BPE,但在更大词表下 token-to-word 比显著恶化。BPE 的跨语言压缩率分布更紧凑,而 Unigram 的 SentencePiece 实现在测试的 tokenizer 类型中压缩率最差。不过,Unigram 的一个优势是同一字符串可以有多种分词,便于在数据增强或采样场景中产生多样性。

#3. Tokenizer 训练数据混合与跨语言公平性

#3.1 训练数据配比对词表的影响

Tokenizer 训练语料中语言和领域的混合比例,直接决定了词表的分配。对 BPE 训练数据的逆向推断分析显示:

  • GPT-4o 的 tokenizer 比其前代更多语言化,训练数据中约 39% 为非英语,覆盖至少 0.1% 数据量的语言有 68 种。
  • Mistral NeMo 的 tokenizer 训练数据中约 47% 为非英语。
  • LLaMA 3 的 tokenizer 主要为多语言扩展(约 48% 非英语)。
  • Claude 的 tokenizer 的逆向推断结果为约 57.5% 代码、38.8% 英语、3.7% 其他语言。

这些数字说明,主流模型的 tokenizer 训练数据配比差异巨大,且代码在 tokenizer 训练中的权重可能远高于其在预训练混合中的权重。

#3.2 频率目标的跨语言不平等

标准 BPE 和 Unigram 依赖频率目标——在整个训练语料上最大化压缩。在多语言语料中,这一全局准则不可避免地偏向代表性最大的语言。在词表大小约束下,主要惠及高资源语言的子词被优先纳入,而低资源语言所需的子词被牺牲。

后果包括:

  • 过度切分:低资源语言的文本被切分成不成比例更长的 token 序列,形态学上不合理的切分。
  • 计算成本不平等:按 token 计费的服务对低资源语言用户收取更高费用。
  • 模型性能下降:碎片化或语义不连贯的分词使模型丢失有价值的归纳偏置。

#3.3 Parity-aware BPE

Parity-aware BPE 通过修改合并选择规则解决这一问题。经典 BPE 选择语料级共现频率最高的子词对;Parity-aware BPE 在每一步分别计算每种语言的共现统计量,然后使用当前压缩率最差的语言的统计量来选择下一步合并。

这一“fair-max”更新策略逐步均衡各语言的字符串压缩率。实证结果表明,Parity-aware BPE 将跨语言 token 成本不平等(以 Gini 系数衡量)降低最高 89%,同时对全局压缩率的影响可忽略,对下游语言模型性能无实质影响。

#3.4 公平性评估指标

指标 定义 优势 局限
Fertility 每词 token 数 直观、常用 对 CJK 语言有盲区
压缩率(CR) UTF-8 字节/token 字节级客观 不反映语义密度
字符/token 比(CPT) 字符数/token 对 CJK 敏感 跨语言不可比
STRR 保持为单 token 的词比例 揭示词表分配策略 需词边界信息
Gini 系数 各语言 token 成本的基尼系数 单一数字概括公平性 不区分方向性偏差

STRR 揭示了系统性的英语优先、中文强支持、印地语碎片化的模式。TokLens 工具包提供了 fertility、CPT、压缩率、归一化序列长度、STRR 和跨语言 parity 六项内在指标的完整评估。

#4. 特殊 token 与 Chat Template

#4.1 特殊 token 的设计维度

特殊 token 是词表中保留的 ID,用于编码控制信息而非普通文本内容。其设计维度包括:

  • 消息起止与角色边界:如 <|im_start|>、<|im_end|>、[USER]、[ASST]。
  • 工具调用与返回:结构化工具调用的请求和响应标记。
  • 多模态占位符:图像 token、音频 token 的起止标记。
  • 文档边界:Packing 中用于分隔不同样本的边界 token。

特殊 token 不应仅用普通文本拼写模拟(如用字符串 <|im_start|> 而非专用 token ID),否则用户输入可能与控制边界混淆,导致注入攻击或角色混淆。

#4.2 Chat template 是输入协议

Chat template 将消息列表(角色+内容)转换为模型训练时使用的单一字符串格式。不同模型采用截然不同的格式:

  • 即时消息格式:User: Hello! Bot: Hi!
  • 角色标记格式:[USER] Hello! [/USER] [ASST] Hi! [/ASST]
  • 结构化边界格式:<|im_start|>user Hello!<|im_end|>

如果微调或推理时使用的格式与训练时不同,会导致严重的、无声的性能下降。模型不会抛出异常,只会表现变差,且很难调试原因。这一现象被称为分布漂移——模型从一种输入分布学习,突然面对不同的分布。

损失掩码与角色错位的相互作用。如果 Chat template 将助手回复的结束标记(如 </s>)归属于下一条消息而非助手回合,损失掩码可能漏掉结束标记的训练信号。更严重的是角色交换错误:如果用户和助手的角色标记被互换,交叉熵损失会训练模型复现用户的句子作为回答,而真正的回答 token 失去目标状态。

#4.3 模板一致性的工程保障

Hugging Face 的 chat_template 属性以 Jinja 模板字符串的形式保存格式信息,可直接从 checkpoint 加载和复用。推荐做法包括:

  • 从同一 checkpoint 加载 tokenizer 和模型,避免分布漂移。
  • 在 CI 中检查 Chat template 是否将停止 token 保留在损失掩码中。
  • 固定 system/user/assistant 的顺序、分隔符和停止 token,不跨数据源漂移,否则模型学到的是“格式噪声”而非任务。

#5. Packing 的数学边界与工程实现

#5.1 注意力掩码的约束

若窗口内拼入 KK 条样本,样本 jj 的位置集合为 IjI_j,独立样本训练要求注意力掩码满足:

Aab=1⟺a,b∈Ij 对某个 j 且 b≤a. A_{ab}=1\quad\Longleftrightarrow\quad a,b\in I_j\text{ 对某个 }j\text{ 且 }b\le a.

即 token aa 只能看本样本的历史。否则 Ij+1I_{j+1} 会把 IjI_j 的末尾当作虚假的前文。

#5.2 两种 Packing 策略

Wrapped packing(遗留策略) :简单地将多条样本首尾相接,注意力掩码仍为标准因果下三角。问题在于样本之间可以互相注意——后一样本可以看到前一样本的内容,产生交叉污染。

BFD packing(当前默认) :TRL 的 packing_strategy="bfd" 自动启用 padding-free 模式,通过传递分割打包序列的 position_ids,使 FlashAttention-2 仅在每个样本内部计算注意力。这样既保持了 FlashAttention-2 的加速效果,又消除了交叉污染。BFD 使用最佳适应递减算法将样本装入固定长度窗口,最大化 packing 率。

#5.3 损失掩码与注意力掩码的独立性

损失掩码 mtm_t 与注意力掩码 AA 分别控制哪些位置产生损失与哪些位置可被看见,不能互相替代。SFT 中用户 token 不计损失,不代表助手不能看用户 token。同样,Packing 中样本间不能互相注意,不代表所有样本的损失都要被屏蔽。

#5.4 长文档的 Packing 策略

对于长文档,Packing 的挑战从“装更多短样本”变为“如何切分与拼接”。常见策略包括:

  • 文档级 packing:整篇文档作为一个样本,若超过窗口长度则截断或分段。截断会丢失文档末尾信息。
  • 分段 packing:将长文档按语义边界切分为多个片段,分别与其他样本打包。需要保证片段之间的注意力隔离。
  • 滑动窗口:对超长文档使用滑动窗口分段,相邻段之间有重叠。重叠部分在前向计算中被重复处理,增加计算成本但保留上下文连续性。

#6. 多模态 Tokenizer

#6.1 为什么需要多模态 tokenizer

多模态模型需要将图像、音频、视频等连续信号转换为离散 token 序列,以便与文本共享同一个自回归目标。这一过程面临两个根本性挑战:表示容量(有限码本能否保留足够信息)和离散化信息损失。

#6.2 统一离散空间的进展

LongCat-Next / DiNA 框架提出了将多模态信息表示在共享离散空间中的统一方法。其核心创新是 dNaViT(Discrete Native Any-resolution Visual Transformer) ,在任意分辨率下执行 tokenization 和 de-tokenization,将连续视觉信号转换为层次化离散 token。所有模态在单一自回归目标下处理,最小化模态特定的设计。

视觉 tokenization:将图像划分为 patch,每个 patch 通过视觉编码器(如 ViT)提取特征,再通过量化器(VQ-VAE 或残差量化)映射为离散 token。图像 token 序列以特殊起止 token 包裹,如 <|v_start|> 和 <|v_end|>,内部由 <|img_pad|> 占位。

音频 tokenization:音频可通过两种方式离散化——基于梅尔频谱帧的 token 或基于波形容量的 token。离散音频 token 不仅捕获文本对齐的语义,还包含情感、语调、环境上下文等副语言信息。

掩码 tokenization:SAMTok 将任意区域掩码转换为两个文本特殊 token,并通过这些 token 高保真重建掩码。这使得基础多模态大模型(如 QwenVL 系列)能够通过标准下一 token 预测学习像素级能力。

#6.3 多模态 token 的成本

多模态输入中,图像和音频 token 的表示和计数依模型而变,不能把所有 token 成本等同于普通文本 token。一张 224×224 的图像在 patch size 为 16 时产生 196 个视觉 token,加上起止标记共约 200 个 token。一段 10 秒的音频可能产生数百个音频 token。这些 token 在注意力计算和 KV 缓存中与文本 token 同等对待,显著增加了上下文长度和推理成本。

#7. 何时需要重训 tokenizer

现象 可能原因 优先检查
特定语言 token/字数异常高 原词表该语言覆盖少 语种分层压缩率、Gini 系数
代码符号被过度拆分 代码语料不足 代码任务 token 长度与错误率
术语切碎 专业词频低 领域样本统计、STRR
新增 token 后性能下降 新嵌入未充分训练、旧 token 分布变化 新旧模板与嵌入初始化
Chat template 不一致 训练/推理模板漂移 角色标记、停止 token、损失掩码

重训 tokenizer 会改变词表 ID、嵌入矩阵和已有权重接口;在继续预训练时往往优先评估现有分词器是否够用,再决定是否引入额外迁移成本。若决定重训,Parity-aware BPE 可作为多语言模型的责任默认选项,在保持全局压缩率的同时将跨语言不平等降低最多 89%。

#原始资料

本页由仓库中的 Markdown 生成。具体技术结论请结合正文引用与实验条件理解。

输入关键词,探索整个知识库

↑ ↓ 选择 ↵ 打开36 篇笔记,一次搜索