文章

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:默认用于分词的词典

该目录结构基于 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 得到最大概率路径。计算过程见 calccut_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-IDFjieba.analyse.extract_tags("这是一个很长的、关于如何学习分词算法的文档")先分词,TF-IDF 高的作为关键词,IDF 由 Jieba 内置提供,TF 从文档计算而来
TextRankjieba.analyse.textrank("这是一个很长的、关于如何学习分词算法的文档")先分词,以固定窗口计算词的共现关系,计算词的 PageRank,得分高的作为关键词

Jieba 中使用默认的通用配置,提供领域内的 IDF 词表及停词表可能取得更好的效果。

本文由作者按照 CC BY 4.0 进行授权