理解 Tokenization:为什么需要分词

这堂课聊一个很基础、但被很多人忽略的问题:为什么要分词?

说白了,计算机不认识“句子”。你给它一句“我爱北京天安门”,它看到的是 7 个字符,不是 3 个词。没有分词,模型就只能靠字符级序列去猜语义——你想想看,字符级的上下文窗口多长才能学到“天安门”是一个整体?所以,分词是所有 NLP 任务的起跑线。我记得刚入行那年,在一个中文情感分析项目里,我直接跳过分词,把字符序列扔给 LSTM,结果指标奇差。后来才知道,分词这一步错了,后面的工作全是白费。

第一道关卡:从句子到词汇

自然语言处理的核心是处理词汇。不管任务是什么,模型都得先知道“词”在哪。

中文没有天然空格分隔,所以中文分词本身就成了一道坎。英文虽然有空隙,但也要处理标点粘连、缩写(如 doesn't 拆成 does + n't)、 复合词等。就拿英文来说:

  • "New York" — 拆成 ["New", "York"] 还是合并 ["New York"] ?
  • "playing" — 拆成 ["play", "ing"] 还是保持 ["playing"] ?

不同选择会生成完全不同的词汇表。模型学习的是“词”的分布,如果连词的边界都不一致,学出来的表示也是混乱的。我在项目中就踩过这种坑:同一个词在训练集和测试集里的切分不一致,导致准确率直接掉 10%。

我的习惯: 对于中文任务,我至少会用 jieba 或 pkuseg 先跑一遍,并且手工维护一个自定义词典,避免专有名词被切开。对于英文,我倾向使用子词分词器(如 BPE),因为能同时处理形态变化和未登录词。

分词的三个战场:词边界、形态变化、未登录词

分词这件事听上去简单,实际落地时有三座大山:

  1. 词边界不清晰: 比如“武汉市长江大桥”,如果直接按最大匹配切,会切成“武汉市/长江大桥”;但有些人也能理解为“武汉/市长/江大桥”。
  2. 形态变化(词形还原): 英文的 went → go, better → good。如果你不做归一化,词汇表会爆炸。
  3. 未登录词(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 个百分点。

警告: 不要默认“预训练模型自带分词器就万事大吉”。不同语言、不同领域最好手动校验分词结果。我在项目中就见过中文文本被 BERT 的 tokenizer 切成残缺字的情况,比如“人工智能”被切成了“人”、“工”、“智”、“能”——你说模型能学到什么语义?

分词的现实影响:词汇表大小与 OOV 暴击

不同的分词方式直接影响词汇表大小和 OOV 率。我整理了一份之前项目中的实测数据,供你参考:

分词方式词汇表大小OOV 率(测试集)模型参数量(估算)
词级(完整词汇)~500K6.2%~50M(仅词嵌入)
字符级~5K0.0%~0.5M
子词级(BPE 32K)~32K0.1%~3.2M

看到没?词级分词让词汇表暴涨,OOV 依然存在;字符级虽然 OOV 为零,但序列长度翻了好几倍;子词级恰到好处地平衡了两者。所以现在几乎所有主流模型(、BERT、LLaMA 等)全部使用子词分词。

嗯,到这里你应该明白了:分词不是简单的预处理,而是直接决定 NLP 系统性能的底层工程。跳过它,就等于打地基时忽略混凝土配比——上面盖的楼越漂亮,越容易塌。

Tokenization 在 NLP 流程中的位置与作用 原始文本 文本预处理 分词 / 子词 (BPE / WordPiece) 词汇表 & 索引 词嵌入层 下游任务(分类、生成、翻译、问答……) Tokenization 阶段 向量化阶段 分词的质量直接影响整个 NLP 管道。 跳过它,寸步难行。

最后提一句:分词的颗粒度选择不要迷信一个万能答案。如果你是做机器翻译,子词分词几乎是标准配置;但如果你做命名实体识别,太细的切分反而会破坏实体边界。我在金融领域的实体识别项目中,就发现中文数字实体(如“五千三百二十一”)被 BPE 切成“五”、“千”、“三百”、“二十一”,导致实体边界直接断裂。后来我在 BPE 词表里加了数字实体规则,效果才恢复正常。

记住:分词没有银弹。你只有理解它的原理,才能在不同任务中做对选择。