前言

在学习 LLaMA 论文的时候,我们很容易把注意力放在 Transformer、Attention、RoPE、SwiGLU、AdamW 这些看起来更“高级”的模块上。

但是有一个非常基础、非常容易被忽略的东西,其实决定了大模型理解文本的第一步:

Tokenizer,分词器。

很多新手刚开始学大模型时,会默认认为:

我输入一句话,大模型就直接理解了这句话。

但实际上不是这样。

大模型并不是直接读取“文字”的。
它真正看到的是一串数字,也就是 token ID

比如我们输入:

I love machine learning.

模型内部看到的可能不是这句话本身,而是类似:

[306, 5360, 4933, 6509, 29889]

这些数字才是模型真正处理的对象。

所以,Tokenizer 的作用就是:

把人类能读懂的文本,切分成模型能处理的 token,再把 token 转成数字 ID。

这篇文章就专门讲清楚:

  1. Token 是什么?
  2. Tokenizer 是什么?
  3. 为什么不能直接按单词切分?
  4. BPE 是怎么工作的?
  5. SentencePiece 和 BPE 是什么关系?
  6. LLaMA 为什么要关注 tokenizer?
  7. Tokenizer 和后续视觉 RAG、专利文档理解有什么关系?

这篇文章不追求炫技,而是站在新手视角,把概念讲透。


一、模型真的认识文字吗?

先问一个问题:

大模型真的认识“我爱机器学习”这几个字吗?

从人的角度看,我们输入的是:

我爱机器学习

但是模型不能直接处理字符串。

神经网络本质上处理的是数字张量,也就是一堆矩阵、向量、浮点数。
所以文本进入模型前,必须经过一个转换过程。

完整流程大概是:

原始文本
↓
Tokenizer 切分
↓
token 序列
↓
token ID 序列
↓
Embedding 层
↓
向量表示
↓
Transformer 模型

举个例子:

原始文本:I love machine learning.

经过 tokenizer 后,可能变成:

tokens: ["I", "love", "machine", "learning", "."]

再映射成数字 ID:

token IDs: [306, 5360, 4933, 6509, 29889]

然后 embedding 层会把每个 ID 变成一个向量:

306  → [0.12, -0.03, 0.78, ...]
5360 → [0.44,  0.21, -0.19, ...]

所以模型真正理解语言的第一步,不是 Attention,而是 tokenizer。

如果 tokenizer 把文本切得不好,后面的模型再强,也会受到影响。


二、几个基础概念:Token、Vocabulary、Token ID、Tokenizer

在正式讲 BPE 之前,我们先把几个基础名词说清楚。


2.1 Token 是什么?

Token 可以理解成模型处理文本的基本单位。

它不一定等于一个字,也不一定等于一个词。

一个 token 可能是:

一个英文单词:machine
一个中文字符:我
一个中文词:机器
一个子词:learn
一个后缀:ing
一个标点:.
一个数字:2
一个空格相关符号

所以 token 不是人类语言里的固定单位,而是 tokenizer 根据规则或算法切出来的单位。

比如:

unbelievable

可能被切成:

un / believe / able

也可能被切成:

un / believable

甚至可能是:

unbelievable

具体怎么切,取决于 tokenizer 的词表和算法。


2.2 Vocabulary 是什么?

Vocabulary,简称 vocab,中文叫 词表

词表就是 tokenizer 认识的所有 token 的集合。

比如一个极简词表可能是:

["I", "love", "machine", "learning", ".", "un", "believe", "able"]

每个 token 都会被分配一个编号,也就是 token ID。


2.3 Token ID 是什么?

Token ID 就是 token 在词表里的编号。

比如:

"I"        → 306
"love"     → 5360
"machine"  → 4933
"learning" → 6509
"."        → 29889

模型不直接处理 "love" 这个字符串,而是处理它对应的数字 ID。


2.4 Tokenizer 是什么?

Tokenizer,中文叫 分词器

