BPE Tokenizer 完整入门:从汉字编码到 GPT-2 风格 Byte-Level BPE
BPE Tokenizer 完整入门从汉字编码到 GPT-2 风格 Byte-Level BPE主题汉字如何进入 GPT-2 风格 tokenizerBPE 的统计算法是什么完整算例是什么还有 BPE、WordPiece、Unigram、SentencePiece 等算法的区别以及主流大模型是否还在使用 BPE。0. 一句话总览大模型不能直接吃人类文字它只能处理整数 token id。所以文本进入大模型前要经过原始文本 ↓ 字符编码例如 Unicode / UTF-8 ↓ tokenizer 切分 ↓ token id 序列 ↓ embedding 向量 ↓ TransformerBPE tokenizer 的本质是用统计方法找出训练语料中最常一起出现的相邻片段把它们合并成更大的 token从而压缩文本长度。GPT-2 风格 BPE 的特殊点是它不是直接从 Unicode 字符开始而是先把文本转成 UTF-8 字节然后对字节序列做 BPE 合并。1. 汉字在计算机里先怎么表示1.1 Unicode给每个字符一个编号例如中 → Unicode 码点 U4E2D 国 → Unicode 码点 U56FD 人 → Unicode 码点 U4EBAUnicode 负责回答这个字符在全世界字符表里的编号是多少但 Unicode 码点还不是计算机真正存储的字节。1.2 UTF-8把 Unicode 码点变成字节计算机最终存的是字节即 8-bit 数据。例如中 → U4E2D → UTF-8: E4 B8 AD 国 → U56FD → UTF-8: E5 9B BD所以中国 → E4 B8 AD E5 9B BD大部分常用汉字在 UTF-8 里是 3 个字节。字符UnicodeUTF-8 字节字节数AU0041411中U4E2DE4 B8 AD3国U56FDE5 9B BD3U1F602F0 9F 98 8242. GPT-2 风格 BPE 如何处理汉字GPT-2 风格 tokenizer 的链路是汉字文本 ↓ UTF-8 bytes ↓ byte-to-unicode 映射 ↓ BPE merge 合并 ↓ token 字符串 ↓ vocab 查询 ↓ token id重点GPT-2 风格 BPE 看到的不是“中”这个汉字本身而是“中”的 UTF-8 字节序列。2.1 以“中”为例中 ↓ Unicode U4E2D ↓ UTF-8 E4 B8 AD十进制就是E4 B8 AD 228 184 173GPT-2 tokenizer 会把每个 byte 映射成一个可打印的 Unicode 字符。大致可以理解为0xE4 → ä 0xB8 → ¸ 0xAD → ľ于是中 → E4 B8 AD → ä ¸ ľ → 举注意举不是乱码而是 GPT-2 byte-level tokenizer 的内部 byte 表示。2.2 以“中国”为例中 → E4 B8 AD 国 → E5 9B BD所以中国 → E4 B8 AD E5 9B BD经过 GPT-2 的 byte-to-unicode 映射大致变成中国 → ä¸ľåĽ½然后 BPE 会根据 merge 表决定是否合并ä ¸ → ä¸ ä¸ ľ → 举 # 可以表示“中” å Ľ → åĽ åĽ ½ → åĽ½ # 可以表示“国” 举 åĽ½ → ä¸ľåĽ½ # 可以表示“中国”如果训练语料里“中国”非常常见最终可能合并成一个 token。如果没有学到整个“中国”可能拆成中国 → 中 / 国如果中文语料很少甚至可能拆得更碎中国 → E4 / B8 / AD / E5 / 9B / BD3. tokenizer 的“学习”是不是统计是的。BPE tokenizer 的学习主要是统计不是语义理解。它学的不是“中国”是什么意思 “苹果”是水果还是公司 “Transformer”是什么神经网络它学的是哪些相邻符号经常一起出现 哪些片段值得合并成一个 token 怎样用有限词表压缩训练语料所以 BPE tokenizer 更像一个统计压缩器。4. BPE 算法的核心原理BPE 全称 Byte Pair Encoding字节对编码。它最早是数据压缩算法后来被用作 NLP 的子词切分算法。核心规则非常简单1. 把文本拆成最小符号 2. 统计所有相邻符号 pair 的出现次数 3. 找到出现次数最高的 pair 4. 把这个 pair 合并成一个新符号 5. 更新语料 6. 重复直到词表大小达到目标一句话哪两个相邻片段最常一起出现就先把它们合并。5. BPE 的完整小白算例为了让小白容易理解先用英文字符演示。假设训练语料是low lower lowest newer wider我们先把每个词拆成字符low → l o w /w lower → l o w e r /w lowest → l o w e s t /w newer → n e w e r /w wider → w i d e r /w其中/w表示词尾。为什么要有词尾因为要区分low和lower 里面的 low词尾可以帮助 tokenizer 学到“某个片段在词尾出现”。5.1 第 1 轮统计相邻 pair对每个词统计相邻符号。例如low → l o w /w里面有(l, o) (o, w) (w, /w)lower → l o w e r /w里面有(l, o) (o, w) (w, e) (e, r) (r, /w)统计整个语料假设得到pair次数l o3o w3e r3r /w3w e2其他1假设我们选择第一个最高频 pairl o → lo5.2 第 1 轮合并后的语料low → lo w /w lower → lo w e r /w lowest → lo w e s t /w newer → n e w e r /w wider → w i d e r /w词表新增lo5.3 第 2 轮重新统计 pair现在重新统计可能最高频是lo w → low合并后low → low /w lower → low e r /w lowest → low e s t /w newer → n e w e r /w wider → w i d e r /w词表新增low5.4 第 3 轮继续统计可能最高频是e r → er合并后low → low /w lower → low er /w lowest → low e s t /w newer → n e w er /w wider → w i d er /w词表新增er5.5 继续重复BPE 会一直重复统计 pair → 找最高频 → 合并 → 更新语料直到达到目标词表大小。最后词表里可能有l o w e r lo low er lower new wide ...6. BPE 的数学表达设训练语料中每个序列是sᵢ [x₁, x₂, ..., xₘ]每一轮统计所有相邻 paircount(a, b) Σᵢ Σⱼ 1[(xⱼ, xⱼ₊₁) (a, b)]选择出现次数最多的 pair(a*, b*) argmax count(a, b)然后定义新符号c a* b*把所有a* b*替换成c这就是 BPE 的核心。7. BPE 训练伪代码deftrain_bpe(corpus,vocab_size):# 1. 初始切分字符级或 byte 级corpus_symbolssplit_to_initial_symbols(corpus)# 2. 初始词表vocabset(all_symbols(corpus_symbols))# 3. 合并规则merges[]whilelen(vocab)vocab_size:pair_counts{}# 4. 统计所有相邻 pairforseqincorpus_symbols:fora,binzip(seq,seq[1:]):pair_counts[(a,b)]pair_counts.get((a,b),0)1# 5. 找最高频 pairbest_pairmax(pair_counts,keypair_counts.get)# 6. 合并new_symbolbest_pair[0]best_pair[1]merges.append(best_pair)vocab.add(new_symbol)# 7. 更新语料corpus_symbolsmerge_pair(corpus_symbols,best_pair,new_symbol)returnvocab,merges核心就三句话统计 pair 选最高频 pair 合并 pair8. GPT-2 风格 Byte-Level BPE 的特殊流程传统 BPE 可以从字符开始。GPT-2 风格 BPE 从 byte 开始。完整流程是原始文本 ↓ 正则预切分 ↓ UTF-8 bytes ↓ byte-to-unicode 映射 ↓ BPE pair 统计与合并 ↓ vocab.json merges.txt8.1 为什么要从 byte 开始因为任何文本都能变成 UTF-8 bytes。所以 byte-level BPE 有一个巨大优点不会真正遇到 OOV也就是不会出现“这个字符完全无法编码”。中文、日文、韩文、emoji、生僻字、特殊符号、乱码都能编码。8.2 GPT-2 风格 BPE 的两个文件训练完成后通常得到两个核心文件vocab.json merges.txt其中vocab.jsontoken 字符串 → token id merges.txtBPE 合并规则和优先级推理时不再统计而是直接用merges.txt里的合并顺序。9. GPT-2 风格 BPE 推理时如何编码训练阶段统计语料 → 学 vocab 和 merges推理阶段输入文本 → 按已有 merges 合并 → 查 vocab → token id假设 merge rank 是(ä, ¸) rank 100 (ä¸, ľ) rank 101 (å, Ľ) rank 200 (åĽ, ½) rank 201 (举, åĽ½) rank 5000输入中国先变成ä ¸ ľ å Ľ ½按 merge rank 合并ä ¸ ľ å Ľ ½ → ä¸ ľ å Ľ ½ → 举 å Ľ ½ → 举 åĽ ½ → 举 åĽ½ → ä¸ľåĽ½最后查词表ä¸ľåĽ½ → token_id如果词表里没有“中国”这个整体 token就会停在更小的粒度举 / åĽ½也就是中 / 国10. 中文在 BPE 中为什么可能被切碎原因不是中文本身不能被 BPE 表示而是训练语料决定合并规则。如果训练语料主要是英文那么英文片段会被大量合并transform attention function return中文片段出现较少就不一定能合并成较大的 token。于是中文可能更碎我正在学习Transformer可能被切成我 / 正 / 在 / 学 / 习 / Transformer也可能更碎到 byte 级。因此一个 tokenizer 对中文是否友好取决于1. 中文语料占比 2. 词表大小 3. 是否 byte-level 或 byte fallback 4. 是否专门优化中日韩文本 5. 是否优化代码、数学、Markdown 等结构文本11. BPE 的优点和缺点11.1 优点优点解释简单算法就是统计 pair 贪心合并快训练和推理都容易优化稳定编码确定性强可逆byte-level BPE 可以 lossless decode无 OOVbyte-level BPE 可以表示任意文本压缩有效高频片段会变成短 token 序列11.2 缺点缺点解释贪心每一步只看局部最高频 pair不保证全局最优不懂语义合并依据是频率不是语义对语料敏感训练语料偏英文中文就可能碎词表浪费高频但无语义价值的片段也可能进词表多语言公平性问题不同语言 token 压缩率可能差别很大12. 有没有比 BPE 更好的算法有。主流替代方案包括WordPiece Unigram Language Model SentencePiece byte fallback / hybrid tokenizer 直接 byte-level language model13. WordPiece比 BPE 更关注“组合价值”WordPiece 是 BERT 系列常用的 tokenizer。BPE 主要看哪个 pair 出现次数最多WordPiece 更关心合并这个 pair 后是否能更好解释语料一种直观分数可以写成score(a, b) freq(a, b) / (freq(a) × freq(b))这个分数表达的是a 和 b 是不是强绑定还是只是因为 a、b 各自都太常见所以碰巧一起出现很多例如New York如果New和York经常强绑定那么 WordPiece 倾向于把它们看作更有价值的组合。BPE 更像出现次数多就合并WordPiece 更像绑定关系强、对建模有收益才合并14. Unigram LM更像真正的概率模型Unigram Language Model 是 SentencePiece 中常用的一类算法。它和 BPE 的方向相反。BPE 是从小词表开始不断合并词表变大Unigram LM 是先准备一个很大的候选词表然后删除贡献小的 token词表变小14.1 Unigram LM 如何理解比如人工智能可以有很多切法人工 / 智能 人 / 工 / 智 / 能 人工智能 人工 / 智 / 能Unigram LM 给每个 token 一个概率P(人工) P(智能) P(人工智能) P(人) P(工) ...一句话的概率是所有切分路径概率的总和或最优路径概率近似。训练目标是让整个训练语料的概率最大然后删除那些对语料概率贡献小的 token。14.2 Unigram LM 的优点优点解释概率化比 BPE 的贪心频率更接近统计建模多切分路径可以考虑一句话的多种切法适合多语言尤其适合中文、日文这种无空格语言可用于采样可以做 subword regularization增强模型鲁棒性缺点是训练更复杂 推理实现更复杂 工程速度可能不如 BPE 简单直接15. SentencePiece不是单一算法而是 tokenizer 框架SentencePiece 是一个语言无关的 tokenizer / detokenizer 框架。它支持BPE Unigram LM char word它的重要特点是可以直接从原始文本训练不要求先按空格分词。这对中文、日文、韩文很重要因为这些语言不像英文那样天然用空格分词。SentencePiece 通常用▁表示空格。例如Hello world可以内部表示成▁Hello ▁world这样空格也成为 tokenizer 学习的一部分。16. BPE、WordPiece、Unigram、SentencePiece 对比方法核心思想训练方向优点缺点典型模型BPE高频相邻 pair 合并小词表 → 大词表简单、快、稳定贪心、不懂语义GPT-2、RoBERTa、很多 GPT 类模型Byte-Level BPE从 UTF-8 byte 开始做 BPEbyte → subword无 OOV、可逆、工程强某些语言可能碎GPT-2、OpenAI tiktoken、Qwen、DeepSeek LLMWordPiece更关注合并带来的建模收益小词表 → 大词表比 BPE 更概率化实现复杂BERT、DistilBERT、MobileBERTUnigram LM概率模型选择 token大词表 → 小词表理论优雅、多切分路径训练复杂T5/ALBERT 等 SentencePiece 系模型SentencePiecetokenizer 框架支持 BPE/Unigram语言无关适合中日韩本身不是单一算法LLaMA 1/2、T5、Gemma 等许多模型17. 主流大模型是不是还在用 BPE结论是的BPE 或 BPE 变体仍然是主流大模型 tokenizer 的核心路线之一但不是唯一路线。常见情况如下。17.1 GPT-2 / RoBERTa / BART / DeBERTa这些模型使用 BPE 或 byte-level BPE 类 tokenizer。GPT-2 使用 byte-level BPE词表大小约 50,257256 个 byte 基础 token 约 50,000 个 BPE merge token 特殊 token17.2 OpenAI tiktoken 系列OpenAI 的tiktoken是快速 BPE tokenizer用于 OpenAI 模型。它的核心仍是 BPE文本 → bytes → BPE token ids它强调可逆 无损 可以处理任意文本 压缩文本长度17.3 LLaMA 系列LLaMA 1 / LLaMA 2 主要使用 SentencePiece tokenizer。LLaMA 3 之后切换到更大的 tokenizer词表约 128K并更重视多语言和压缩效率。LLaMA 3 的 tokenizer 通常被描述为 tiktoken/BPE 风格Meta 官方也强调 LLaMA 3 使用 128K 词表来更高效编码语言。17.4 Qwen 系列Qwen 系列使用 BPE / tiktoken 风格 tokenizer。Qwen-7B 的 tokenizer 说明中明确提到BPE tokenization on UTF-8 bytes using tiktoken也就是说Qwen 是典型的 byte-level BPE 路线。17.5 DeepSeek 系列DeepSeek LLM 官方说明中提到使用 Hugging Face Tokenizer 实现 Byte-level BPE并使用专门设计的 pre-tokenizer。所以 DeepSeek LLM / DeepSeek-R1 一类模型也属于 byte-level BPE 路线或其变体。17.6 BERT 系列BERT 使用 WordPiece。它不是 BPE但和 BPE 一样属于子词 tokenizer。BERT 的 WordPiece 推理阶段常用贪心最长匹配unaffable → un / ##aff / ##able17.7 T5 / ALBERT / 一些多语言模型这些模型常用 SentencePiece / Unigram LM。SentencePiece 对中日韩、多语言、无空格文本比较友好。18. 对中文大模型来说tokenizer 设计为什么重要因为 token 数直接影响训练成本 推理成本 上下文长度利用率 显存占用 attention 计算量Transformer 注意力复杂度近似是O(n²)其中n是 token 数。如果中文一句话被切得很碎token 数变多那么上下文窗口被浪费 推理变慢 训练成本变高 长文本能力下降所以中文友好的 tokenizer 会尽量让常见中文词、短语、标点组合、换行结构、代码符号成为更高效的 token。19. 从工程角度如何判断一个 tokenizer 好不好一个好的 tokenizer 应该满足指标解释压缩率高同样文本 token 数少无 OOV任意字符都能编码可逆decode(encode(text)) text多语言公平中文、英文、代码都不要太浪费代码友好对缩进、换行、括号、关键字优化数学/Markdown 友好对公式、符号、表格结构友好编码速度快推理服务中 tokenizer 不能成为瓶颈词表大小合适太小会碎太大会增加 embedding 参数20. BPE 为什么还没被淘汰因为 BPE 虽然不是理论最优但工程优势非常强简单 确定 快 容易实现 容易并行 容易部署 可逆性好 byte-level 后没有 OOV所以今天很多大模型仍然使用 BPE 或 BPE 变体。它就像工程中的“足够好且足够稳定”的方案。21. 最终总结21.1 汉字进入 GPT-2 风格 BPE 的过程中 ↓ Unicode U4E2D ↓ UTF-8 E4 B8 AD ↓ byte-to-unicode ä ¸ ľ ↓ BPE merge 举 ↓ vocab 某个 token id21.2 BPE 的核心算法1. 拆成最小符号 2. 统计所有相邻 pair 3. 找出现次数最多的 pair 4. 合并成新 token 5. 更新语料 6. 重复直到词表达到目标大小公式(a*, b*) argmax count(a, b)然后a* b* → a*b*21.3 BPE 的本质统计压缩不是语义理解它学到的是哪些片段经常一起出现值得用一个 token 表示不是这些片段到底是什么意思21.4 更高级的 tokenizer 算法BPE最高频 pair 合并简单稳定 WordPiece更关注合并对建模概率的收益 Unigram LM概率模型从大候选词表中删除低贡献 token SentencePiece语言无关 tokenizer 框架支持 BPE 和 Unigram Byte-level BPE从 UTF-8 byte 开始无 OOV可逆21.5 主流大模型是否使用 BPE答案是大量主流大模型仍然使用 BPE 或 BPE 变体。包括GPT-2byte-level BPE OpenAI tiktoken 系BPE QwenUTF-8 byte-level BPE / tiktoken 风格 DeepSeek LLMByte-level BPE RoBERTa/BART/DeBERTaBPE 类 LLaMA 3大词表 tiktoken/BPE 风格但也有很多模型使用其他路线BERTWordPiece T5/ALBERT/部分多语言模型SentencePiece / Unigram LLaMA 1/2SentencePiece22. 参考资料Hugging Face Transformers tokenizer summary: https://huggingface.co/docs/transformers/en/tokenizer_summaryHugging Face LLM Course: Byte-Pair Encoding tokenization: https://huggingface.co/learn/llm-course/en/chapter6/5Hugging Face LLM Course: WordPiece tokenization: https://huggingface.co/learn/llm-course/en/chapter6/6Google Research Blog: A Fast WordPiece Tokenization System: https://research.google/blog/a-fast-wordpiece-tokenization-system/SentencePiece paper: https://arxiv.org/abs/1808.06226SentencePiece GitHub: https://github.com/google/sentencepieceOpenAI tiktoken GitHub: https://github.com/openai/tiktokenMeta Llama 3 introduction: https://ai.meta.com/blog/meta-llama-3/DeepSeek LLM GitHub FAQ: https://github.com/deepseek-ai/DeepSeek-LLMQwen tokenizer note: https://huggingface.co/Qwen/Qwen-7B/blob/main/tokenization_qwen.py