BPE 分词器从零:字节进、id 出 本节摘要:字节进、id 出、id 还原回同样字节——构建每个现代文本模型至今仍从它起步的分词器。语言模型从不见文本,只见整数。字符串到整数列表再回来的映射就是分词器;这层错了,训练的每条损失曲线量的都是错的东西。本节手搓字节级 Byte-Pair Encoding:从 256 个原始字节的已知字母表出发,反复合并训练语料里最频繁的相邻符号对,直到词表达目标大小;新文本按学到的合并表同序应用。你会学到合并表的确定性、special token 的保留、字节字母表为何是通用分词器的正确地基,以及往返无损保证。 对应原课程:Phase 19 · Lesson 30 · (原英文 )。本节属「从零构建 GPT」赛道第一节。
本节摘要:字节进、id 出、id 还原回同样字节——构建每个现代文本模型至今仍从它起步的分词器。语言模型从不见文本,只见整数。字符串到整数列表再回来的映射就是分词器;这层错了,训练的每条损失曲线量的都是错的东西。本节手搓字节级 Byte-Pair Encoding:从 256 个原始字节的已知字母表出发,反复合并训练语料里最频繁的相邻符号对,直到词表达目标大小;新文本按学到的合并表同序应用。你会学到合并表的确定性、special token 的保留、字节字母表为何是通用分词器的正确地基,以及往返无损保证。
对应原课程:Phase 19 · Lesson 30 ·
bpe-tokenizer-from-scratch(原英文phases/19-capstone-projects/30-bpe-tokenizer-from-scratch/docs/en.md)。本节属「从零构建 GPT」赛道第一节。
阅读完本节,你应当能够:
<|endoftext|>、<|pad|>),使其在训练与解码中存活。通用文本模型的主牌子词分词器家族是 Byte-Pair Encoding。想法很小:从已知字母表出发,找训练语料里出现最多的相邻符号对,合并成新符号,重复到词表达目标大小。编码新文本复用同一合并表、同序。
我们建字节级变体——字母表是 256 个原始字节,而非 Unicode 码点。这个选择让分词器处理任何 UTF-8 输入而无需回退到 unknown token。
前 256 个 id 留给原始字节 0x00~0xFF,保证任何输入字符串在任何合并发生前都能用词表表达。字节块后留一小段给 special token,训练循环从不把这些 id 提为合并目标——我们把它们完全排除在预分词流之外。
预分词器在训练看到语料前,按空白与标点边界切。不切,BPE 合并步会愉快地学跨词边界的合并,词表填满整个常见短语;切了,合并在词内进行,结果能泛化。
训练循环(每步三件事):
def train_step(self): pair_counts = count_adjacent_pairs(self.words) # 按词频加权 best = max(pair_counts, key=pair_counts.get) # 最频繁对 (a, b) new_id = self.next_id(); self.merge_table[best] = new_id self.words = [rewrite(w, best, new_id) for w in self.words] # 每处 (a,b) -> new_id
编码新文本不调合并计数器,而是按学到的序应用合并表。对一个新词,编码器从字节切分起,扫当前序列找最低秩(最早学的)可应用合并,执行,再扫,循环到表中无合并可应用。按秩排序让编码确定且在同输入上匹配训练行为——先学的合并坐表顶、先应用;若两合并能在同位应用,低秩者赢。
special token 是字节流永不产出的 id,我们手工保留。两个够本节用:<|endoftext|>(预训练时分文档,告诉模型「新文档开始,别让上一个的上下文泄漏」)、<|pad|>(填短序列使批成矩形张量,训练时损失掩码隐藏它)。编码器接受一个标志允许输入里的 special token;标志关,<|endoftext|> 串被分词成拼出它的字节;标志开,字面串映到保留 id 且不经任何合并。
往返保证:编码再解码必须精确返回输入字节。解码器按序拼接每个 id 的字节展开。因为每个 id 要么是原始字节、要么是两个已知 id 的拼接,递归展开总终止于原始字节,解码返回这些字节拼出的 UTF-8 串。测试套件在未见句子、带 Unicode emoji 的句子、含字面 <|endoftext|> 的句子上检查此性质。
main.py 定义四个对象:BPETokenizer(持词表、合并表、special token 表)、train(训练循环)、encode(推理路径)、decode(字节拼接)。底部 demo 在内置语料上训小分词器、编码留出句、解码回、打印两者。code/tests/test_bpe.py 钉死往返性质、special token 保留、合并排序。把 demo 词表从 300 调到 600,看留出句编码长度下降——这条曲线就是 BPE 压缩曲线。
本节的字节级 BPE 与 GPT-2/GPT-4 的 tiktoken、Llama 的 sentencepiece 同源。差异在预分词器:生产级用正则驱动(GPT-2 的 's|'t|... 模式),本节用简单的空白标点切,够在小语料上产合理合并,与链其余部分的契约不变。字节级(非 Unicode 码点)是关键选择——它保证任何 UTF-8 输入可表达,无 unknown token 回退。下一个赛道节把分词器当黑盒,在其上建滑动窗口数据集。
<|endoftext|> 的输入,对比标志关(分词成字节)与开(映保留 id)的 id 流。下一节,我们用这个分词器建「滑动窗口数据集」——把语料切成下一 token 预测的训练例。