它主要做两件事:

第一,把文本切成 token。

I love machine learning.
↓
["I", "love", "machine", "learning", "."]

第二,把 token 转成 token ID。

["I", "love", "machine", "learning", "."]
↓
[306, 5360, 4933, 6509, 29889]

所以 tokenizer 是文本进入大模型之前的“入口”。

一句话总结:

Tokenizer 负责把自然语言文本变成模型能处理的数字序列。


三、为什么不直接按单词切分?

有人可能会问:

英文不是天然有空格吗?为什么不直接按单词切?

比如:

I love machine learning.

直接按空格切成:

I / love / machine / learning

看起来很自然。

但问题很快就来了。


3.1 问题一:词表会爆炸

英文单词非常多,而且还有大量变形。

比如:

learn
learns
learning
learned
learner
relearn
unlearn
unlearnable

如果每个完整单词都作为一个 token,词表会非常大。

而且模型会把这些词当成完全不同的 token,不容易知道它们之间的关系。

比如:

learn
learning
learned

它们明显有共同词根,但如果作为三个完全独立 token,模型要靠大量数据才能学出它们的联系。


3.2 问题二:罕见词和新词怎么办?

如果模型遇到词表里没有的词怎么办?

比如:

electroencephalography
bioinformatics
uncharacteristically

如果 tokenizer 只能按完整单词切,那么没见过的词就会变成:

[UNK]

也就是 unknown token,未知词。

这会导致信息丢失。

比如:

bioinformatics

如果变成 [UNK],模型就不知道里面有:

bio
inform
atics

这些有意义的片段。


3.3 问题三:代码、数字、公式、专利编号很难处理

大模型不只处理普通英语,还可能处理:

Python 代码
数学公式
论文编号
专利编号
网页 URL
文件路径
表格字段

比如:

CN202410123456.7
FIG. 1
claim 3
torch.nn.Linear

如果直接按单词切,非常容易切得不合理。


四、Subword Tokenization:子词切分思想

为了解决上面的问题,现代大模型通常使用 subword tokenization

Subword,中文叫 子词

它的核心思想是:

常见词可以作为完整 token,罕见词可以拆成更小的 token。

比如:

learning

如果很常见,可以作为一个 token:

learning

但如果是罕见词:

unbelievability

可以拆成:

un / believe / ability

这样有几个好处:

第一,词表不会无限大。
第二,遇到新词也可以拆开处理。
第三,模型可以学习词根、前缀、后缀之间的关系。
第四,对代码、数字、符号更友好。

这就是 BPE、WordPiece、Unigram 等 tokenizer 方法背后的大方向。

LLaMA 使用的是 BPE,所以我们重点讲 BPE。


五、BPE 是什么?

BPE 全称是 Byte-Pair Encoding,中文常叫 字节对编码

这个名字刚开始听起来有点奇怪,我们拆开理解:

  • Byte:字节;
  • Pair:一对,相邻的两个符号;
  • Encoding:编码。

在大模型 tokenizer 语境里,BPE 可以理解为:

从很小的文本单位开始,不断合并语料中最常见的相邻符号对,最终得到一套子词词表。

简单说就是:

先切得很碎
↓
统计哪些片段经常连在一起
↓
把高频片段合并
↓
重复很多次
↓
得到词表

BPE 的核心思想非常朴素:

高频组合值得变成一个 token,低频组合没必要单独占一个 token。


六、BPE 的训练过程:一步一步看懂

我们用一个简化例子来理解 BPE。

假设训练语料只有几个词:

low
lower
lowest
newer
wider

注意:真实大模型的训练语料当然大得多,这里只是为了讲清楚原理。


6.1 第一步:把词拆成字符

一开始,BPE 会把词拆成最小单位。

比如:

low     → l o w
lower   → l o w e r
lowest  → l o w e s t
newer   → n e w e r
wider   → w i d e r

