为什么需要 Tokenization?从字符级到子词级,理解 Unicode 和 UTF-8 编码

为什么需要 Tokenization

大语言模型的核心是 Transformer,它处理的是数字序列,而不是文本。那么问题来了:如何将文本转换成模型可以理解的数字?

这就是 Tokenization 要解决的问题。Tokenization 负责将文本字符串转换为整数序列(tokens),这些整数会作为查找表的索引,获取对应的向量表示(embeddings),最终输入到 Transformer 中。

image.png

Tokenization 看起来简单,但它是 LLM 中许多”奇怪”行为的根源。比如:

这些问题都可以追溯到 Tokenization 的设计。

从字符级 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

字符级方案的工作流程

整个流程可以总结为:

  1. 构建词汇表:从训练文本中提取所有唯一字符
  2. 编码 (Encoding):文本 → Token 序列(整数列表)
  3. 嵌入 (Embedding):每个 token 通过查找表获取向量表示