📌 关键要点
中文没有天然的空格分隔词语,分词是中文 NLP 的基础第一步。
- 不同分词工具的精度与速度差异显著,选型需按场景决定
- BERT 的 WWM(全词遮盖)需要分词工具识别词边界,分词质量直接影响遮盖效果
- 本文覆盖 5 大主流工具,提供对比表与代码示例,便于实测选型
英文有天然的空格分隔单词:
英文:I like eating apples
→ 天然分词:["I", "like", "eating", "apples"]
中文:我喜欢吃苹果
→ 没有空格!需要工具来切分
中文分词的结果取决于粒度:
"我喜欢吃苹果"
粗粒度(词级): ["我", "喜欢", "吃", "苹果"]
细粒度(字级): ["我", "喜", "欢", "吃", "苹", "果"]
BERT 的 WordPiece 分词器 → 细粒度(字级)
传统分词工具(jieba/LTP)→ 粗粒度(词级)
| 维度 | 中文分词(jieba/LTP) | WordPiece(BERT 分词器) |
|---|---|---|
| 切分粒度 | 词级("苹果"是一个词) | 字级("苹""果"各是一个 token) |
| 目的 | 理解文本结构 | 把文本转成模型能处理的 token 序列 |
| 输出 | 词语列表 | token ID 序列 |
| 是否需要训练 | 部分工具需要(pkuseg) | 已预训练好 |
| 典型用途 | WWM 全词遮盖、文本分析 | 模型输入预处理 |
关键区别:WordPiece 不知道"苹果"是一个词,它只知道"苹"和"果"各是一个 token。而 WWM 需要知道词边界,所以必须借助传统分词工具。
| 属性 | 内容 |
|---|---|
| 开发者 | fxjwind(社区维护) |
| 算法 | 基于前缀词典 + 动态规划(HMM 用于新词发现) |
| 模式 | 精确模式 / 全模式 / 搜索引擎模式 |
| 安装 | pip install jieba |
| 体积 | 约 50MB(含词典) |
| 速度 | 快(约 1.5MB/s) |
| 精度 | 中等 |
import jieba
text = "我喜欢吃苹果"
result = jieba.lcut(text)
print(result) # ['我', '喜欢', '吃', '苹果']
# 三种模式
jieba.cut(text, cut_all=False) # 精确模式(默认)
jieba.cut(text, cut_all=True) # 全模式(所有可能的词)
jieba.cut_for_search(text) # 搜索引擎模式(细粒度)
优点:安装简单、速度快、社区活跃、文档丰富 缺点:精度一般、不支持自定义领域模型、新词识别能力弱
jieba 采用两套算法配合的方式:
第一套:前缀词典 + 动态规划 → 处理已知词(词典里有的词)
第二套:HMM 隐马尔可夫模型 → 处理新词(词典里没有的词)
第一套:前缀词典 + 动态规划
jieba 内置词典 dict.txt,每行格式为:词语<TAB>词频<TAB>词性
# 词典文件位置(以 CondaBYP 环境为例)
D:/B/software/anaconda/envs/CondaBYP/Lib/site-packages/jieba/dict.txt
# 词典规模:349,046 行(约 35 万个词)
# 词典内容示例:
我 328841 r ← 词频最高,常用代词
吃 36799 v ← 动词
喜欢 9783 v ← 动词
苹果 1334 n ← 名词
南京市 2046 ns ← 地名
长江大桥 3858 ns ← 地名
研究 35029 vn ← 名动词
生命 6986 vn ← 名动词
分词时,对一句话找出所有可能的切分方式,用动态规划选出词频乘积最大的路径:
"南京市长江大桥"
所有可能的切法:
切法1: 南京 / 市长 / 长江 / 大桥 → 4个词
切法2: 南京市 / 长江大桥 → 2个词,词频乘积更大
切法3: 南 / 京市 / 长江 / 大桥 → ...
动态规划选出最优路径:南京市(2046) × 长江大桥(3858) → 概率最大
第二套:HMM 隐马尔可夫模型
用于识别词典里没有的新词。HMM 给每个字标注 4 种状态之一:
B = Begin(词开头)
M = Middle(词中间)
E = End(词结尾)
S = Single(单字词)
例:"海启星公司"
词典里没有"海启星",但 HMM 标注:
海(B) 启(M) 星(E) → 识别为一个新的三字词
公(B) 司(E) → 词典里的已知词
HMM 通过大量已分词文本训练,学会了哪些字经常组合成词。
jieba ≈ 90% 查词典 + 10% HMM 猜新词
查词典(主力):
→ 速度快、结果稳定
→ 处理日常文本中 90% 以上的字
HMM(补充):
→ 处理词典里没有的新词
→ 人名、地名、新造词等
大部分日常文本都是靠查词典完成的。HMM 只在词典搞不定时才介入。
如果觉得 jieba 的默认词典不够用,有三种方式扩充:
方法 1:运行时添加(临时)
import jieba
# 添加单个词
jieba.add_word("海启星")
jieba.add_word("多源数据处理平台")
# 删除词
jieba.del_word("海启星")
# 注意:程序退出后就失效了
方法 2:加载自定义词典文件(推荐)
import jieba
# 准备一个自定义词典文件 user_dict.txt
# 格式:每行一个词(可选加词频和词性)
# 海启星
# 多源数据处理平台 100 n
# 广东海启星 50 nt
jieba.load_userdict("user_dict.txt")
方法 3:直接修改 dict.txt(不推荐)
# 直接编辑 jieba 安装目录下的 dict.txt
# D:/B/software/anaconda/envs/CondaBYP/Lib/site-packages/jieba/dict.txt
# ⚠️ 不推荐的原因:
# 1. pip 更新 jieba 时会被覆盖
# 2. 直接修改第三方库文件不优雅
# 3. 不同环境需要重复修改
| 方法 | 持久性 | 推荐度 | 适用场景 |
|---|---|---|---|
add_word() |
临时(程序运行期间) | ★★★ | 快速测试 |
load_userdict() |
持久(文件形式) | ★★★★★ | 生产环境 |
修改 dict.txt |
持久(但不优雅) | ★ | 不推荐 |
以下是对不同场景的实测结果(jieba 4.x 版本):
| 场景 | 输入 | 分词结果 | 评价 |
|---|---|---|---|
| 基础句 | 我喜欢吃苹果 | ['我', '喜欢', '吃', '苹果'] | ✅ 完美 |
| 歧义句1 | 南京市长江大桥 | ['南京市', '长江大桥'] | ✅ 正确 |
| 歧义句2 | 研究生命的起源 | ['研究', '生命', '的', '起源'] | ✅ 正确 |
| 歧义句3 | 乒乓球拍卖完了 | ['乒乓球', '拍卖', '完', '了'] | ✅ 正确 |
| 专业术语 | BERT模型使用Masked Language Model | ['BERT', '模型', '使用', 'Masked', ' ', 'Language', ' ', 'Model', ...] | ⚠️ 中英文混合可处理 |
| 新词 | 海启星公司 | ['海启星', '公司', ...] | ✅ HMM 识别出新词 |
| 繁体简单 | 我喜歡吃蘋果 | ['我', '喜歡', '吃', '蘋果'] | ✅ 可以 |
| 繁体复杂 | 語言技術平臺 | ['語言', '技術平', '臺'] | ❌ 切错了 |
| 文言文 | 学而时习之不亦说乎 | ['学而', '时习', '之', '不', '亦', '说乎'] | ❌ 基本不行 |
结论:jieba 对现代汉语(简体)表现良好,经典歧义句全部正确;繁体简单句可以,复杂词会出错;文言文无法处理。对中文维基百科语料的 WWM 实验完全够用。
| 属性 | 内容 |
|---|---|
| 开发者 | 北京大学自然语言处理实验室 |
| 算法 | 基于 BiLSTM-CRF(序列标注模型) |
| 特色 | 支持多领域模型(新闻、网页、医药等) |
| 安装 | pip install pkuseg |
| 体积 | 约 200MB(含预训练模型) |
| 速度 | 中等(比 jieba 慢) |
| 精度 | 高 |
import pkuseg
# 使用默认模型(新闻领域)
seg = pkuseg.pkuseg()
result = seg.cut("我喜欢吃苹果")
print(result) # ['我', '喜欢', '吃', '苹果']
# 使用特定领域模型
seg_web = pkuseg.pkuseg(model_name="web") # 网页领域
seg_med = pkuseg.pkuseg(model_name="medicine") # 医药领域
优点:精度高、支持领域自适应、可训练自定义模型 缺点:速度较慢、体积较大、需要较多依赖
pkuseg 采用 BiLSTM-CRF 序列标注模型(深度学习):
输入:"我喜欢吃苹果"
Step 1: 字符嵌入(Embedding)
每个字 → 一个向量(如"我" → [0.23, -0.15, 0.87, ...])
Step 2: BiLSTM 提取上下文特征
正向 LSTM:从左到右读 → 捕捉"前面的字"
反向 LSTM:从右到左读 → 捕捉"后面的字"
双向拼接 → 每个字同时看到前后文
Step 3: CRF 层输出标签序列
CRF 保证标签序列合法(如 B 后面不能跟 E)
输出:我(S) 喜欢(B-E) 吃(S) 苹果(B-E)
Step 4: 根据标签切词
S=单字词, B=词首, M=词中, E=词尾
→ ['我', '喜欢', '吃', '苹果']
pkuseg 的独特之处是多领域模型:针对不同领域(新闻/网页/医药)训练不同的模型,因为不同领域的用词习惯不同。
| 属性 | 内容 |
|---|---|
| 开发者 | 哈尔滨工业大学 SCIR(社会计算与信息检索研究中心) |
| 算法 | 基于深度学习(BiLSTM-CRF / Transformer) |
| 特色 | 功能全面:分词 + 词性标注 + 命名实体识别 + 依存句法分析 |
| 安装 | pip install ltp |
| 体积 | 约 1GB+(含预训练模型) |
| 速度 | 较慢 |
| 精度 | 高 |
from ltp import LTP
ltp = LTP("LTP/small") # 加载小模型
# 分词
output = ltp.pipeline(["我喜欢吃苹果"], tasks=["cws"])
print(output.cws) # [['我', '喜欢', '吃', '苹果']]
# 同时做分词 + 词性标注 + 命名实体识别
output = ltp.pipeline(["我喜欢吃苹果"], tasks=["cws", "pos", "ner"])
print(output.cws) # [['我', '喜欢', '吃', '苹果']]
print(output.pos) # [['r', 'v', 'v', 'n']] (代词/动词/动词/名词)
优点:功能全面、精度高、哈工大出品(与 WWM 论文一致) 缺点:体积大、依赖多、速度较慢、学习曲线陡
📌 与哈工大论文的关系:哈工大讯飞联合实验室的 WWM 论文(arXiv:1906.08101)使用的就是 LTP 做中文分词,以识别词边界供全词遮盖使用。
LTP 采用深度学习全栈管线,分词只是其中一环:
LTP 的处理流程(以分词为例):
输入:"我喜欢吃苹果"
Step 1: 字符嵌入(Embedding)
每个字 → 向量表示
Step 2: 编码器(Transformer / BiLSTM)
多层 Transformer 或 BiLSTM 提取深层语义特征
比 pkuseg 的 BiLSTM 更强,因为 Transformer 能捕捉更长距离的依赖
Step 3: 解码器(CRF)
输出 BMES 标签序列
→ 我(S) 喜欢(B-E) 吃(S) 苹果(B-E)
Step 4: 多任务联合
分词、词性标注、NER 共享编码器,各自有解码器
→ 一次推理,同时输出分词+词性+实体
LTP 与 pkuseg 的核心差异:
| 维度 | pkuseg | LTP |
|---|---|---|
| 编码器 | BiLSTM | Transformer / BiLSTM |
| 功能 | 仅分词 | 分词+词性+NER+句法 |
| 模型大小 | ~200MB | ~500MB-1GB+ |
| 上下文理解 | 中等 | 更强(Transformer 长距离依赖) |
| 属性 | 内容 |
|---|---|
| 开发者 | hankcs(何晗) |
| 算法 | 多版本:HanLP 1.x(传统)/ HanLP 2.x(深度学习) |
| 特色 | 功能全面、支持多语言 |
| 安装 | pip install hanlp |
| 体积 | 约 500MB+ |
| 速度 | 中等 |
| 精度 | 高 |
import hanlp
# HanLP 2.x(深度学习版)
hanlp.load(hanlp.pretrained.tok.COARSE_ELECTRA_SMALL_ZH)
# 分词
tok = hanlp.load(hanlp.pretrained.tok.COARSE_ELECTRA_SMALL_ZH)
result = tok("我喜欢吃苹果")
print(result) # ['我', '喜欢', '吃', '苹果']
优点:功能全面、文档优秀、社区活跃、支持多语言 缺点:体积较大、部分功能需要额外配置
HanLP 有两个大版本,算法完全不同:
HanLP 1.x(传统版,Java 实现):
→ 基于词典 + CRF + 规则
→ 类似 jieba 的思路,但加了 CRF 和规则后处理
→ 速度快,精度高
HanLP 2.x(深度学习版,Python 实现):
→ 基于预训练语言模型(如 ELECTRA)+ 任务特定解码器
→ 先用大规模语料预训练一个语言模型
→ 再在分词任务上微调
→ 精度最高,但体积大、速度慢
HanLP 2.x 的核心优势是预训练+微调范式:
Step 1: 预训练语言模型(ELECTRA / BERT)
→ 在大规模中文语料上学习语言的通用知识
→ 模型"理解"了中文的基本规律
Step 2: 在分词任务上微调
→ 用标注好的分词数据微调模型
→ 模型学会把"理解"应用到分词任务上
Step 3: 推理
→ 输入新文本 → 模型直接输出分词结果
| 属性 | 内容 |
|---|---|
| 开发者 | 清华大学自然语言处理实验室 |
| 算法 | 基于 CWS(Chinese Word Segmentation)标准 |
| 特色 | 轻量、速度快 |
| 安装 | pip install thulac |
| 体积 | 约 100MB |
| 速度 | 快 |
| 精度 | 中等偏上 |
import thulac
thu = thulac.thulac()
result = thu.cut("我喜欢吃苹果", text=True)
print(result) # "我/r 喜欢/v 吃/v 苹果/n"
优点:轻量、速度快、清华出品 缺点:功能相对单一、社区活跃度下降
THULAC 采用 SSVM(结构化感知机) + 字符序列标注:
输入:"我喜欢吃苹果"
Step 1: 字符特征提取
每个字提取一组特征:
- 当前字是什么
- 前一个字是什么
- 后一个字是什么
- 当前字的 n-gram
Step 2: 结构化感知机打分
对每种可能的 BMES 标签序列打分
→ 选择得分最高的标签序列
Step 3: 根据标签切词
→ 我(S) 喜欢(B-E) 吃(S) 苹果(B-E)
→ ['我', '喜欢', '吃', '苹果']
THULAC 的特点: - 不用深度学习(无神经网络),用的是传统机器学习 - 模型小(~100MB)、推理快 - 同时输出分词 + 词性标注(一体化设计) - 精度介于 jieba 和深度学习工具之间
| 工具 | 算法类型 | 核心技术 | 是否需要训练 | 时代 |
|---|---|---|---|---|
| jieba | 传统 | 前缀词典 + 动态规划 + HMM | 不需要(词典内置) | 2012 |
| pkuseg | 深度学习 | BiLSTM-CRF | 需要下载模型 | 2018 |
| LTP | 深度学习 | Transformer/BiLSTM + CRF | 需要下载模型 | 2020+ |
| HanLP 2.x | 深度学习 | 预训练语言模型 + 微调 | 需要下载模型 | 2020+ |
| THULAC | 传统 | 结构化感知机 + 序列标注 | 不需要(模型内置) | 2015 |
可以看到中文分词工具经历了两代技术演进: 1. 第一代(传统):jieba(词典+HMM)、THULAC(感知机)——模型小、速度快、精度一般 2. 第二代(深度学习):pkuseg(BiLSTM-CRF)、LTP(Transformer)、HanLP(预训练+微调)——模型大、速度慢、精度高
| 工具 | 开发者 | 算法 | 精度 | 速度 | 体积 | 功能 | 适合场景 |
|---|---|---|---|---|---|---|---|
| jieba | 社区 | 词典+HMM | ★★★ | ★★★★★ | 50MB | 分词 | 快速原型、资源受限 |
| pkuseg | 北大 | BiLSTM-CRF | ★★★★ | ★★★ | 200MB | 分词+训练 | 需要领域自适应 |
| LTP | 哈工大 | 深度学习 | ★★★★★ | ★★ | 1GB+ | 全栈 NLP | 需要完整 NLP 管线 |
| HanLP | hankcs | 深度学习 | ★★★★★ | ★★★ | 500MB+ | 全栈+多语言 | 功能全面需求 |
| THULAC | 清华 | 传统 | ★★★★ | ★★★★ | 100MB | 分词 | 轻量快速 |
WWM(Whole Word Masking,全词遮盖)要求以"词"为单位进行遮盖,而不是以"子词"为单位。这就需要先知道哪些字属于同一个词。
原始句子:我喜欢吃苹果
Step 1: 中文分词(用 jieba/LTP 等)
→ ["我", "喜欢", "吃", "苹果"]
Step 2: BERT WordPiece 分词
→ ["我", "喜", "欢", "吃", "苹", "果"]
Step 3: WWM 遮盖决策
如果要遮盖"苹果"→ 必须同时遮盖"苹"和"果"
因为 Step 1 告诉我们"苹果"是一个词
Step 4: 生成训练样本
input_ids: [我, 喜, 欢, 吃, [MASK], [MASK]]
labels: [-, -, -, -, 苹, 果]
没有中文分词 → 不知道词边界 → 无法做 WWM
| 场景 | 推荐工具 | 理由 |
|---|---|---|
| 快速实验 / 资源受限 | jieba | 安装简单、速度最快 |
| 需要高精度 + 领域自适应 | pkuseg | 支持多领域模型 |
| 需要完整 NLP 管线(分词+词性+NER) | LTP | 功能最全面 |
| 与哈工大 WWM 论文保持一致 | LTP | 论文使用的就是 LTP |
| 轻量 + 快速 | THULAC | 体积小、速度快 |
后续计划对以上工具进行实测对比,评估维度:
测试数据:BYP-RC-03 课题的 processed/corpus.txt(137MB,110 万句)
📖 资料来源: - jieba 官方仓库 - pkuseg 官方仓库 - LTP 官方仓库 - HanLP 官方仓库 - THULAC 官方仓库 - 哈工大 WWM 论文(使用 LTP 做分词)
🔗 相关笔记: - WordPiece算法:BERT的分词之道 - bert-base-chinese词表解剖 - 中文BERT三变体