为了表示词的结束,很多讲解里会加一个特殊符号,比如 </w>

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> 表示 word end,也就是词尾。


6.2 第二步:统计相邻 pair 的出现次数

BPE 会统计所有相邻符号对的频率。

比如:

l o
o w
w </w>
w e
e r
r </w>
e s
s t
...

如果 l o 出现很多次,说明它们经常一起出现。
如果 o w 也出现很多次,说明它们也经常一起出现。
如果 e r 经常出现,也可能值得合并。


6.3 第三步:合并最高频 pair

假设当前最高频 pair 是:

l + o

那么 BPE 就把它合并成一个新符号:

lo

于是原来的语料变成:

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>

此时词表里多了一个 token:

lo

6.4 第四步:继续统计,继续合并

下一轮重新统计相邻 pair。

这时可能发现:

lo + w

出现很多次,于是合并:

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>

词表里又多了一个 token:

low

如果后面发现:

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>

6.5 第五步:重复直到达到词表大小

BPE 不会无限合并下去。

我们会提前设定一个词表大小,比如:

32000
50000
100000

当词表大小达到设定值,BPE 训练就停止。

最后我们得到的是:

基础字符 token
+
高频子词 token
+
高频完整词 token
+
特殊 token

七、BPE 编码新文本时怎么做?

上面讲的是 BPE 如何训练词表。

那训练好之后,来了一个新词,BPE 怎么切呢?

假设训练阶段学到了一些 merge 规则:

l + o  → lo
lo + w → low
e + r  → er
low + er → lower

现在来了一个词:

lower

BPE 会先拆成字符:

l o w e r

然后按学到的 merge 规则依次合并:

l o w e r
↓
lo w e r
↓
low e r
↓
low er
↓
lower

如果词表里有 lower,最后可能就是一个 token:

lower

如果词表里没有完整的 lower,但有 lower,那可能就是:

low / er

如果是非常罕见的词,也许会拆得更碎。

所以 BPE 编码的本质是:

根据训练时学到的 merge 规则,把文本尽可能合并成词表里已有的 token。


八、BPE 的本质:高频压缩,低频拆分

BPE 很像一种压缩方法。

高频片段反复出现,就值得用一个 token 表示。

比如:

ing
tion
the
learn
machine

这些片段很常见,就可能成为 token。

但是非常罕见的词没有必要整个放进词表。

比如:

electroencephalographically

如果它很少出现,BPE 可能会拆成:

electro / encephalo / graph / ically

这样做的好处是:

既不会让词表无限膨胀
又能处理没见过的新词

九、BPE 和中文有什么关系?

中文没有天然空格,所以 tokenizer 更重要。

比如一句话:

我喜欢机器学习

可能有多种切法:

我 / 喜欢 / 机器 / 学习

也可能是:

我 / 喜 / 欢 / 机器学习

还可能是:

我 / 喜欢 / 机器学习

不同 tokenizer 的切法不同,模型看到的 token 序列就不同。

这会影响模型对中文语义的理解。

比如:

人工智能

如果被切成:

人工 / 智能

通常比较合理。

如果被切成:

人 / 工 / 智 / 能

也能处理,但序列变长,语义组合成本更高。

所以 tokenizer 对中文、多语言、代码、公式、文档编号都很重要。


十、SentencePiece 是什么?

讲完 BPE,再讲 LLaMA 论文里提到的 SentencePiece

SentencePiece 是一个 tokenizer 工具库。

注意它和 BPE 不是同一个层级的东西。

可以这样理解:

BPE:一种分词算法
SentencePiece:一个实现分词算法的工具

SentencePiece 可以实现不同的子词算法,比如:

BPE
Unigram Language Model

LLaMA 使用的是:

SentencePiece 工具 + BPE 算法

所以你看到论文里说:

byte-pair encoding
SentencePiece

不要混淆。

它的意思不是“BPE 和 SentencePiece 是两个并列算法”,而是:

