理解 Tokenization:为什么需要分词
这堂课聊一个很基础、但被很多人忽略的问题:为什么要分词?
说白了,计算机不认识“句子”。你给它一句“我爱北京天安门”,它看到的是 7 个字符,不是 3 个词。没有分词,模型就只能靠字符级序列去猜语义——你想想看,字符级的上下文窗口多长才能学到“天安门”是一个整体?所以,分词是所有 NLP 任务的起跑线。我记得刚入行那年,在一个中文情感分析项目里,我直接跳过分词,把字符序列扔给 LSTM,结果指标奇差。后来才知道,分词这一步错了,后面的工作全是白费。
第一道关卡:从句子到词汇
自然语言处理的核心是处理词汇。不管任务是什么,模型都得先知道“词”在哪。
中文没有天然空格分隔,所以中文分词本身就成了一道坎。英文虽然有空隙,但也要处理标点粘连、缩写(如 doesn't 拆成 does + n't)、 复合词等。就拿英文来说:
"New York"— 拆成["New", "York"]还是合并["New York"]?"playing"— 拆成["play", "ing"]还是保持["playing"]?
不同选择会生成完全不同的词汇表。模型学习的是“词”的分布,如果连词的边界都不一致,学出来的表示也是混乱的。我在项目中就踩过这种坑:同一个词在训练集和测试集里的切分不一致,导致准确率直接掉 10%。
分词的三个战场:词边界、形态变化、未登录词
分词这件事听上去简单,实际落地时有三座大山:
- 词边界不清晰: 比如“武汉市长江大桥”,如果直接按最大匹配切,会切成“武汉市/长江大桥”;但有些人也能理解为“武汉/市长/江大桥”。
- 形态变化(词形还原): 英文的
went→go,better→good。如果你不做归一化,词汇表会爆炸。 - 未登录词(OOV): 训练集没见过的词,怎么办?直接切掉?还是用子词处理?
我当年第一次做英文情感分析时,直接把所有词原样进词表。结果测试句子里一大堆 running、jumped 等变体,模型全变成 OOV。后来改用 BPE,这些问题才解决。
| 语言 | 主要挑战 | 常见分词策略 |
|---|---|---|
| 中文 | 词边界不明确 | 基于词典的最大匹配、HMM、CRF |
| 英文 | 形态变化、缩写、复合词 | 空格分割 + 子词切分(BPE、WordPiece) |
| 德语/韩语 | 长复合词、黏着语 | 子词分词器 + 形态分析器 |
从词到子词:BPE 如何破解 OOV 困境
你可能会问:为什么不直接用字符级序列?
字符级的优点是永远不会 OOV,但它的语义学习效率极低。你想想看,模型要学“-ing”和“过去式”这种规律,字符级序列得靠很深的网络才能抽象出来。而词级分词虽然直观,但遇到没见过的新词就挂了。
子词分词(比如 BPE)就是在两者之间找平衡点。它把常见词保持完整,罕见的词拆成更小的 substring。举个简单的例子:
初始词汇表(仅字符):
['a', 'b', 'c', 'd', ...]
语料中出现频率最高的连续字符对:
"th" 出现 1000 次
"he" 出现 900 次
"the" 出现 800 次
BPE 依次合并字符对:
第一步:合并 "t" + "h" → "th"
第二步:合并 "t" + "he" → "the"
第三步:合并 "th" + "e" → "the"(实际是合并连续字节对)
最终词表包含子词:['the', 'th', 'he', 'a', 'b', ...]
这样做的好处:常见的词(如 the)保持完整;不常见的词(如 theatrical)会拆成 the + atrical。所有词都有表示,OOV 几乎消失。
为什么 BPE 成为事实标准?
- 自动学习词边界,不需要人工词典。
- 词汇表大小可控(通常 30K–50K)。
- 对多语言任务天然友好,共享 subword 单元。
词向量的基石:分词颗粒度决定表示质量
分词不仅仅是为了索引。你往后学词嵌入就会知道:词向量是基于 学的。如果分词不稳定,同样的语义结构出现不同切分,词向量就学不到稳定表示。
举个例子:
- 训练数据:
"机器学习很强大"→ 分词["机器", "学习", "很", "强大"] - 测试数据:
"机器学习技术"→ 分词["机器学习", "技术"](把前两个字合并为一个词)
那么 "机器学习" 作为一个整体,在训练阶段完全没见过,词向量只能随机初始化。这就是“分词不一致”带来的灾难。我自己曾在一个 BERT 微调任务中复现了这个问题:仅仅因为用了不同的分词器,下游任务的 F1 掉了 6 个百分点。
分词的现实影响:词汇表大小与 OOV 暴击
不同的分词方式直接影响词汇表大小和 OOV 率。我整理了一份之前项目中的实测数据,供你参考:
| 分词方式 | 词汇表大小 | OOV 率(测试集) | 模型参数量(估算) |
|---|---|---|---|
| 词级(完整词汇) | ~500K | 6.2% | ~50M(仅词嵌入) |
| 字符级 | ~5K | 0.0% | ~0.5M |
| 子词级(BPE 32K) | ~32K | 0.1% | ~3.2M |
看到没?词级分词让词汇表暴涨,OOV 依然存在;字符级虽然 OOV 为零,但序列长度翻了好几倍;子词级恰到好处地平衡了两者。所以现在几乎所有主流模型(、BERT、LLaMA 等)全部使用子词分词。
嗯,到这里你应该明白了:分词不是简单的预处理,而是直接决定 NLP 系统性能的底层工程。跳过它,就等于打地基时忽略混凝土配比——上面盖的楼越漂亮,越容易塌。
最后提一句:分词的颗粒度选择不要迷信一个万能答案。如果你是做机器翻译,子词分词几乎是标准配置;但如果你做命名实体识别,太细的切分反而会破坏实体边界。我在金融领域的实体识别项目中,就发现中文数字实体(如“五千三百二十一”)被 BPE 切成“五”、“千”、“三百”、“二十一”,导致实体边界直接断裂。后来我在 BPE 词表里加了数字实体规则,效果才恢复正常。
记住:分词没有银弹。你只有理解它的原理,才能在不同任务中做对选择。