分词技术是NLP领域中十分关键的一部分,无论是使用机器学习还是深度学习,分词的效果好坏直接影响到最终结果。在中文领域中最常用的分词工具是结巴分词(jieba),下面简单的介绍下分词的技术以及jieba的原理和代码分析,主要是添加了一些注释,仅供参考。
中文分词
目前中文分词技术主要分为两类,基于词典的分词方法,基于概率统计的分词方法。
- 基于词典分词
顾名思义,根据已有词典进行分词,类似于查字典。基于词典的分词方法简单、速度快,效果也还可以,但对歧义和新词的处理不是很好,对词典中未登录的词没法进行处理。主要有:
- 正向最大匹配
- 逆向最大匹配
- 双向最大匹配
- 最少分词数分词
2.基于概率统计分词
基于统计的分词方法是从大量已经分词的文本中,利用统计学习方法来学习词的切分规律,从而实现对未知文本的切分。简单的来说就是从已有的大量文本中训练模型,对未知文本进行预测,得到分词结果。主要有:
- HMM
- CRF
- Bilstm-CRF
3.总结
基于词典分词,更有利于根据人们意愿得到想要的分词结果,训练速度快。
基于概率统计模型分词,可以处理未知文本,但需要大量的训练文本才能保证效果。
jieba分词原理及对应代码解析
jieba分词结合了词典和统计学习方法,分词效果快,也能处理未知文本。词典部分是作者找的语料dict.txt(安装jieba后可以在site-packages/目录下找到,大约5M大小),统计学习模型部分是一个训练好的HMM模型。分词过程如下:
- 根据默认词典进行前缀词典初始化
- 基于前缀词典实现高效的词图扫描,生成句子中汉字所有可能成词情况所构成的有向无环图
- 采用了动态规划查找最大概率路径, 找出基于词频的最大切分组合
- 对于未登录词,采用了基于汉字成词能力的 HMM 模型。
词典
默认词典一共包含3列(单词,词频,词性)
初始化
self.FREQ, self.total = self.gen_pfdict(self.get_dict_file())
在初始化函数initialize()有如上代码,用于获得词频和词频总数。其中FREQ是一个dict,存储词频。
前缀词典构建
def
根据gen_pfdict()的代码逻辑,发现FREQ不仅存储了单词及其对应的词频,还有每个单词的前缀子串,如果前缀子串不能作为一个词语(没有出现在词典中)它的词频为0。
生成所有成词情况的有向无环图DAG
def
该函数传入一个句子,并返回这个句子所有可能分词情况的DAG。
如图,用dict存储的图,key表示字符串起点下标,value代表字符串末尾的下表。对“我爱重庆”计算DAG,得到图中结果,分词的所有情况有:我、爱、爱重、重、重庆、庆。然后要做的是从所有可能的分词结果中找到最好的一个。
计算最优概率路径
目标:最大化句子的概率
def
对于较长的句子复杂度是比较高的,所以这里使用了维特比算法(动态规划)计算最优路径,从右往左计算。直接对route进行修改,无需return。route[N]存储以N开头,最优分词的结果末尾字符的下标,即sentence[N:route[N][1]]即为单词。
分词函数
def
代码里使用的是yield而不是return,因此分词结果是一个generator。总结一下,得到的最优分词路径中,主要有两种情况,单个字和单词。
1.对于单词或单个字+单词,直接返回结果。
2.对于连续多个字单独成词的情况,将他们全部存入buf。若buf不是词典中的词,使用HMM模型处理。若buf是词典中的词,证明通过计算的确单独成词效果更好,返回每个字作为词。
_cut_DAG是属于精准模式+HMM,jieba还有另外的模式,详见https://github.com/fxsjy/jieba