为什么需要 Tokenization?从字符级到子词级,理解 Unicode 和 UTF-8 编码
大语言模型的核心是 Transformer,它处理的是数字序列,而不是文本。那么问题来了:如何将文本转换成模型可以理解的数字?
这就是 Tokenization 要解决的问题。Tokenization 负责将文本字符串转换为整数序列(tokens),这些整数会作为查找表的索引,获取对应的向量表示(embeddings),最终输入到 Transformer 中。

Tokenization 看起来简单,但它是 LLM 中许多”奇怪”行为的根源。比如:
- 为什么 GPT 有时不擅长逐字母拼写?
- 为什么某些语言的性能比英语差?
- 为什么模型对某些字符串特别敏感?
这些问题都可以追溯到 Tokenization 的设计。
让我们从最简单的方案开始:字符级 Tokenization。假设我们有一段文本:
text = 'This is CookLLM, and keeping study with me!'
我们可以提取所有出现的唯一字符,构建一个词汇表:
# 获取所有唯一字符
chars = sorted(list(set(text)))
vocab_size = len(chars)
print(''.join(chars))
# 输出: !,CLMTadeghikmnopstuwy
print(vocab_size)
# 输出: 23
这个词汇表只有 23 个字符。接下来,我们创建字符到整数的映射:
# 创建字符 <-> 整数的映射
stoi = {ch: i for i, ch in enumerate(chars)} # string to integer
itos = {i: ch for i, ch in enumerate(chars)} # integer to string
# 编码函数:字符串 -> 整数列表
encode = lambda s: [stoi[c] for c in s]
# 解码函数:整数列表 -> 字符串
decode = lambda l: ''.join([itos[i] for i in l])
# 测试
print(encode("this is"))
# 输出: [19, 11, 12, 18, 0, 12, 18]
print(decode(encode("this is")))
# 输出: this is
现在我们可以将整个文本转换为 token 序列:
import torch
data = torch.tensor(encode(text), dtype=torch.long)
print(data.shape, data.dtype)
# 输出: torch.Size([43]) torch.int64
print(data[:20])
# 前 20 个 tokens
整个流程可以总结为: