4. 数据与 Tokenizer
- 解释 BPE 合并过程
- 测量词表对序列长度的影响
- 正确处理特殊 token、chat template 与 loss mask
- 字符串与字节基础
- 完成环境自检
本章产物三组 BPE 对照结果、往返测试和 tokenizer 配置。
4.1 数据比“多”更复杂
预训练数据处理通常包含:
采集 → 许可证检查 → 格式解析 → 语言识别
→ 文档/段落去重 → 质量过滤 → 隐私与敏感信息处理
→ 安全过滤 → 数据混合配比 → tokenizer → packing常见风险:
- 测试集进入训练集,造成 benchmark 污染。
- 同一网页的多个镜像被重复训练。
- 自动生成垃圾文本比例过高。
- 个人信息、密钥、私有代码或受限版权内容进入语料。
- 某种语言或领域占比失衡。
模型会学习数据中的规律,也会学习其中的偏见、错误和格式噪声。
4.2 为什么不能直接按“词”切分
纯词级 tokenizer:
- 词表极大。
- 新词、拼写变化、代码符号难处理。
纯字符 tokenizer:
- 词表小。
- 序列很长,计算成本高。
子词 tokenizer 在两者之间折中。常见方法:
- BPE:反复合并最常见的相邻单元。
- WordPiece:选择能较好提升语言模型似然的合并。
- Unigram:从大候选词表中逐步删除贡献较小的 token。
- Byte-level:从字节出发,理论上可以表示任意文本。
4.3 BPE 的核心思想
假设语料初始被拆成最小单位,统计相邻对:
l o w
l o w e r
n e w e s t如果 l + o 最常见,就合并为 lo;下一轮可能把 lo + w 合并为 low。反复进行,直到达到目标词表大小。
词表越大:
- 同一文本 token 数通常更少。
- embedding 和输出分类头更大。
- 稀有 token 学习样本可能更少。
词表越小则相反。Tokenizer 是模型设计的一部分,不只是预处理工具。
4.4 特殊 token 和聊天模板
常见特殊 token:
<bos> 序列开始
<eos> 序列结束
<pad> 批次补齐
<unk> 未知单元聊天模型还需要把角色转为训练时一致的文本格式。messages 本身不是模型输入,chat template 会把它们序列化。例如概念上:
<system>你是一位老师</system>
<user>什么是注意力?</user>
<assistant>模板不匹配会导致:
- 模型误解角色边界。
- 无法正确停止。
- 多轮历史混乱。
- 明明模型正常,实际效果却很差。
4.5 亲手训练 BPE
python code/03_train_bpe.py \
--corpus data/tiny_corpus.txt \
--vocab-size 512实验:
- 分别使用词表 128、512、2000。
- 记录同一段中文和代码的 token 数。
- 检查罕见汉字、Emoji、英文和空格能否往返解码。
- 观察词表增大后,常用短语是否合并成更长 token。
严肃模型训练 tokenizer 时,训练 tokenizer 的样本分布要尽量代表最终语料,但 tokenizer 训练数据本身也需要合规和去重。
4.6 Packing、Padding 和 Attention Mask
短样本分别 pad 到固定长度会浪费计算。Packing 把多个短样本连接进同一序列:
[样本 A][EOS][样本 B][EOS][样本 C][EOS]但必须明确:
- 样本之间是否允许相互注意。
- loss 是否包括 prompt、padding 和特殊 token。
- position IDs 是否连续。
- EOS 是否正确插入。
SFT 时经常只希望 assistant 回答参与 loss,user/system token 只提供上下文。这通过把不计 loss 的 labels 设为 -100 实现;PyTorch 交叉熵会忽略这些位置。
4.7 动手:建立 Tokenizer 对照实验
实验 02|Tokenizer 资源:CPU;时间:约 2 分钟;变量:词表大小;产物:token 数与往返解码对照表。
先跑独立脚本,确认最短链路:
python code/03_train_bpe.py --corpus data/tiny_corpus.txt --vocab-size 128
python code/03_train_bpe.py --corpus data/tiny_corpus.txt --vocab-size 512再打开Tokenizer 对照 Notebook。Notebook 会在相同语料和预切分设置下训练 128、256、512 三组 BPE,并输出:
requested=<请求词表> actual=<实际词表> tokens=<样本文本 token 数> decoded=<解码文本>结果表至少包含三类输入:
| 输入类型 | 示例 | 需要检查的风险 |
|---|---|---|
| 中文技术文本 | 注意力会复用 KV Cache | 中文切分过碎、英文缩写边界 |
| 代码 | for i in range(8): | 空格、缩进、标点和标识符 |
| 边界字符 | Emoji、罕见字、组合字符 | [UNK]、字节回退、不可逆解码 |
不要只写“词表越大 token 越少”。小语料可能达不到请求的词表大小;规范化和 pre-tokenizer 也可能主导结果。报告中必须同时记录实际词表、token 数、解码结果和 tokenizer 配置。
验收标准:
- 任意支持字符满足
decode(encode(text))的预期往返规则。 - 能解释词表增大对序列长度、embedding 参数量和稀有 token 样本数的不同影响。
- 能指出模型权重与 tokenizer 不匹配时,为什么结果不是“稍差一点”,而是输入语义被整体破坏。
本章依据
原理性结论以原始论文、官方文档或公开教材为依据。论文中的实验结果只适用于其声明的模型、数据、硬件和评估设置。
将 BPE 用于开放词表神经机器翻译的经典工作。
直接从原始文本训练、语言无关的子词切分方法。
Tokenizer 训练、规范化、预切分和解码实现。