自然语言处理之:c++中文分词(附源码)

来源:互联网 发布:防御矩阵 觉醒 steam 编辑:程序博客网 时间:2024/06/09 17:29

githup地址:https://github.com/jbymy
一、简介
中文分词是地然语言处理中的最基础的环节,到目前为止已经有不少优秀的分词工具的出现,如“中科院分词”,“结巴分词”等。个人认为在中文分词领域在算法层面上虽层出不穷,但归其根本仍然是大同小异,基于统计的分词算法在根本上并无太大差别,因此我写的这个分词算法在保证高准确性的情况下以实用性,灵活性为主打方向。

二、wordseg分词算法
借鉴结巴分词的思想,采用基于词典的有向无环图算法结合HMM隐马尔科夫模型分词。关于结巴分词已经有非常成熟的版本了,本分词工具中的基础词典也是结巴分词的词典加上自己整理的新词。wordseg分词工具基本思路如下:

  • 用TRIE树存储词典
    Trie树的详细描述详见维基百科,这里有一张百科截图
    Trie数示例
    上图显示的是英文词典的Trie树,中文词典构成Trie数也和上图类似,只不过每一个节点存储的是每个中文汉字的编码(在linux下一般是UTF-8编码,每一个汉字用一个INT型变量表示,具体的转码方式参见UTF-8编码)或者我代码中的func.cpp中的如下代码
    汉字转int16
    该函数实现的功能是将汉字字符串转为 int16 的 vector
    Trie树的每一个节点保存从根节点到该节点是否成词语、词频、词性等信息。
  • 按分割符切割文本
    按照分割符号进行首轮切割,文本处理中一般讲一篇文章处理成一行,因此首轮切割以trie树根节点中未出现的字符进行切割:标点符号,其他字符等,可利用自定义词典制定规则。
  • 对短句进行切分
    1:有向无环图分词
    一个句子的分词路径会形成一个有向无环图,从网上找到的示意图如下:这里写图片描述
    一个句子的分词概率可以看做是每个词语形成的联合概率,每个词语的概率为 :词频/词典总词频
    具体过程
    因此问题转为一个最短路径问题,可参照经典的最短路径算法解决,具体可见seg.cpp。
    2:人名识别模块
    人名识别模块是命名实体识别中的一部分,根据需求就单独提了出来,可以用经典的HMM进行识别。本算法中我是用了400万中文人名训练得到词典。
    3:HMM补充分词
    HMM分词是对语句进行序列标注的过程。给定一个字的序列,找出最可能的标签序列。利用BMES标签来分词的,B(开头),M(中间),E(结尾),S(独立成词)。HMM分词的词典如下:
    这里写图片描述
    这里只显示了前面几行,前四行是状态转移矩阵:BEMS四种状态的转移矩阵,下面是发射矩阵:是BEMS四种状态下发射到当前观测值(汉字)的条件概率(词典为了后续计算方便存储的是中间计算用值而非原始概率)。
    两个矩阵的计算:通过统计已标注训练语料统计字的序列标注情况和状态的二元转移即可得到。
    句子的状态系列可以用维特比算法实现。本分词工具中采用HMM作为词典分词的补充,为了召回未登录命名实体词:如“知春路”等。

三、HMM模型和基于词典分词的异同点

  • 相同点
    都是采用统计的方法:基于词典的DAG分词利用了词频作为每个词的概率,最后计算整句的联合概率;从另一个角度看HMM,也是统计了词语概率分布情况,例如“中国”这个词,P(B)*P(中|B)*P(E)*P(国|E) 就是该词的概率,只不过采用了另外的形式用转移矩阵和发射矩阵来计算。采用同一份训练语料可以同时生成分词词典和HMM矩阵。
    消歧算法:词典分词采用了最短路径算法,HMM分词采用了维特比算法,仔细研究这两种算法不难发现这两种算法是很类似的。

  • 不同点
    词典分词基于的是自定义词典,会更加准确但是对未登录词无法识别,不在词典中的词的概率为零,而HMM则相对模糊,任意两个字之间都能转移概率,像“XX路”这种词语,由于“路”作为“E”的概率很大,很容易被识别为实体词,这对于命名实体识别别具效果。

四、当前分词领域的难点
中文分词发展到今天,就算法层面上来看,各大算法虽别具一格但都殊途同归,各有利弊,对于常规词语效果相差无几。中文分词的瓶颈在于未登录词,放眼到实际业务中,真正对业务效果有影响的是垂直领域类的未登录词,市面上的各种开源分词工具都无法做到,这在业界也是一难题。在具体的业务中往往要通过一些实体词挖掘,新词挖掘方法来补充词典,对于不同的业务,词语的粒度都不一样,因此还需要具体问题具体解决。

五、wordseg分词工具的使用
直接使用程序:下载githup代码,commom, dict, wordseg 放在同一目录下,在wordseg目录下执行make生成可执行文件seg 即可。
./seg 0 ../dict/seg/ inputfile(输入文件) outputfile(输出文件)
分词函数使用示例见 main.cpp
这里写图片描述

2 0
原创粉丝点击