您的位置:首页 > 其它

mmseg分词算法及实现

2016-06-30 11:23 218 查看


一、分词方法

关于中文分词 参考之前写的jieba分词源码分析 jieba中文分词。 

中文分词算法大概分为两大类:

一是基于字符串匹配,即扫描字符串,使用如正向/逆向最大匹配,最小切分等策略(俗称基于词典的) 

基于词典的分词算法比较常见,比如正向/逆向最大匹配,最小切分(使一句话中的词语数量最少)等。具体使用的时候,通常是多种算法合用,或者一种为主、多种为辅,同时还会加入词性、词频等属性来辅助处理(运用某些简单的数学模型)。 

这类算法优点是速度块,时间复杂度较低,实现简单,文中介绍的mmseg属于这类算法。

二是基于统计以及机器学习的分词方式(非词典方法) 

一般主要是运用概率统计、机器学习等方面的方法,目前常见的是CRF,HMM等。 

这类分词事先对中文进行建模,根据观测到的数据(标注好的语料)对模型参数进行估计(训练)。 在分词阶段再通过模型计算各种分词出现的概率,将概率最大的分词结果作为最终结果。 

有关HMM可以参见之前写的 jieba HMM。CRF分词可以参考 这里。 

这类算法对未登录词识别效果较好,能够根据使用领域达到较高的分词精度,但是实现比较复杂,通常需要大量的前期工作。


二、mmseg算法

下面介绍下mmseg算法:


mmseg算法

使用过mmseg算法的童鞋都反映其简单、高效、实用、效果还不错,参考论文 mmseg, 

其主要思想每次从一个完整的句子里,按照从左向右的顺序,识别出多种不同的3个词的组合,然后根据下面的4条消歧规则,确定最佳的备选词组合。 

选择备选词组合中的第1个词,作为1次迭代的分词结果;剩余的词(即这个句子中除了第一个已经分出的词的剩余部分)继续进行下一轮的分词运算。采用这种办法的好处是,为传统的前向最大匹配算法加入了上下文信息,解决了其每次选词只考虑词本身,而忽视上下文相关词的问题。 mmseg论文中说明了(The
Maximum Matching Algorithm and Its Variants)Maximum Matching Algorithm的两种方法: 

1. Simple方法,即简单的正向匹配,根据开头的字,列出所有可能的结果。比如“国际化大都市”,可以得到: 

国 际 化 

国际化 

国际化 

这三个匹配结果(假设这四个词都包含在词典里)。 

2. Complex方法,匹配出所有的“三个词的词组”(即原文中的chunk,“词组”),即从某一既定的字为起始位置,得到所有可能的“以三个词为一组”的所有组合。比如“研究生命起源”,可以得到 

研_究_生 

研_究_生命 

研究生_命_起源 

研究_生命_起源 

这些“词组”(根据词典,可能远不止这些,仅此举例)。


四个规则

消除歧义的规则”有四个,使用中依次用这四个规则进行过滤,直到只有一种结果或者第四个规则使用完毕,4条消歧规则包括: 

1. 备选词组合的长度之和最大(Maximum matching (Chen & Liu 1992)); 

2. 备选词组合的平均词长最大(Largest average word length (Chen & Liu, 1992)); 

3. 备选词组合的词长变化最小(Smallest variance of word lengths (Chen & Liu, 1992)); 

4. 备选词组合中,单字词的出现频率统计值最高(取单字词词频的自然对数,然后将得到的值相加,取总和最大的词)(Largest sum of degree of morphemic freedom of one-character words)。 
下面分别举例说明一下4个规则:

备选词组合的长度之和最大(Maximum matching (Chen & Liu 1992)) 

有两种情况,分别对应于使用“simple”和“complex”的匹配方法。对“simple”匹配方法,选择长度最大的词,用在上文的例子中即选择“国际化”; 

对“complex”匹配方法,选择“词组长度最大的”那个词组,然后选择这个词组的第一个词,作为切分出的第一个词,如上文 的例子中即“研究生_命_起源”中的“研究生”,或者“研究_生命_起源”中的“研究”。 

对于有超过一个词组满足这个条件的则应用下一个规则。

备选词组合的平均词长最大(Largest average word length (Chen & Liu, 1992)) 

经过规则1过滤后,如果剩余的词组超过1个,那就选择平均词语长度最大的那个(平均词长=词组总字数/词语数量)。比如“生活水平”,可能得到如下词组: 

生_活水_平 (4/3=1.33) 

生活_水_平 (4/3=1.33) 

生活_水平 (4/2=2) 

根据此规则,就可以确定选择“生活_水平”这个词组。 

对于上面的 “研究生命起源” 结果:”研究生_命_起源“ “研究_生命_起源” 利用rule2有 

研究生_命_起源 (6/3=2) 

研究_生命_起源 (4/3=2) 

可见这条规则没有起作用,因为两者平均长度相同。论文中也有说明: 

This rule is useful only for condition in which one or more word position in the chunks are empty. When the chunks are real three-word chunks, this rule is not useful. Because three-word chunks with the same total length will certainly have the same average
length. Therefore we need another solution.