LLaMA 使用 BPE 作为分词算法,并用 SentencePiece 这个工具来实现。


十一、为什么 SentencePiece 对大模型很常用?

SentencePiece 有一个很重要的特点:

它可以直接从原始文本训练 tokenizer,不强制要求提前按空格分词。

这对多语言特别重要。

因为英文有空格,但中文、日文等语言没有天然空格。

比如:

我喜欢机器学习

如果 tokenizer 工具强依赖空格,就不适合中文。

SentencePiece 把句子看成一串符号,可以直接学习子词单位,因此更适合多语言场景。


十二、LLaMA tokenizer 的几个细节

LLaMA 论文中提到,它使用 BPE tokenizer,并且使用 SentencePiece 实现。除此之外,还有几个细节值得注意。


12.1 数字拆分

LLaMA 会把数字拆成单个 digit。

比如:

2026

可能会被处理成:

2 / 0 / 2 / 6

这样做有什么好处?

如果把 20262027 当成两个完全不同的 token,模型需要分别学习它们。

但如果拆成数字:

2026 → 2 / 0 / 2 / 6
2027 → 2 / 0 / 2 / 7

模型就能看到它们前面结构类似。

这对年份、编号、金额、数学表达、专利编号都有帮助。

比如专利文档里经常有:

CN202410123456.7
US20230123456A1
FIG. 2
claim 1

如果 tokenizer 能更细粒度地处理数字和符号,模型就更容易理解这些编号之间的关系。


12.2 Byte fallback 是什么?

Byte fallback 可以理解成:

如果遇到词表里没有的字符,就退回到字节级别表示。

这解决了未知字符问题。

比如模型遇到一个很少见的符号、乱码、特殊字符,如果 tokenizer 完全不认识,就可能变成 [UNK]

但 byte fallback 可以把它拆成更底层的字节表示。

这样至少不会彻底丢失信息。

一句话理解:

byte fallback = 遇到不认识的字符,也尽量用字节表示出来,而不是直接丢掉

这对多语言、网页文本、代码、专利文档、特殊符号都很重要。


十三、Tokenizer 会影响什么?

很多人觉得 tokenizer 只是预处理工具,和模型能力关系不大。

这个理解是不对的。

Tokenizer 至少会影响下面几个方面。


13.1 影响序列长度

同一句话,不同 tokenizer 切出来的 token 数可能不同。

比如:

我喜欢机器学习

一种切法:

我 / 喜欢 / 机器学习

只有 3 个 token。

另一种切法:

我 / 喜 / 欢 / 机 / 器 / 学 / 习

有 7 个 token。

token 越多,序列越长,计算成本越高。

Transformer Attention 的计算和序列长度关系非常大。
如果序列长度变长,显存和计算都会增加。


13.2 影响模型学习语义的难度

如果 tokenizer 把有意义的词切得太碎,模型需要在后面层里重新组合语义。

比如:

机器学习

如果切成:

机器 / 学习

模型比较容易理解。

如果切成:

机 / 器 / 学 / 习

模型也能学,但组合成本更高。


13.3 影响代码能力

代码里有很多特殊符号:

torch.nn.Linear(in_features, out_features)

如果 tokenizer 能合理处理:

torch
.
nn
.
Linear
(
in
_
features
,
out
_
features
)

模型就更容易学习代码结构。

如果切得很混乱,模型学习代码会更困难。


13.4 影响数学和数字能力

数字如果处理不好,模型可能很难理解数值结构。

比如:

12345
12346

如果它们是两个完全不同 token,模型不容易看出它们只差最后一位。

如果拆成数字,模型至少能看到结构相似性。

当然,数字拆分并不代表模型就真正会数学推理。
但它能让模型更好地接触数字结构。


十三、Tokenizer 和 Embedding 的关系

Tokenizer 只负责把文本变成 token ID。

但 token ID 还不能直接进入 Transformer。

中间还需要 Embedding 层

