1、简介
MMSeg是由台湾学者蔡志浩(Chih-Hao Tsai)于1996年提出的基于字符串匹配(亦称基于词典)的中文分词算法。词典分词应用广、可控、速度快,但也存在无法解决歧义问题,比如,“武汉市长江大桥”是应分词“武汉/市长/江大桥”还是“武汉市/长江/大桥”。基于此,有人提出了正向最大匹配策略,但是可能会出现分词错误的情况,比如:若词典中有“武汉市长”,则原句被分词成“武汉市长/江大桥”。单纯的最大匹配还是无法完美地解决歧义,因而MMSeg在正向最大匹配的基础上设计了四个启发式规则,解决了词典分词的歧义问题。
使用过MMSEG分词算法的人反馈都不错,觉得这个算法简单高效,而且还非常准确。作者声称这个规则达到了99.69%的准确率并且93.21%的歧义能被这个规则消除。
2、MMseg分词算法原理
2.1、基本含义
MMSeg的字符串匹配算法分为两种
- Simple,即简单的正向匹配,根据开头的字,列出所有可能的结果,取最大匹配结果;
- Complex,匹配出所有的“三个词的词组”(即原文中的chunk,“词组”),即从某一既定的字为起始位置,得到所有可能的“以三个词为一组”的所有组合,设计了四个去歧义规则(Ambiguity Resolution Rules)指导分词。
接下我们主要讲Complex的原理。
首先来理解一下chunk,它是MMSeg分词算法中一个关键的概念。Chunk中包含依据上下文分出的一组词和相关的属性,包括长度(Length)、平均长度(Average Length)、标准差的平方(Variance)和自由语素度(Degree Of Morphemic Freedom)。下表中列出了各个属性的含义:
属性 | 含义 |
---|---|
长度(Length) | chuck中各个词的长度之和 |
平均长度(Average Length) | chuck中各个词的长度求平均 |
标准差的平方(Variance) | chuck中各个词的长度求标准差 |
自由语素度(Degree Of Morphemic Freedom) | 各单字词词频的对数之和 |
可能看到上面的一些概念会觉得有些模糊,下面我们通过一个简单的列子加深理解。
其实chunk就是三个词为一组,在chunk确定之后,chunk的各种属性也就随之可以计算出来,比如”长春市长春药店”,我们可以得到很多chunk
长春_市_长春
长_春_市长
长春_市长_春药
长春市_长春_药店
….
词典分解出来的可能更多(假定这些词都在词典中)。上面列举的三个词为一组的就是一个chunk,一旦chunk确定之后chunk的各种属性也就可以随之计算出来。
2.2、四个规则
消除歧义的规则有四个,使用中依次用这四个规则进行过滤,直到只有一种结果或者第四个规则使用完毕,4条消歧规则包括:
- 规则1:取最大匹配的chunk (Rule 1: Maximum matching)
- 规则2:取平均词长最大的chunk (Rule 2: Largest average word length)
- 规则3:取词长标准差最小的chunk (Rule 3: Smallest variance of word lengths)
- 规则4:取单字词自由语素度之和最大的chunk (Rule 4: Largest sum of degree of morphemic freedom of one-character words)
接来下举例进行分析:
取最大匹配的chunk(Maximum matching (Chen & Liu 1992))
有两种情况,分别对应于使用“simple”和“complex”的匹配方法。对“simple”匹配方法,选择长度最大的词,用在上文的例子中即选择“长春市”;
对“complex”匹配方法,选择“词组长度最大的”那个词组,然后选择这个词组的第一个词,作为切分出的第一个词,所以在上文提到的例子中我们选择的chunk为
“长春市_长春_药店”,切分出的词为“长春市”从以上的情况来看貌似simple和complex没啥区别,如果是对“北京大学城”进行分词的话,“simple”第一个词结果为“北京大学”,“complex”第一个词结果为”北京”,正确的分词结果为“北京 大学城”。
对于有超过一个词组满足这个条件的则应用下一个规则。
取平均词长最大的chunk(Largest average word length (Chen & Liu, 1992))
经过规则1过滤后,如果剩余的词组超过1个,那就选择平均词语长度最大的那个(平均词长=词组总字数/词语数量)。比如:“李志博士兵”,经过以上规则过滤后,可能还剩下如下的chunk:李志博_士兵(5/2=2.5)
李志_博士_兵(5/3=1.67)依据此规则就可以确定“李志博_士兵”这个chunk,但是对于上述的“北京大学城”此条规则并未起作用。
- 取词长标准差最小的chunk(Smallest variance of word lengths (Chen & Liu, 1992))
由于词语长度的变化率可以由标准差反映,所以此处直接套用标准差公式即可。比如针对”研究生命起源“这个case,经过上面的规则过滤之后有:研究_生命_起源 (标准差=sqrt(((2-2)^2+(2-2)^2+(2-2^2))/3)=0)
研究生_命_起源 (标准差=sqrt(((2-3)^2+(2-1)^2+(2-2)^2)/3)=0.8165)
- 取单字词自由语素度之和最大的chunk(Largest sum of degree of morphemic freedom of one-character words)
这里的自由语素度可以用一个数学公式表达:log(frequency),即词频的自然对数(这里log表示数学中的ln)。这个规则的意思是“计算词组中的所有单字词词频的自然对数,然后将得到的值相加,取总和最大的词组”。比如:
“设施和服务”,经过上面的规则过滤之后,这个会有如下几种组合设施_和服_务
设施_和_服务
假设“务”作为单字词时候的频率是30,“和”作为单字词时候的频率是100,对30和100取自然对数,然后取最大值者,所以取“和”字所在的词组,即“设施_和_服务”。
也许会问为什么要对“词频”取自然对数呢?可以这样理解,词组中单字词词频总和可能一样,但是实际的效果并不同,比如
A_BBB_C (单字词词频,A:3, C:7)
DD_E_F (单字词词频,E:5,F:5)
表示两个词组,A、C、E、F表示不同的单字词,如果不取自然对数,单纯就词频来计算,那么这两个词组是一样的(3+7=5+5),但实际上不同的词频范围所表示的效果也不同,所以这里取自然对数,以表区分(ln(3)+ln(7) < ln(5)+ln(5), 3.0445<3.2189)。
从上面的叙述来看MMseg算法是将一个句子尽可能长和尽可能均匀的切分,这与中文的语法习惯比较相符。同时,分词效果与词典的关系较大,尤其是词典中单字词的频率。“simple”只是用了上述的第一个规则,其实等同于向前最大匹配算法,“complex”以上的四个规则都是用了。实际使用中,一般都是使用complex的匹配方法+四个规则过滤。在使用MMseg词典分词算法的时候尽量使用领域词典对该领域进行分词,如:计算机类词库、医疗词库、金融词库、旅游类词库等,这样效果会好很多。
总的来说MMseg是一个简单、快速、有效的分词方法。
3、算法拓展
上文基本上将MMseg算法的原理讲解了一遍,但是其实上述的规则并未消除掉所有的歧义,比如:“购买文学艺术作品”按照上述的“complex”模式进行分词可能经过四个规则过滤后还会剩余一下2个chunk:
购买_文学_艺术作品
购买_文学艺术_作品
如果词典中有“文学艺术作品”,那么对于这个语句使用MMseg分词是没问题的。
那么如果词典中没有呢,至少我加载jieba分词的词典中是没有的,是使用该词典进行MMseg分词,经过四个规则过滤之后剩下以上两个chunk。那么如何对以上的两个chunk进行优选呢。受 规则4 的启发,我们是否在以上的规则基础之上加入 第五条规则:
(5) 计算chunk自由语素度,取最大的那个chunk,即
$$max(\sum_{i=0}^{N} log(freq_{w_i})),N=2$$
所以,针对上述例子中,如果使用jieba分词词典,各个词在词典中的信息为:购买(4275)、文学(6890)、艺术作品(3)、文学艺术(3)、作品(9248)
则有:
$$\begin{split}
f(购买||文学||艺术作品){} & =log(freq_{购买})+log(freq_{文学})+log(freq_{艺术作品}) \\
{} & =log(4275)+log(6890)+log(3) \\
{} & =18.397
\end{split}$$
$$\begin{split}
f(购买||文学艺术||作品){} & =log(freq_{购买})+log(freq_{文学艺术})+log(freq_{作品}) \\
{} & =log(4275)+log(3)+log(9248) \\
{} & =18.691
\end{split}$$
则按照规则5,则最后取“购买_文学艺术_作品”这个chunk,就解决了经过4个规则过滤之后的还剩多个chunk的情况。
当然,以上加的这条规则为个人的一个思路,因为在实际中确确实实遇到了这个问题。其实加规则的思路还有很多,比如:
- 直接计算chunk的trigram的概率,因为chunk刚好是三个词组合,缺点是计算量大。
- 依据chunk的第一个词的log(freq),取该值的最大那个chunk,因为chunk过滤完成之后也只是决定了当前的分词结果为哪个词。
其实以上思路的都是从算法规则上添加条件过滤,解决问题的方式还可以从词典入手,比如针对以上的例子加入“文学艺术作品”这个词汇。
拓展中所涉及的内容都是个人的一个理解和遇到问题的一个思路,将其列举了出来,并不为MMseg论文中的内容。如果有不妥之处希望大家批评指正。
4、算法demo实现
1 | #!/usr/bin/python |
上述代码中将jieba分词的词典中的word和freq转换为{word:freq}的json文件,则运行程序的结果为:
3
持股/基金/包含/融通基金a/,/银行存款/,/交易性金融资产
购买/文学艺术/作品
吉林省/长春市/长春/药店/,/我/来到/了/北京/大学城/,/看到/了/北京/大学生/的/技术/和/服务/很/厉害/。/工信处/女干事/每月/经过/楼下/都/要/亲口/交代/,/2015/年/5/月/机器/需要/维护/。/我们/中出/了/一个/汉奸/。
董事长/包含/李志/博士
市销率(ps)
吉林省/长春市/长春/药店
算法原理很简单,以上代码为python的一个简单实现。如果想做成java版本话,这个可以作为一个简单的参照!
5、总结
优点:
MMseg是一个简单快速有效的词典分词算法,无需训练语料,算法效果来说很不错。
有些句子在使用jieba的词典的情况下比jieba的分词结果都要好。比如:“我来到了北京大学城”,使用jieba分词和mmseg分词的结果为:我/来到/了/北京/大学城(MMseg结果)
我/来到/了/北京大学/城(jieba结果)缺点:
有着词典分词的缺点就是对于未登录词无法解决。
其实也存在也使用词典分词也无法解决的反例,比如:把手抬起来,正确的分词是把_手_抬起_来,但经过本算法的规则过滤为把手_抬起_来。
对于未登录词可以使用模型(HMM、CRF、BI-LSTM)来解决。