1、中文分词技术是中文自然语言处理的第一项核心技术,在语言理解中,词是最小的语言成分,分词处理过程通过计算机将句子转化为词的表示。自中文自动分词技术被提出以来,目前已经出现了很多方法,可主要归纳为规则分词、统计分词和混合分词三类主要方法。
(1)基于规则的分词是一种机械分词方法,主要是通过维护词典,在切分语句时将语句的每个字符串与词表中的词进行逐一匹配,找到则切分,否则不予切分。
按照切分方式,主要有正向最大匹配、逆向最大匹配和双向最大匹配三种方法。双向最大匹配法是将正向最大匹配法得到的分词结果和逆向最大匹配法得到的结果进行比较,然后按照最大匹配原则,选取词数切分最少的作为结果。
(2)基于统计的分词模型其主要思想是把每个词看作是由词的最小单位的各个字组成的,如果相连的字在不同的文本中出现的次数越多,就证明这相连的字很可能就是一个词。其一般分为两步:首先建立统计语言模型,其次对句子进行单词分词,然后对划分的结果进行概率计算,获得概率最大的分词方式。
(3)基于混合的分词方法
实际上,在实际工程应用中,最常用的就是先基于词典的方式进行分词,然后再用统计分词方法进行辅助。这样能够在保证在词典分词准确率的基础是上,对未登录词和歧义词有较好的识别。
2、结巴分词原理
官方Github上对所用算法的描述为:
(1)基于前缀词典实现高效的词图扫描,生成句子中汉字所有可能成词情况所构成的有向无环图 (DAG);
(2)采用了动态规划查找最大概率路径, 找出基于词频的最大切分组合;
(3)对于未登录词,采用了基于汉字成词能力的 HMM 模型,使用了 Viterbi 算法。
下面逐一介绍:
(1)构造前缀词典
基于Trie树结构实现高效的词图扫描,生成句子中汉字所有可能成词情况所构成的有向无环图(DAG)
jieba 在其源码中有一个 dict.txt 文件,基于这个文件生成一个 trie 树,将需要进行分词的句子生成有向无环图 (DAG),通俗来说就是将需要分词的句子对照已有的 trie 树生成几种可能的区分。
就像上面这样我们将需要分词的句子“有意见分歧”对照已存在的trie树生成了一个DAG,那么可能的路径有:
- 路径1:0-1-3-5 对应切分方案S1:有 / 意见 / 分歧
- 路径2:0-2-3-5 对应切分方案S2:有意 / 见 / 分歧
结巴分词首先会依照统计词典dict.txt构造前缀词典。dict.txt含有近35万的词条,每个词条占用一行,其中每一行有3列,第一列为词条,第二列为对应的词频,第三列为词性,构造前缀词典需要用到前两列。
具体做法为:首先定义一个空的python字典,然后遍历dict.txt的每一行,取词条作为字典的键,词频作为对应的键值,然后遍历该词条的前缀,如果前缀对应的键不在字典里,就把该前缀设为字典新的键,对应的键值设为0,如果前缀在字典里,则什么都不做。
这样等遍历完dict.txt后,前缀词典就构造好了。在构造前缀词典时,会对统计词典里所有词条的词频做一下累加,累加值等计算最大概率路径时会用到。
(2)生成有向无环图(DAG)
用正则表达式分割句子后,对每一个单独的子句会生成一个有向无环图。
具体方式为:先定义一个空的python字典,然后遍历子句,当前子句元素的索引会作为字典的一个键,对应的键值为一个python列表(初始为空),然后会以当前索引作为子串的起始索引,不断向后遍历生成不同的子串,如果子串在前缀词典里且键值不为0的话,则把子串的终止索引添加到列表中。
这样等遍历完子句的所有字后,对应的DAG就生成好了。(子串的键值如果是0,则说明它不是一个词条)
计算最大概率路径,找出基于词频的最大切分组合
查找待分词句子中已经切分好的词语, 对该词语查找该词语出现的频率(次数/总数),如果没有该词(既然是基于词典查找进行的分词,应该是有的), 就把词典中出现频率最小的那个词语的频率作为该词的频率, 也就是说P(某词语) = FREQ.get(‘某词语’,min_freq)
根据动态规划查找最大概率路径的方法, 对句子从右往左反向计算最大概率,即P(NodeN)=1.0, P(NodeN-1)=P(NodeN)*Max(P(倒数第一个词))…依次类推, 最后得到最大概率路径, 得到最大概率的切分组合:
DAG的起点到终点会有很多路径,需要找到一条概率最大的路径,然后据此进行分词。可以采用动态规划来求解最大概率路径。
具体来说就是:从子句的最后一个字开始,倒序遍历子句的每个字,取当前字对应索引在DAG字典中的键值(一个python列表),然后遍历该列表,当前字会和列表中每个字两两组合成一个词条,然后基于词频计算出当前字到句尾的概率,以python元组的方式保存最大概率,元祖第一个元素是最大概率的对数,第二个元素为最大概率对应词条的终止索引。
词频可看作DAG中边的权重,之所以取概率的对数是为了防止数值下溢。有了最大概率路径,分词结果也就随之确定。
(3)对未登录词采用HMM模型进行分词
未登陆词指的是在dict.txt里面没有被记录的词,值得注意的是当我们把dict.txt中的词语全部删除,jieba依然能够进行分词,但是分出来的词大部分长度都为2,其实这个时候使用的就是HMM来进行分词了。当出现没有在前缀词典里收录的词时,会采用HMM模型进行分词。HMM模型有5个基本组成:观测序列、状态序列、状态初始概率、状态转移概率和状态发射概率。分词属于HMM的预测问题,即已知观测序列、状态初始概率、状态转移概率和状态发射概率的条件下,求状态序列。结巴分词已经内置了训练好的状态初始概率、状态转移概率和状态发射概率。
句子会作为观测序列,当有新句子进来时,具体做法为:先通过Viterbi算法求出概率最大的状态序列,然后基于状态序列输出分词结果(每个字的状态为B、M、E、S之一)。
我们可以将中文的词汇按照B(begin–开始的位置)E(end–结束的位置)M(middle–中间的位置)S(singgle–单独成词的位置,没有前也没有后)排列成一个序列,jieba中就是以这种形式来标记中文的词语,举个例子:“深圳”这个词语可以被标记为BE,也就是深/B圳/E,其意义是深是开始的位置,圳是结束的位置,复杂一些的词语“值得关注”会被标记为BMME,也就是开始,中间,中间,结束。
在jieba中对语料进行了训练从而得到了三个概率表,通过训练得到了概率表再加上viterbi算法就可以得到一个概率最大的BEMS序列, 按照B打头, E结尾的方式, 对待分词的句子重新组合, 就得到了分词结果。
例如我们有一个待分词的句子“全世界都在学中国话”,这句话可以得到一个BEMS序列 [S,B,E,S,S,S,B,E,S](注意这个序列是一个假设,不一定正确),接下来我们将序列中的BE放在一起得到一个词,单独的S独立出来,那么我们就可以得到这样的一个分词结果“全/S 世界/BE 都/S 在/S 学/S 中国/BE 话/S”这样我们就得到了一个较为准确的分词结果。
3、总结
通过上面的三步我们就得到了尽可能正确的分词结果,我们来总结一下。
在上面的文章中我们了解了为什么在中文自然语言处理中需要分词,并且亲自动手实践了jieba分词器,我们发现其在不同的场景中可以使用不同的分词模式,并且还可以添加新词,在使用jieba分词器的基础上我们进一步了解了其实现的原理,感受到了jieba分词器在设计以及实现上的可靠性。