Tokenization 分词器深度解析:BPE vs WordPiece vs SentencePiece
2026/5/28 19:19:14 网站建设 项目流程

Tokenization 分词器深度解析:BPE vs WordPiece vs SentencePiece

一、为什么Tokenization值得你花30分钟深入理解?

如果你曾好奇过这些问题,这篇文章就是为你写的:

  • 为什么 ChatGPT 数数永远数不对(连 “strawberry 里有几个 r” 都会错)?
  • 为什么同一个中文句子,用不同模型分词后的 token 数能差一倍?
  • 为什么有些模型对英文支持完美,中文却频频产出乱码?
  • 那个神秘的<|endoftext|>标记到底是什么,为什么删掉它模型就疯了?

答案都藏在Tokenization(分词)这个看似"无聊"的预处理步骤里。它处于自然语言与大模型之间的最底层——决定了一句话如何被切割、编码、以及模型如何"看见"文本。Andrej Karpathy 在 2023 年的一期演讲中直言:“Tokenization 是我在大模型课程中最想强调的部分,因为几乎所有的诡异行为都可以追溯到它。”

本文将带你从零开始,深入掌握三大主流子词分词算法的原理、实现与实战权衡。读完你会理解:

  • BPE 如何用"合并操作"从零构建词汇表
  • WordPiece 如何在 BPE 基础上引入概率语言模型视角
  • SentencePiece 如何解决多语言空格歧义问题
  • 这三者分别在哪些顶流模型中使用(GPT / BERT / LLaMA)
  • 亲手实现一个最小化 BPE tokenizer 并获得深刻直觉

二、Tokenization 的本质问题

2.1 从字符到子词的进化

给定一段文本,我们需要将其转换为模型可以处理的数字序列。历史上经历过三种范式:

阶段粒度示例 “我喜欢NLP”问题
字符级每个字我 / 喜 / 欢 / N / L / P序列太长,丢失语义
词级每个词我 / 喜欢 / NLP词汇表爆炸,OOV(未登录词)灾难
子词级词根+词缀我 / 喜欢 / N / LP✅ 最优平衡

子词分词**(subword tokenization)** 的核心理念:将高频词保持完整,将低频词拆分为可重用的子词单元。这样:

  • 词汇表大小可控(通常 30k–256k)
  • 任意新词都能通过子词组合表示(消灭 OOV)
  • 模型可以利用子词间的形态学规律(如play,playing,played共享play词干)

2.2 大模型中 Tokenizer 的关键角色

在大模型训练中,tokenizer 的影响被严重低估。它直接决定了:

  1. 上下文窗口利用率:同样一段话语,不同 tokenizer 产出的 token 数可能相差 2–3 倍。这对有 4K/8K/128K 窗口限制的模型至关重要。
  2. 推理成本:按 token 计费的 API(如 OpenAI GPT-4)中,tokenizer 的压缩效率直接转化为你的账单数字。
  3. 多语言公平性:英文通常每个词 ~1.3 token,而中文一个汉字可能就是 1-2 token,导致同等语义信息的中文 Token 成本远高于英文。
  4. 特殊能力边界:模型的拼写检查、数学计算、代码生成能力,都受限于 tokenizer 的粒度设计。

三、BPE(Byte Pair Encoding)—— GPT 系列的基石

3.1 直觉理解

BPE 最初是 1994 年提出的一种数据压缩算法,2016 年被 Sennrich 等人引入 NLP 用于解决机器翻译中的罕见词问题。其思路异常朴素:

从字符级开始,反复合并出现频率最高的相邻 token 对,直到达到目标词汇表大小。

想象你在压缩字符串 “aaabdaaabac”:

  • 最常见相邻对是aa(出现 4 次)→ 用Z替换 → “ZabdZabac”
  • 现在最常见相邻对是ab(出现 2 次)→ 用Y替换 → “ZYdZYac”

在 NLP 中,“字符"被替换为"字节"或"Unicode 字符”,合并操作持续进行直到词汇表达到预设大小(如 GPT-2 的 50,257)。

3.2 训练算法(伪代码)

输入:语料库 C,目标词汇表大小 V 输出:合并规则列表 merges 1. 初始化:vocab = 所有唯一字符(或字节) 2. 将语料库表示为字符序列:corpus = [list(word) for word in C] 3. while |vocab| < V: a. 统计所有相邻 token 对的频率 b. 选出频率最高的 pair (A, B) c. 将 (A, B) 加入 merges d. 向 vocab 添加新 token "AB" e. 在 corpus 中将所有 A B 替换为 AB 4. 返回 merges

3.3 手把手 Python 实现

下面是一个可运行的 BPE 训练器,约 100 行代码覆盖核心逻辑:

importrefromcollectionsimportCounter,defaultdictclassSimpleBPETokenizer:"""最小化 BPE tokenizer,展示核心训练和编码逻辑"""def__init__(self,vocab_size=300):self.vocab_size=vocab_size self.merges={}# (token_a, token_b) -> merged_tokenself.vocab=set()def_get_stats(self,corpus):"""统计语料库中所有相邻 token 对的频率"""pairs=defaultdict(int)forwordincorpus:foriinrange(len(word)-1):pairs[(word[i],word[i+1])]+=1returnpairsdef_merge_pair(self,pair,corpus):"""将指定 token 对在语料库中全部合并"""a,b=pair new_token=a+b new_corpus=[]forwordincorpus:new_word=[]i=0whilei<len(word):ifi<len(word)-1andword[i]==aandword[i+1]==b:new_word.append(new_token)i+=2else:new_word.append(word[i])i+=1new_corpus.append(new_word)returnnew_corpusdeftrain(self,texts):"""在文本列表上训练 BPE"""# 分词 + 在每个词后添加结束符words=[]fortextintexts:forwordintext

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询