备选词组合的词长变化最小(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) 

于是通过这一规则选择“研究_生命_起源”这个词组。

备选词组合中,单字词的出现频率统计值最高(Largest sum of degree of morphemic freedom of one-character words) 

这里的degree of morphemic freedom可以用一个数学公式表达:log(frequency),即词频的自然对数(这里log表示数学中的ln)。这个规则的意思是“计算词组中的所有单字词词频的自然对数,然后将得到的值相加,取总和最大的词组”。比如: 

“设施和服务”,这个会有如下几种组合 

设施_和服_务_ 

设施_和_服务_ 

设_施_和服_ 

经过规则1过滤得到: 

设施_和服_务_ 

设施_和_服务_ 

规则2和规则3都无法得到唯一结果,只能利用最后一个规则 第一条中的“务”和第二条中的“和”,从直观看,显然是“和”的词频在日常场景下要高,这依赖一个词频字典和的词频决定了的分词。 

假设“务”作为单字词时候的频率是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)。

这个四个过滤规则中,如果使用simple的匹配方法,只能使用第一个规则过滤,如果使用complex的匹配方法,则四个规则都可以使用。实际使用中,一般都是使用complex的匹配方法+四个规则过滤。(实际中complex用的最多)。 
通过上面的叙述可以发现MMSEG是一个“直观”的分词方法。它把一个句子“尽可能长” && “尽可能均匀”(“尽可能长”是指所切分的词尽可能的长)的区切分,这与中文的语法习惯比较相符,其实MMSEG的分词效果与词典关系较大,尤其是词典中单字词的频率。可以根据使用领域,专门定制词典(比如计算机类词库,生活信息类词库,旅游类词库等,欢迎体验我狗的 细胞词库 ),尽可能的细分词典,这样得到的分词效果会好很多。 
总的来说 MMSEG是一个简单、可行、快速的分词方法。


三、mmseg分词算法实现

有关mmseg的分词策略通过上面的介绍,大家有一定的认识了,其**主要**思想就是每次从一个完整的句子里,按照从左向右的顺序,识别出多种不同的3个词的组合(chunk),然后根据mmseg的4条消歧规则,确定最佳的备选词组合,选择备选词组合中的最佳词(即第1个词),作为1次迭代的分词结果;剩余的词(即这个句子中除了第一个已经分出的词的剩余部分)继续进行下一轮的分词运算。可以仔细阅读mmseg的论文,写的挺详细的。指导算法思路,下面来看下实现过程:
memseg分词所需要的工作分为下面几点:
转码,string转换成utf16( unicode),以及逆转换。 

关键部分,是分词的前提。因为每次进行分词前,都需要将string decode成utf16(unicode),分词完要输出的时候又需要将unicode encode成string。 因为在分词算法中,对于句子是按一个字一个字来计算和分词的。所以转换成unicode是完成分词算法的必要前提。转码部分利用了StringUtil.hpp的转码模块。
trie字典树 

将词库字典转换成trie树,从而可以进行高效查找。实现起来利用c++的数据结构unordered_map即可实现,详细参见代码。 

mmseg实现起来采用了{.h, .cpp} and {.hpp} 两种组织方式,为什么采用hpp?

mmseg的主要目录结构如下: 
mmseg 

├── build : 生成*.so 动态链接库 

├── data :词典数据 

└── src : 实现源码 

└── util : 字符串处理通用库 


代码采用c++11实现(g++ version >= 4.8 is recommended),已有详细注释,不在敷述,请见源码。 
https://github.com/ustcdane/mmseg 。
使用方法 

在主程序中 如main.cpp 
如果使用hpp格式的文件 即#include “src/Mmseg.hpp”,编译方式为: 

g++ -Ofast -march=native -funroll-loops -o mmseg -std=c++11 main.cpp
如果使用hpp格式的文件 即#include “src/Mmseg.h”,编译方式为: 

g++ -Ofast -march=native -funroll-loops -o mmseg -std=c++11 main.cpp src/Mmseg.cpp 

另外为了能够方便的在其他地方使用mmseg分词,也可以把这部分代码编译成*.so的动态链接库,进入目录build , 里面有已经写好的Makefile文件 直接make 即可生成 libmmseg.so的 动态链接库文件,从而方便在其它程序中调用。

注意 为了更清楚的看mmseg的过程,我分别在 Mmseg.cpp 和 Mmseg.cpp中定义了宏 

#define DEBUG_LEVEL , 如果不想看计算过程可以直接注释掉这一行代码。


四、测试结果

进入mmesg ,运行命令,编译main.cpp,生成mmseg可执行文件。

g++ -Ofast -march=native -funroll-loops -o mmseg -std=c++11 main.cpp

运行mmseg即可测试,在我的机器上测试结果如下。



源码见: https://github.com/ustcdane/mmseg ,实现过程参考了 jdeng 关于mmseg的代码。艾玛,搞了一天终于写完。


参考

http://technology.chtsai.org/mmseg/
http://bjzhkuang.iteye.com/blog/1988127
https://github.com/jdeng/mmseg   
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  分词 mmseg