
在这篇文章里,我们会学习 BPE(Byte Pair Encoding,字节对编码):大多数现代大型语言模型(LLM)都会使用这种分词算法,在处理文本之前先把文本拆成更小的片段。
我们会理解 BPE 是什么,为什么需要它,以及它如何通过一个简单例子一步步工作。
我是 Amit Shekhar,Outcome School 创始人。我教过并辅导过很多开发者,他们靠自己的努力拿到了高薪技术岗位;我也帮助过许多科技公司解决各自独特的问题,并创建过很多被顶级公司使用的开源库。我热衷于通过开源、博客和视频分享知识。
我在 Outcome School 教 AI and Machine Learning。
我们开始吧。
什么是 Tokenization?
在理解 BPE 之前,我们必须先理解 tokenization。
当我们输入一句话,比如 “I love teaching AI” 时,我们看到的是词。但模型不能直接理解词。它处理的是数字。所以第一步,是把文本拆成叫作 token 的小片段。然后每个 token 会被转换成一个数字。这个把文本拆成 token 的过程,就叫 tokenization。
可以把它想成一块巧克力。完整的巧克力就是句子。你掰下来的每一小块就是一个 token。模型一次处理多个 token。
现在问题来了:我们怎么决定从哪里切?每个 token 应该多大或多小?
这就是不同 tokenization 方法登场的地方。
问题:如何把文本拆成 token?
把文本拆成 token 有不同方式。我们先看两个最简单的方法,并理解为什么它们并不好用。
方法 1:词级 tokenization
最简单的想法是把每个词当成一个 token。
对于句子 “I love teaching”,token 会是:“I”、“love”、“teaching”。
这看起来很容易。但这个方法的问题是,模型需要知道语言里的每一个词。想想看,光英语就有几十万个词。还有人名、技术术语、其他语言里的词、拼写错误,以及每天都在被创造出来的新词。
如果模型看到一个从未见过的词,比如 “ChatGPT” 或 “tokenization”,它就不知道该怎么办。它会把整个未知词当成一个单独的 “unknown” token,并丢失所有含义。
方法 2:字符级 tokenization
另一个极端是把每个字符都当成一个 token。
对于句子 “I love”,token 会是:“I”、“ ”、“l”、“o”、“v”、“e”。
现在模型可以处理任何词,因为每个词都是由字符组成的,而唯一字符的数量相比词的数量要小得多。比如英语只有 26 个字母。即使加上数字、标点和空格,总数仍然很小。不会再有任何词完全 “unknown”。
但这个方法的问题是 token 太小。同一段文本,模型必须处理更多 token。一个 10 个词的句子可能变成 50 多个 token。这会让训练非常慢,也让模型更难理解含义,因为像 “l” 这样的单个字符本身几乎没有什么含义。
我们需要一种两全其美的方案:不要太大,也不要太小。所以,BPE 来救场了。
什么是 BPE(Byte Pair Encoding)?
BPE(Byte Pair Encoding) 是一种 tokenization 算法,它会把文本拆成介于字符和词之间的片段。
它的工作方式是:反复寻找相邻 token 中最常见的一对,然后把这一对合并成一个 token。
所以,BPE 从单个字符开始,不断合并最频繁出现的字符对,直到构建出一套由常见片段组成的词表。这些片段叫作子词(subword)。子词可以是完整词(比如 “the”),也可以是词的一部分(比如 “ing”、“un”、“tion”),甚至也可以是单个字符。
可以把它想成学习速记。刚开始记笔记时,你会把每个字母都写下来。但时间久了,你会发现某些组合经常出现,比如 “ing” 或 “tion”。于是你会为它们创建快捷写法。BPE 会自动做同样的事。
BPE 如何工作:一步一步看
学习这个概念最好的方式,是看一个例子。
假设我们有下面这段文本,想用它来构建词表:
low low low low low
lower lower
newest newest newest newest newest newest
widest widest widest
为了便于理解,我们先统计词频:
- “low” 出现 5 次
- “lower” 出现 2 次
- “newest” 出现 6 次
- “widest” 出现 3 次
步骤 1:从字符开始
我们先把每个词拆成单个字符。我们还会在每个词末尾加一个特殊的词尾符号 “_”,这样模型就知道词在哪里结束。
l o w _ (frequency: 5)
l o w e r _ (frequency: 2)
n e w e s t _ (frequency: 6)
w i d e s t _ (frequency: 3)
初始词表就是所有单个字符:{l, o, w, e, r, n, s, t, i, d, _}
步骤 2:找到最频繁的一对
现在,我们查看整段文本中所有相邻 token 对,并统计每一对出现了多少次。
为了便于理解,我们统计几个重要的相邻对:
- “e s” 出现在 “newest”(6 次)和 “widest”(3 次)中 = 9 次
- “s t” 出现在 “newest”(6 次)和 “widest”(3 次)中 = 9 次
- “t _” 出现在 “newest”(6 次)和 “widest”(3 次)中 = 9 次
- “l o” 出现在 “low”(5 次)和 “lower”(2 次)中 = 7 次
- “o w” 出现在 “low”(5 次)和 “lower”(2 次)中 = 7 次
最频繁的相邻对是 “e s”,出现了 9 次。我们把这一对合并成一个 token:“es”。
步骤 3:合并并更新
把 “e s” 合并成 “es” 之后,我们的词现在长这样:
l o w _ (frequency: 5)
l o w e r _ (frequency: 2)
n e w es t _ (frequency: 6)
w i d es t _ (frequency: 3)
现在的词表是:{l, o, w, e, r, n, s, t, i, d, _, es}
这里可以看到,凡是 “e” 和 “s” 连在一起出现的地方,都被合并成了 “es”。
步骤 4:重复
我们重复同样的过程,找到下一个最频繁的相邻对。现在 “es t” 出现了 9 次(6 次来自 “newest”,3 次来自 “widest”)。我们把它合并成 “est”。
l o w _ (frequency: 5)
l o w e r _ (frequency: 2)
n e w est _ (frequency: 6)
w i d est _ (frequency: 3)
词表:{l, o, w, e, r, n, s, t, i, d, _, es, est}
我们会不断重复这个过程。下一次合并可能是把 “est ” 合成 “est”。然后把 “l o” 合成 “lo”。再把 “lo w” 合成 “low”。以此类推。
每次合并,词表都会增加一个 token,而文本会用更少、更大的片段来表示。
步骤 5:达到目标后停止
我们会持续合并,直到达到想要的词表大小。这个大小是我们预先选择的一个数字。现代 LLM 通常使用 32,000 到 256,000 个 token 的词表。例如,LLaMA 3 使用大约 128K token 的词表,而 Gemma 使用大约 256K token。
最终词表会包含几类东西:
- 像 “the”、“is”、“and” 这样的常见完整词
- 像 “ing”、“tion”、“est”、“un” 这样的常见子词
- 用于罕见组合的单个字符
这就是 BPE 构建词表的方式。
给你的一条简短提醒
无论你在哪个技术领域工作,都应该熟悉这些主题:
- LLM
- RAG
- MCP
- Agent
- Fine-tuning
- Quantization
我们在一个视频里把它们串了起来:
AI Engineering Explained: LLM, RAG, MCP, Agent, Fine-Tuning, and Quantization
不用停下来读,先收藏,等有时间再看。未来的你会感谢现在的你。
现在,我们回到主题。
BPE 如何处理新文本
BPE 训练完成后,它会得到两样东西:一个词表,以及一份有顺序的合并规则列表。顺序很重要。训练期间,BPE 按特定顺序学到了这些合并:先把 “e s” 合成 “es”,再把 “es t” 合成 “est”,再把 “l o” 合成 “lo”,以此类推。这个确切顺序会被保存下来。
现在,当 BPE 需要处理新文本时,它不是简单地去词表里查子词。相反,它会按照训练时学到的同一顺序,重新播放同一组合并规则。
假设我们想把 “lowest” 这个词拆成 token。它的过程如下:
开始: 把这个词拆成单个字符:l o w e s t
应用合并规则 1(把 “e s” 合成 “es”):l o w es t
应用合并规则 2(把 “es t” 合成 “est”):l o w est
应用合并规则 3(把 “l o” 合成 “lo”):lo w est
应用合并规则 4(把 “lo w” 合成 “low”):low est
结果:“lowest” 被拆成 [“low”, “est”]。
类似地,对于 “newer” 这个词:
开始: n e w e r
合并规则会按顺序应用。这个词中匹配到相邻对的规则会一步步被应用,最终得到:[“new”, “er”]。
对于像 “widestness” 这样的罕见词:
开始: w i d e s t n e s s
合并规则会按顺序应用。像 “es” 和 “est” 这样的常见合并,只要匹配上就会触发。剩下的字符会保持为更小的片段。这个词会被拆成词表中已知的子词。
为什么顺序很重要? 因为不同的合并顺序可能产生不同结果。如果我们只是做贪心查找,也就是尝试寻找词表中最长的匹配子词,就可能得到和模型预期不同的分词结果。通过严格按照训练顺序重放合并规则,我们每次都能得到一致且正确的分词结果。
这就是 BPE 的妙处。它永远不会遇到完全未知的词。即使一个词从未见过,BPE 也可以通过应用合并规则把它拆成更小的片段。在最坏情况下,它会退回到单个字符,但对于常见语言,这种情况很少发生。问题解决了。
这就是 BPE 处理新文本的方式。
如果我们想深入学习 Tokenization 和 LLM 内部机制,并从零开始构建一个大型语言模型(LLM),可以看看 Outcome School 的 AI and Machine Learning Program。
为什么现代 LLM 使用 BPE
BPE 是大多数现代 LLM 使用的 tokenization 算法。原因如下:
处理未知词: BPE 可以处理任何词,即使这个词从未见过,也能把它拆成已知的子词片段。没有任何词会完全 “unknown”。
词表大小高效: BPE 不需要为每一个可能出现的词准备数百万个条目,而是创建一个 32,000 到 256,000 个 token 的紧凑词表,就能表示任意文本。这让我们的工作轻松很多。
平衡含义和效率: 像 “the” 和 “is” 这样的常见词会保留为单个 token,处理快,也有明确含义。罕见词会被拆成子词片段,仍然可理解、可管理。这给了我们两全其美的效果。
跨语言工作: BPE 在字符层面工作,所以可以处理任何语言、任何文字系统和任何特殊字符。同一个算法可以用于英语、中文、阿拉伯语和代码,而且算法本身不需要任何改变。
模型使用的是 BPE 的某种变体,但核心思想保持不变:BPE 或它的变体,位于现代 LLM 读取和处理文本方式的核心。
现在,我们已经理解了 BPE(Byte Pair Encoding)以及它如何工作。
准备 AI Engineering 面试:AI Engineering Interview Questions
今天就到这里。
谢谢
Amit Shekhar Outcome School 创始人