一、为什么要分词?

  1. 将原始文本转为模型可处理的输入
    绝大多数模型(如 RNN、Transformer)都以“词”或“子词”作为基本输入单元,必须先将文本切分成 token,再映射到向量空间。
  2. 降低词表(vocabulary)规模
    特别是子词级别分词(Subword Tokenization),能兼顾表达能力与词表大小,避免出现过多的“未登录词(OOV)”。
  3. 增强模型泛化能力
    将生僻词拆分为常见子词,有助于模型处理新词、拼写错误或多种拼写变体。

二、分词的主要类型

类别 级别 优点 缺点 应用场景
字符级 Character 简单、零 OOV 序列过长、语义信息稀薄 语言模型预训练、某些拼写校正任务
词级 Word 语义完整 OOV 问题严重、词表巨大 传统 NLP 任务(文本分类、命名实体识别)
子词级 Subword 兼顾 OOV 缩减与语义完整 需要预先训练分词模型 现代预训练模型(BERT、GPT、T5 等)

三、常见子词分词算法

1. Byte Pair Encoding(BPE)

  • 核心思想:从字符级开始,反复将最频繁出现的连续字符对(byte pair)合并为一个新符号,直到达到预设合并次数或词表大小。

  • 算法流程

    1. 初始化:将所有词拆成字符序列,并统计所有相邻字符对的频率;
    2. 重复 nn 次:选取频率最高的字符对 (a,b)(a,b),合并为新符号 “ab”,更新所有词的序列与频率;
    3. 最终输出:合并后形成的符号集即为子词词表。
  • 优点:简单高效;能自动学习常见词缀、词根。

  • 缺点:合并过程是贪心的,可能忽略长范围的全局最优。

2. WordPiece

  • 核心思想:类似 BPE,但在合并时最大化语言模型(通常是 unigram LM)的似然。

  • 算法流程

    1. 初始化:同 BPE,以字符为单位;
    2. 每次合并候选子词,评估合并后在语料上根据语言模型的损失(负对数似然)变化,选择对损失贡献最大的合并;
    3. 重复至词表大小目标。
  • 优点:模型驱动,子词更能提升语言建模效果;

  • 缺点:计算开销较 BPE 大。

3. Unigram Language Model

  • 核心思想:直接假设文本由一组子词以 unigram(独立同分布)方式生成,用 EM 算法学习每个子词的概率,并剪除低概率子词直至词表大小。
  • 算法流程
    1. 收集所有可能子词(长度上限);
    2. E 步:计算每个子词在所有切分方案中的后验概率;
    3. M 步:根据后验概率重新估计子词概率;
    4. 剪枝:移除最不重要的子词,直至词表符合大小要求;
    5. 重复 E/M/剪枝。
  • 优点:生成式视角,灵活且精度高;
  • 缺点:训练与推理速度相对较慢。

四、中文分词的特殊性

中文文本没有空格分隔词,因此需要额外的策略:

  1. 基于词典的最大匹配法

    • 正向最大匹配(MM):从左向右尽量匹配长词;
    • 逆向最大匹配(RMM):从右向左。
    • 改进:双向同时匹配并根据最小分词数、单字数等原则选择最佳分词。
  2. 基于统计的隐马尔可夫模型(HMM)

    • 将分词看作序列标注,状态一般为 {B, M, E, S}(词首、中、尾、单字),用已标注语料估计转移与发射概率,通过 Viterbi 解码最优路径。
  3. 基于条件随机场(CRF)

    • 在 HMM 基础上添加更多特征(字 n-gram、数字、标点等),用全局最优的 CRF 进行标注,性能更好。
  4. 深度学习序列标注

    • BiLSTM-CRF、BERT-Softmax、Transformer-CRF 等,将分词当作序列分类/标注问题,性能进一步提升。
  5. 混合方法

    • 结合词典、规则、统计和神经网络,多层次、多策略融合使得分词更精准。

五、主流工具与实现

语言/框架 English Tokenizers 中文分词工具
Python • Hugging Face tokenizers 实现 BPE/WordPiece/Unigram• NLTK word_tokenize• spaCy nlp.tokenizer • jieba• pkuseg• THULAC• HanLP
Java OpenNLP Tokenizer、Stanford CoreNLP IKAnalyzer、Ansj
C++/Rust Hugging Face tokenizers (Rust 库)
# 以 Hugging Face tokenizers 为例:加载 BPE 分词器
from tokenizers import Tokenizer, models, pre_tokenizers, trainers

# 1. 初始化空 BPE 模型
tokenizer = Tokenizer(models.BPE())
tokenizer.pre_tokenizer = pre_tokenizers.Whitespace()

# 2. 训练:传入文本文件列表,设置词表大小
trainer = trainers.BpeTrainer(vocab_size=30000, show_progress=True)
tokenizer.train(files=["corpus.txt"], trainer=trainer)

# 3. 分词示例
output = tokenizer.encode("This is a test sentence for tokenization.")
print(output.tokens)

六、实践要点与建议

  1. 根据任务选择粒度

    • 文本分类、情感分析等可用词级或子词级;
    • 预训练语言模型几乎都用子词级。
  2. 词表大小与 OOV 权衡

    • 词表越大,OOV 越少,但模型体积和计算开销也越高;
    • 通常 2–5 万级别的子词表在多数场景下效果较好。
  3. 规范化与清洗

    • 分词前做统一大小写、标点、特殊符号处理;
    • 对中文可先进行全角转半角、简繁体统一。
  4. 保留或剔除空格/子词前缀

    • 某些实现(如 Hugging Face 的 “##” 前缀)指明子词是否是词中一部分;
    • 在下游任务中要注意处理或移除这些特殊标记。
  5. 多语言混合场景

    • 可先按空格分割粗粒度语言,再对其中每种语言用专用分词器细分;
    • 或使用 Unicode 脚本判断走不同分词流程。
Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