中文分词简单高效的MMSeg

Posted 人工智能爱好者社区

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了中文分词简单高效的MMSeg相关的知识,希望对你有一定的参考价值。



作者简介:

Treant  人工智能爱好者社区专栏作者

博客专栏:https://www.cnblogs.com/en-heng


最近碰到一个分词匹配需求——给定一个关键词表,作为自定义分词词典,用户query文本分词后,是否有词落入这个自定义词典中?现有的大多数Java系的分词方案基本都支持添加自定义词典,但是却不支持HDFS路径的。因此,我需要寻找一种简单高效的分词方案,稍作包装即可支持HDFS。MMSeg分词算法正是完美地契合了这种需求。


1. MMseg简介

MMSeg是蔡志浩(Chih-Hao Tsai)提出的基于字符串匹配(亦称基于词典)的中文分词算法。基于词典的分词方案无法解决歧义问题,比如,“武汉市长江大桥”是应分词“武汉/市长/江大桥”还是“武汉市/长江/大桥”。基于此,有人提出了正向最大匹配策略,但是可能会出现分词错误的情况,比如:若词典中有“武汉市长”,则原句被分词成“武汉市长/江大桥”。单纯的最大匹配还是无法完美地解决歧义,因而MMSeg在正向最大匹配的基础上设计了四个启发式规则。isnowfy大神的《浅谈中文分词》对于各种主流的分词算法做了精辟的论述。


MMSeg的字符串匹配算法分为两种:

  • Simple,简单的正向最大匹配,即按能匹配上的最长词做切分;

  • Complex,在正向最大匹配的基础上,考虑相邻词的词长,设计了四个去歧义规则(Ambiguity Resolution Rules)指导分词。

在complex分词算法中,MMSeg将切分的相邻三个词作为词块(chunk),应用如下四个消歧义规则:

  1. 备选词块的长度最大(Maximum matching),即三个词的词长之和最大;

  2. 备选词块的平均词长最大(Largest average word length),即要求词长分布尽可能均匀;

  3. 备选词块的词长变化最小(Smallest variance of word lengths );

  4. 备选词块中(若有)单字的出现词自由度最高(Largest sum of degree of morphemic freedom of one-character words)。

这篇文章《mmseg分词算法及实现》(点击“阅读原文”)对于这四个规则做了更为细致的介绍,本文无再赘言了。


2. 实战

MMSeg的Java实现有mmseg4j,本地路径添加自定义词典分词:

String txt = "在一起并发生了中文分词."; // user-defined dictionary parent-path Dictionary dic = Dictionary.getInstance("src\\resources\\dict"); Seg seg = new ComplexSeg(dic); MMSeg mmSeg = new MMSeg(new StringReader(txt), seg); Word word = null; while ((word = mmSeg.next()) != null) {  System.out.print(word + "|"); }

mmseg4j("com.chenlb.mmseg4j" % "mmseg4j-core" % "1.10.0")没有wiki,通过分析源码才知道getInstance方法的路径参数应是父目录,并且词典文件的命名应符合规范:chars.dic(单字词表)、wordsXXX.dic(词长>1词表)。mmseg4j所加载的分词词典为类Dictionary数据成员Map<Character, CharNode> dict,其中Character为词的首字,CharNode是一棵trie树,存储拥有共同前缀(首字)的词。loadWord方法为加载自定义词表。


基于上面的代码分析,封装添加HDFS路径词典的Scala代码如下:

import com.chenlb.mmseg4j.CharNode import org.apache.hadoop.conf.Configuration import org.apache.hadoop.fs.{FSDataInputStream, FileSystem, Path} import scala.io.Source /**  * @author rain  */ object MMSegUtil {  // str[1:-1], the last len(str)-1 characters  def tail(str: String): Array[Char] = {    str.toCharArray.takeRight(str.length - 1)  }  // load user-define word dictionary  def loadWord(path: String, dic: java.util.Map[Character, CharNode]) = {    val fs = FileSystem.get(new Configuration)    val in: FSDataInputStream = fs.open(new Path(path))    Source.fromInputStream(in).getLines()      .filter(_.length > 1)      .foreach { line =>        val cn: CharNode = dic.get(line.charAt(0))        cn match {          case null => dic.put(line.charAt(0), cn)          case _ => cn.addWordTail(tail(line))        }      }  } }

即可在Spark程序中调用分词:

val dictionary = Dictionary.getInstance() MMSegUtil.loadWord(dicPath, dictionary.getDict) val seg = new ComplexSeg(dictionary)

值得指出,ComplexSeg类有List remove操作,因而不适于做成广播变量,不然则报ConcurrentModificationException。推荐的做法,配合RDD的mapPartitions在for yield外层new ComplexSeg;相当于每个Partition都有一个ComplexSeg。


mmseg4j存在分词不准确的情况,比如,『培养并发挥热忱的特性』被分词成『培养/并发/挥/热忱/的/特性』。这是因为mmseg4j的chars词典中,挥 20429的词频高于并 2789(针对于MMSeg的规则4)。基于词典的分词方案的准确性,严重依赖于词典;必须要有好的词典,才会有好的分词结果。


大家都在看








   



回复 免费                获取免费课程

回复 直播                获取系列直播课

回复 Python           1小时破冰入门Python

回复 人工智能         从零入门人工智能

回复 深度学习         手把手教你用Python深度学习

回复 机器学习         小白学数据挖掘与机器学习

回复 贝叶斯算法      贝叶斯与新闻分类实战

回复 数据分析师      数据分析师八大能力培养

回复 自然语言处理  自然语言处理之AI深度学习


以上是关于中文分词简单高效的MMSeg的主要内容,如果未能解决你的问题,请参考以下文章

IT技术交流solr 中文分词 mmseg4j 使用例子

Mmseg中文分词算法解析

[Nutch]Solr配置自定义的中文分词器mmseg4j

mmseg中文分词扩展库发布

全文检索引擎Solr系列——整合中文分词组件mmseg4j

coreseek mmseg分词配置和创建