由于要做微博短文本情感分析方向的毕设,而中文分词是NLP方面的基础,也是核心,从一定程度上决定了NLP的水平。现在就近期所学的中文分词做个基本介绍。
当下的中文分词,主要有3种方式,一是基于字符串匹配的机械分词,二是基于句意理解的理解性分词,三是基于统计模型的统计分词。但纵观各分词器的实际做法,是要将其中的两种或三种相结合才能达到相对理想的结果。只运用其中一种的分词的话,所成的分词器是肯定不理想的,但这三者之中第一种机械分词方法,基本上是分词当中都要采用的分词方式,它是传统的也是当下新的分词方法的基础。
下边就近期做的两个基于二分查找和双数组实现的trie树的纯机械分词做简要介绍,如果有同志对这两种机械分词感兴趣的话,可以上我已经开源到github中直接下载并导入myeclipse中自己试着学习和开发,地址为:https://github.com/erliang20088 ,现有2个分词项目,另一个是针对二分分词做的一个标红组件,这三个部分都可以直接使用或是打成jar后当组件使用。
trie,全拼为retrieve,即检索的意思,trie树即retrieve树的意思,它是对检索特别高效的一种树的变形,像哈夫曼树也是树的一种变形应用。它的检索效率高,一般都要结合确定型有限状态自动机(DFA,Determinstic Finite Automatic) ,即随着在树中找到的节点的状态的转移来确定是不是找到了一个词,一般要定义几中状态来标志树中某些关联节点的状态转移,如词前缀、已成词、词的中间状态(即是一个词的结尾,是另外一个词的中间,如中国、中国人,此’国‘字就是这种状态)。在本系统中用0,1,2分别标记之。trie树的demo图如下:
词典中有:中国、中国人、中国心、中央、中天、天下、你,共6个词。注意它们标注的状态0,1,2各自代表的含意。
对于每个char字符,都对应一个唯一的int类型的整型值,当然也包括ascii码中所有值。对于每个词的首字符都可以以O(1)复杂度在root的首字的数组集合中找到。但是对于要查找的"中间“这个词是否在词典中如何查找呢?
(1)基于二分查找实现的trie树
在词典构建成trie树的过程中,像中国、中央、中天后的”国、央、天“三字都会顺序存放在”中“节点的后边,如刚刚说到的每个char都对应一个int类型的整型值,从中后边无后续节点,到先后加入中国、中央、中天这样的词,都是按照它们对应的整型值的大小二分查找后顺序放下,每次再有值插入如上循环即可。
所以当有查询过来的时候,如中间,先找到”中“字,”间“就会在”国、央、天“三字中二分查找,一看没有,就说中间不是一个词。
如上为二分查找的简要过程。
(2)基于双数组实现的trie树
通过二分实现的trie树很明显,查找的时间复杂度在O(log2 N)的复杂度,而对于查找最快的莫过于数组的按index查找,时间复杂度为O(1)。
双数组构建trie树的步骤如下:
(a) 将词典中的词进行按首字符分组,例如分成M组。
(b) 将这m组分别处理,即一组一组的处理加入到trie树中。
(c)首先初始化两个对象数组,base[n],check[n].base中存储的是trie树的每个节点的所有信息。check存储的是当前节点的上个节点的位置。由于是数组的组织形式,记录了上个节点的位置,就可以直接找到这个词的上个节点了。主要是在查询的时候用它来判断是不是真正的前后连接关系。
(d)下面以组M1为例为描述插入规则,其它组也是如此进行。
以刚刚的词典中的6个词进行分组,M1(中国、中国人、中国心、中央、中天),M2(天下),M3(你)共3组。下边对M1组进行处理。
”中“字对应的int类型的整型值,假设为20030,也可以自定义它对应的整型值,只要保证它的整型值的唯一性即可。这样中字可以直接插入root的首字符数组集合中,以root[20030]的形式表示,说明root中的第20030个位置已经有值了,下面要讲清以什么规则来为这个位置上的对象定值。
这个位置上的值主要是来确定跟它在一个词序列的下个字的位置的,原则就是这个位置上的值加上它的后续字符的整型对值的值必须是唯一的,此时的位置的唯一性是指该组中的该字符的它的所有的后续第一个字符值的组合都要是唯一的,否则就会导致一个字把另外一个字给掩没掉。 这样就保证了在查询词AB是否是一个词的时候,可以根据找到的(首字符A所在位置的值+B的整型值)定位到trie树中的A字符对应的下个链接字符的值,若是null或不是B则说明AB不是一个词,若是B的话还要再查看下它对应的check的值是否为A确定此B是不是AB组合构建出来的。上边是插入第1个字符的时候处理过程。当插入第2个字符的时候,也要对以第2个字符为首的词序列分组(因为第一个字符是一样的),这样又会产生一批新的分组,然后再分成一组一组的形式的构建到trie树中,当第3个字符的时候依然如此,即M1产生,M1,M2,M3...Mn,当然处理M1的时候,又产生了M11,M12,M13...M1n,然后处理M11,当处理M11的时候又产生M111,M112...M11n,然后处理M111......当然这是建立在有M1+M11+M11这样的词序列的情况,如果词为中国,最多只需要处理2次,如果为中国人,则最多处理3次,若为中国人民,则最多处理4次。则中文词语以2字词居于绝大多数,3字或4字词是相对极少的。所以这样递归处理起来还是相当可以的。
以上写的比较多也比较啰嗦,希望有耐心的同学能看懂。 这是在经过7天查资料+5天java编程后,最终实现的一个双数组trie树分词的基本总结。很难用简单的话语描述的特别清楚。也推荐一下2个链接,我是在反复推敲它们的基础之上做出来的,感谢他们的帮助。
http://cidian.iask.sina.com.cn/a/vj74.html , http://www.baike.com/wiki/%E5%8F%8C%E6%95%B0%E7%BB%84trie%E6%A0%91,
http://blog.sina.com.cn/s/blog_5cf4a61d0100yemd.html。
这三分链接再加上我这篇文章的细节描述,估计很多同志能相对容易些搞定。祝君好运。
上周就想写了,一直也没时间,这周末总算写完了,该继续下一步了。