深入了解大语言模型分词:从字符到子词的完整旅程

·

在任何大型语言模型(LLM)踏足训练阶段之前,分词(tokenization)总是第一道关卡。分词决定了模型究竟用怎样的“分辨率”看待语言,它直接影响推理速度、内存占用乃至最终表现。本篇将带你走完一次从朴素字符编码到高效 BPE 的全过程,并附赠完整的 Python 实战代码思路,助你亲手训练专属分词器。

什么是分词

分词就是把原始文本转化为一系列最小语义单元——token。token 可以是字符、子词、整词,甚至标点符号。
示例:

“你好,世界!” → [‘你好’, ‘,’, ‘世界’, ‘!’]

这些 token 会被映射到唯一整数 id,再查表成为高质量向量,送入模型。一个恰到好处的分词策略能显著压缩序列长度,降低算力开销。

为什么离不开分词?

  1. 文本→数字:模型只吃数字。
  2. 降维降噪:把动态无穷的自然语言映射到有限、稳定的向量空间。
  3. 统一标准:无论中文、英文还是 Emoji,最终都会“对齐”到同一个嵌入表。

如果你手握 10 GB 多语种语料,却缺少一套锋利的分词方案,训练出来的 LLM 很可能在“未见过的字符”前直接宕机。

朴素方案一览

粒度优点缺陷
字符级词汇固定(一般 ≤ 50 k),不会 OOV序列 tyrannosaurus 级拉长,transformer 能看到的有效信息骤减
词级信息密集,序列短词表 bulge 到百万级,跨语种极难收敛,且对黏着语极不友好

两者夹击之下,“子词”成为折中方案:既压缩长度,又控制词表规模。BPE 正是这条中间道路上最闪耀的明星。

👉 想了解如何一句话把词表压缩到 32 k 仍滴水不漏?最快路径在这里。

Unicode ord、UTF-8?先别急

只有当 UTF-8 遇上 Byte Pair Encoding(BPE),序列可被“二次压缩”成 32 k–50 k 的词表,才真正实现精悍与兼容 (multilingual bytes) 的双赢。

Byte Pair Encoding (BPE) 算法原理解析

核心思想一句话:把出现频率最高的连续字节对反复替换为新 token,直至预设词表填满。
示例流程:

原始序列

aaabdaaabac
  1. 合并 “aa” → ‘Z’
  2. 合并 “ab” → ‘Y’
  3. 合并 “ZY” → ‘X’

路径记录:Z=aa, Y=ab, X=ZY;逆向即可解码。
结果极度紧凑:

XdXac

该思路延伸到所有 UTF-8 字节流后,优点突出:

Python 手把手:20 行代码训练你的专属分词器

以下伪代码演示完整套路,替换 text 即可即刻复用:

# 载入语料
text = open("corpus.txt", encoding="utf-8").read()
tokens = list(map(int, text.encode("utf-8")))     # 先切成字节

# 统计频率 > 合并 > 循环
merges = {}
vocab_size_final = 32768
for _ in range(vocab_size_final - 256):
    stats = get_stats(tokens)
    top = max(stats, key=stats.get)
    new_id = 256 + len(merges)
    merges[top] = new_id         # 记录合并表
    tokens = merge(tokens, top, new_id)

# 解码测试
def decode(ids):
    tokens = b"".join(vocab[i] for i in ids)
    return tokens.decode("utf-8", errors="replace")

print(decode(encode("中文🎉NLP真酷")))

把 merge list 序列化保存成 merges.txt,便能在任何环境秒加载,真正做到“从零到上线”的一体分词方案。

拓展思考:多语种小词表的大威力

研究反复验证:同样的 50 k 词表规模下,训练集覆盖 100+ 语言,比单一英语词表平均压缩序列长度 30%—45%,下游任务 BLEU/Rouge 反而更高。原因在于同形异义、共享子词带来的泛化红利,几乎免费。

👉 想亲手验证多语种压缩率?别让浏览器停留太久,先测为敬

FAQ:关于分词,你可能想立刻问

Q1:词表越大越好吗?

A:不是。过大词表会拉长嵌入矩阵,显存爆炸;过小则序列太长,同样伤性能。实验显示,30 k–100 k 是在开源 LLM 场景中最平衡区间。

Q2:BPE 会不会把中文整句切成不可读的碎片?

A:不会。高频词“你好”“谢谢”会直接作为整体 token 存在;低频人名则拆成偏旁+部首,仍保存可逆字节路径。

Q3:如何评估分词器好坏?

A:核心指标有两组:
1) 词对句的压缩率(越小越好);
2) 最终下游任务分数(越高越好)。做一个交叉验证即可筛选。

Q4:要不要把停用词和符号单独拎出来?

A:不需要。让 BPE 自己学;强制人工标记反而破坏频次统计。

Q5:一定要重写代码吗?有没有现成可用?

A:transformers 库里的 ByteLevelBPETokenizer 已默认采用相同思路,10 行 API 就能无痛启动。但理解原理可帮助你定制特殊领域词表。

结论

分词不是“前菜”,而是让 LLM 能够饱餐文本的“刀叉”。从字符到子词的每一次抉择,都会影响显存、速度与泛化天花板。掌握 BPE 你便能亲手打磨一套贴合自己数据的利刃,真正做到“小词表、大格局”。祝你调参愉快,打造独一无二的大模型!