Embedding,中文叫 嵌入向量

它的作用是:

把离散的 token ID 映射成连续向量。

公式可以写成:

[
x_i = E[t_i]
]

这里每个符号都解释一下:

  • (t_i):第 (i) 个 token 的 ID;
  • (E):embedding 矩阵,也就是一个可学习参数表;
  • (E[t_i]):从 embedding 矩阵里取出第 (t_i) 行;
  • (x_i):第 (i) 个 token 对应的向量表示。

举个例子:

token ID: 306

embedding 层会查表:

E[306] = [0.12, -0.03, 0.78, ..., 0.21]

这个向量才会进入 Transformer。

如果模型 hidden size 是 4096,那么每个 token 都会变成一个 4096 维向量。

所以完整过程是:

文本
↓
Tokenizer
↓
token ID
↓
Embedding
↓
token vector
↓
Transformer

十五、一个完整例子:从文本到模型输入

我们用一个例子把流程串起来。

输入文本:

FIG. 1 shows a neural network.

第一步,Tokenizer 切分。

可能得到:

["FIG", ".", "1", "shows", "a", "neural", "network", "."]

第二步,映射成 token ID。

[14523, 29889, 29896, 4892, 263, 17842, 9876, 29889]

第三步,Embedding 查表。

14523 → 向量 x1
29889 → 向量 x2
29896 → 向量 x3
...

第四步,形成输入矩阵。

如果一共有 8 个 token,每个 token 是 4096 维,那么输入矩阵形状是:

[8, 4096]

也就是:

seq_len = 8
hidden_dim = 4096

然后这个矩阵才会进入 LLaMA 的 Transformer block。


十六、新手容易踩的几个坑


坑 1:以为 token 就是单词

这是最常见误区。

token 不等于单词。

它可能是词、子词、字符、符号、数字片段。


坑 2:以为 tokenizer 不重要

Tokenizer 非常重要。

它影响:

序列长度
显存消耗
训练效率
多语言能力
代码能力
数字处理能力

坑 3:把 BPE 和 SentencePiece 混为一谈

二者不是一个层级。

BPE 是算法
SentencePiece 是工具

LLaMA 是用 SentencePiece 实现 BPE。


坑 4:以为数字作为完整 token 一定更好

不一定。

完整数字 token 可以缩短序列,但泛化可能差。
拆成 digit 序列会让序列变长,但更容易捕捉数字结构。

LLaMA 选择把数字拆成单个 digit,是为了更好处理数字结构。


十七、和 LLaMA 论文的关系

LLaMA 论文中 tokenizer 只是 Approach 里的一小段,但它是模型训练的入口。

LLaMA 的整体训练流程是:

公开文本数据
↓
清洗和去重
↓
Tokenizer 切分
↓
变成 token 序列
↓
输入 Decoder-only Transformer
↓
预测下一个 token
↓
计算 loss
↓
AdamW 更新参数

所以 tokenizer 决定了:

训练数据最终以什么形式进入模型

如果 tokenizer 不合理,模型从第一步就会受到影响。


十八、总结

这篇文章主要讲了大模型如何“读文字”。

核心结论如下:

概念含义
Token模型处理文本的基本单位,不一定是词
Vocabularytokenizer 认识的所有 token 的集合
Token IDtoken 在词表中的数字编号
Tokenizer把文本切成 token,并映射成 token ID 的工具
BPE从字符开始,不断合并高频相邻片段的子词算法
SentencePiecetokenizer 工具,可以实现 BPE 等算法
Byte fallback遇到未知字符时退回字节级表示
Embedding把 token ID 映射成连续向量

一句话总结:

大模型不是直接读取文字,而是先通过 tokenizer 把文本变成 token ID,再通过 embedding 变成向量,最后才进入 Transformer。

对于 LLaMA 来说,Tokenizer 是模型训练入口,决定了文本侧信息如何进入模型。

更多推荐