语言模型与分词
对应课程 Lecture 1:Overview, tokenization(Percy)
语言模型是什么?
一句话定义:语言模型(Language Model,LM)是对词元序列的概率分布进行建模的模型,它回答的问题是“给定前面的文本,下一个词元是什么?”
形式化地说,一个自回归(autoregressive)语言模型定义了条件概率:
给定一个提示(prompt)
一个重要的视角转变
传统 NLP 的思路是“一个任务一个模型”:翻译训练翻译模型、摘要训练摘要模型。语言模型开启了一个新范式——一个通用的系统处理所有任务。你要做的只是把任务描述成文本(提示),模型自己“续写”出答案。这就是大语言模型时代的基础。
造一个语言模型需要哪些部件?
这是整门课的地图。从原始互联网到可用的模型,完整流水线是:
互联网文本 → [数据:获取/过滤/去重] → 训练语料
→ [分词:文本 → token] → token 序列
→ [训练:架构 + 优化器] → 基座模型
→ [后训练:SFT/RLHF] → 对话/推理模型
→ [评估] → 部署上线每个方框都是课程的一个章节。这门课的目标就是让你亲手实现每一个方框,而不是调用现成的库。
分词:从字符串到词元
为什么需要分词?
神经网络的输入是数字,不是字符串。所以第一步要把文本切分成离散单元,再映射为整数 ID,这个过程叫分词(tokenization)。切分出来的单元叫词元(token)。
分词看似简单,实际上深刻影响模型的行为:
- 词表(vocabulary)大小决定了嵌入矩阵(embedding)的规模;
- 切分粒度决定了序列长度(直接影响计算量);
- 处理数字、代码、罕见语言的效果,很大程度上取决于分词器怎么切。
第一步:先编码成字节
现代分词器通常从 Unicode 字节 出发,而不是直接操作字符。把任意文本 UTF-8 编码后,得到一个字节序列,字节数固定为 256 种——这样任何字符串都能表示,不会出现“没见过的字符”这种尴尬。
但只用 256 个字节会把常见单词切得太碎(比如 “the” 变成 3 个 token),序列太长,计算成本高。于是需要一个“把常见字节序列合并成更大单元”的算法。
字节对编码(BPE)
字节对编码(Byte Pair Encoding,BPE) 是最主流的算法。它的思想非常朴素:迭代地把语料中出现次数最多的相邻符号对合并成一个新符号,直到词表达到目标大小。
训练过程:
- 初始词表 = 256 个字节;
- 统计语料中每一对相邻符号的出现次数;
- 把次数最多的那一对合并为新符号,加入词表;
- 重复 2-3 步,直到词表大小(比如 5 万~20 万)。
例如对语料 "aaabdaaabac",若 aa 出现最多,先合并 aa → Z,得到 ZbdZbdac;再继续合并下一对,以此类推。
使用过程(编码一段新文本):
- 文本 → UTF-8 字节序列;
- 按照训练时记录的合并规则,按优先级从高到低依次套用;
- 得到最终的 token 序列,再查表变成整数 ID。
BPE 的工程细节
真实实现里有不少“魔鬼细节”,它们不是理论问题,而是工程折衷:
- 预切分(pre-tokenization):先用正则把文本粗切成“词”级别的片段(比如按空格/标点切开),BPE 只在每个片段内部进行。否则
dog.和dog!里的dog学不到一起,还会出现跨空格、跨行的怪异 token。 - 数字处理:GPT-2 把数字按单个数码切分(
1234 → 1|2|3|4),Llama 3 则把数字切成最多 3 位一组。这直接影响模型的算术能力。 - 特殊 token:
<|endoftext|>这类控制符号不参与 BPE 合并,直接映射到保留 ID。 - 压缩率与安全:极端 token(如某些多字节序列)可能引发行为异常,工业分词器会做检测和清洗。
词表大小的权衡
| 词表大小 | 优点 | 缺点 |
|---|---|---|
| 小(如 3 万) | 嵌入矩阵小,省参数 | 序列更长,注意力计算更贵 |
| 大(如 20 万) | 序列短,语义单元完整 | 嵌入层参数多,低频 token 学不好 |
经验法则:现代模型普遍在 10 万~25 万 这个量级(如 Llama 3 为 128000),并倾向于“宁可词表大一点,把训练序列压短”,因为计算瓶颈往往在序列长度上。
小结
- 语言模型 = 对“下一个词元”的概率建模,自回归地生成文本。
- 完整流水线:数据 → 分词 → 训练 → 后训练 → 评估,本课程逐个击破。
- 分词从 UTF-8 字节出发,用 BPE 迭代合并高频对;预切分、数字切分等细节会真实影响模型能力。