Jieba 分词原理和应用
Jieba 分词工具
Jieba 是一款使用 Python 实现、基于统计的中文分词工具。由于使用方式简单,且分词效率较高,在中文分词领域较为流行。目前主要的分词算法及其区别在于:
分词算法 | 类型 | 思路 |
---|---|---|
正向最大匹配 | 基于词典匹配 | 从前向后,持续扫描,尝试匹配词典中的词 |
逆向最大匹配 | 基于词典匹配 | 与正向最大匹配类似,但分词顺序从后向前进行,在中文领域结果更准确 |
最大概率路径 | 基于统计 | 相邻的字出现次数越多,越可能是组成词。统计大量语料中的词频,对于输入找出基于词频的最大切分路径 |
Jieba 目录结构
- extra_dict/:额外的词典
- stop_words.txt:默认中英停词表
- jieba/
- analyse/:实现关键词提取算法
- idf.txt:IDF 词典
- textrank.py:基于 TextRank 算法的关键词提取
- tfidf.py:基于 TF-IDF 算法的关键词提取
- finalseg/:实现基于 HMM 的成词算法
- __init__.py:实现 Viterbi 算法
- prob_emit.p, prob_emit.py:发射概率
- prob_start.p, prob_start.py:开始概率
- prob_trans.p, prob_trans.py:转移概率
- lac_small/:Paddle 相关分词模型
- posseg/:词性标注模型
- __init__.py:实现分词核心逻辑
- __main__.py:提供命令行工具
- _compat.py:主要是提供 Python 2/3 代码兼容能力
- dict.txt:默认用于分词的词典
- analyse/:实现关键词提取算法
该目录结构基于 Jieba v0.42.1 版本,以下各种结果也基于该版本。
Jieba 分词的基本原理与过程
Jieba 分词的原理是根据前缀词典对输入文本进行扫描,得到所有可能的切分(以 DAG 表示),然后通过动态规划算法,计算得到最大概率路径以得到最终切分。与基于词典最大匹配的分词方法不同,Jieba 是基于概率统计实现的,基础分词相关代码位于 jieba/__init__.py 中。
初始化前缀词典
基于词频统计的词典构造前缀词典(字符串到词频的映射),原始该词典至少包含词、词频(可省略)和词性标注(可省略)。示例见 jieba/dict.txt,词、词频、词性中间使用空格分隔。
Jieba 中支持自定义词典,使开发者可以使用特定领域的词典,或根据现有词典进行微调。
前缀树的实现并不使用 Trie 数据结构,而是使用哈希表实现,因为 Python 中实现 Trie 空间效率较低。具体来说,对于词表中一个长度为 $n$ 的词,遍历其从起始位置开始的 $n$ 个子串,如果该子串在词表中不存在,则在哈希表中加入该子串,并将对应的值(即频次)设置为 0。这一实现,使得前缀词典比原始词表大小增加了 42.7%。前缀树构建过程的实现见 gen_pfdict。
Jieba 将前缀树的实现从 Trie 改为前缀词典的背景见不用Trie,减少内存加快速度;优化代码细节。
生成 DAG 词图
根据前缀词典,就可以对输入文本进行扫描,得到所有可能的切分。
所有可能的切分可以使用有向无环图(DAG)表示,具体实现为字典,其键为词的开始位置,值为列表,表示当前位置词所有可能的结束位置。切分过程见 get_DAG 方法。
查找最大概率路径
在得到所有可能切分方式构成的 DAG 图后,其中从文本起始位置到结束位置可能有多条路径,因此需要计算出最大概率路径,即最优分割。
最大概率路径的求解需要使用动态规划方法求解,为避免概率乘积造成下溢,将概率乘积转换为对数概率的和。在计算最大概率路径时,Jieba 采用从后向前的方式,每到达一个节点,它后面的节点到终点的最大概率路径 route
就已经算出来。最后,在从起点遍历 route
得到最大概率路径。计算过程见 calc 和 cut_DAG_NO_HMM。
Jieba 中查找最大概率路径的实现采用从后向前计算的方式,是其 DAG 的数据存储结构使然,其结果与从前向后计算的结果完全一致。
分词策略
分词模式
分词模式 | 示例 | 说明 |
---|---|---|
全模式 | jieba.lcut("乒乓球拍卖完了", cut_all=True) | 把句子中所有可以组成词的词语都扫描出来,速度快,但不能解决歧义问题。在得到 DAG 图后,即得到了全模式的结果。 |
精确模式 | jieba.lcut("乒乓球拍卖完了") | 试图将句子最精确地切开,适合文本分析。在全模式的结果上,查找最大概率路径,即得到了精确模式的结果。 |
HMM 模式 | jieba.lcut("乒乓球拍卖完了", HMM=True) | 使用 HMM 成词模型,尝试合并长度为 1 的词。 |
搜索引擎模式 | jieba.lcut_for_search("乒乓球拍卖完了") | 在精确模式的基础上,对长词再次切分,适合用于搜索引擎分词。在精确模式的结果上,对长度超过 2 的词,进行 2-gram 和 3-gram 切分,如果有子词在前缀树中,则也加入返回结果中。 |
Paddle 模式 | jieba.lcut("乒乓球拍卖完了", use_paddle=True) | 与上述基于统计的分词方法不同,Paddle 模式利用 PaddlePaddle 训练的双向 GRU 序列标注模型实现分词。该分词模式要求 jieba 版本在 0.40 以上,一般较少使用。 |
HMM 成词模型
Jieba 中提供了基于 BEMS 序列标注隐马尔可夫(Hidden Markov Model,HMM)成词模型,使用 Viterbi 算法寻找满足观测序列的最优隐含状态序列,具备一定的新词(即未登录词)发现的能力。
具体而言,该 HMM 模型的五元组为:
- 状态值集合:即字在词中的位置 $S$,分为 B(begin)、E(end)、M(middle)、S(single)四种
- 观察值集合:即观察序列 $O$,也就是输入的文本字符串
- 状态的初始分布:即序列起始位置的状态分布 $\pi$,见 finalseg/prob_start.py
- 状态的转移概率矩阵:即状态间的概率转移矩阵 $A$,见 finalseg/prob_trans.py
- 状态产生观察的概率矩阵:即发射概率矩阵 $B$,见 finalseg/prob_emit.py
与成词模型类似,Jieba 中的词性标注功能,也是采用 HMM 模型实现的。
关键词抽取
关键词提取是指将与文档内容最相关的一些词提取出来,这些关键词就可以用来完成文档聚类、分类、自动摘要等一系列任务。Jieba 中实现了两种主要的非监督关键词提取算法,分别是 TF-IDF 和 TextRank。
关键词抽取算法 | 示例 | 基本思想 |
---|---|---|
TF-IDF | jieba.analyse.extract_tags("这是一个很长的、关于如何学习分词算法的文档") | 先分词,TF-IDF 高的作为关键词,IDF 由 Jieba 内置提供,TF 从文档计算而来 |
TextRank | jieba.analyse.textrank("这是一个很长的、关于如何学习分词算法的文档") | 先分词,以固定窗口计算词的共现关系,计算词的 PageRank,得分高的作为关键词 |
Jieba 中使用默认的通用配置,提供领域内的 IDF 词表及停词表可能取得更好的效果。