HanLP分词工具中的ViterbiSegment分词流程


本篇文章将重点讲解HanLP的ViterbiSegment分词器类,而不涉及感知机和条件随机场分词器,也不涉及基于字的分词器由于这些分词器都不是咱们在实践中经常使用的,并且ViterbiSegment也是做者直接封装到HanLP类中的分词器,做者也推荐使用该分词器,同时文本分类包以及其余一些天然语言处理任务包中的分词器也都间接使用了ViterbiSegment分词器。java

今天的文章还会介绍各分词词典文件的使用位置以及做用,相信小伙伴们看了今天的文章应该不会再在github上提出干预自定义不生效的问题了进入正题,本篇的内容比较多,建议收藏后再细读。git

1. 分词器配置变量github

分词器的相关配置定义在Config.java类中,这里咱们将分词相关的全部配置变量列于下表多线程

 

图1.jpg

这种配置类何时实例化呢,不用想确定是分词开始前就会实例化,拿HanLP类中的ViterbiSegment分词类举例。该类的继承关系用以下图所示:ide

 图2.jpg

由继承关系图能够看到,只要实例化ViterbiSegment则首先会执行Segment()初始化,在该方法中实例化分词器配置对象config。这些配置变量都是公有变量,所以能够在ViterbiSegment类实例化为对象后直接在外部修改。那么何时来使用这些配置变量呢,固然是在分词的时候,具体是哪一个类的哪一个方法呢,固然是ViterbiSegment类的List<Term> segSentence(char[] sentence)方法。学习

另外请注意上边的3个类,全部ViterbiSegment的分词方法都集中在这3个类中。spa

2. 词典的使用条件和前后顺序(也介绍分词流程)线程

咱们知道了词典配置变量使用的位置后,就能够肯定每一个词典的使用条件了以及每一个词典的使用顺序对象

1. 词语粗分继承

1)构建词图

对应方法为void generateWordNet(final WordNet wordNetStorage),在此方法中系统使用CoreNatureDictionary.txt文件切分出全部可能的分词路径。此时若是配置变量useCustomDictionary为true,则将CustomDictionary.txt中的词也考虑进来,说明CustomDictionary.txt优先级会高。另外你们能够看到CoreNatureDictionary.txt实际上也充当了隐马词性标注的发射矩阵,里边某些多词性词也列出了词性序列以及各词性对应的频次。

2)用户定制词典干预

若是配置变量useCustomDictionary为true,即须要使用CustomDictionary.txt进行干预,则执行下边对应的方法,不然跳过该步骤。用户词典干预根据是否进行全切分有两种不一样方法:当配置变量indexMode>0时,即系统处于全切分模式时,对应方法为

 

List<Vertex> combineByCustomDictionary(List<Vertex> vertexList, DoubleArrayTrie<CoreDictionary.Attribute> dat, final WordNet wordNetAll),

 

若是indexMode=0,即系统处于普通分词模式,对应方法为

 

List<Vertex> combineByCustomDictionary(List<Vertex> vertexList, DoubleArrayTrie<CoreDictionary.Attribute> dat)。

 

从调用的方法咱们不难看出,全切分时系统会根据CustomDictionary.txt添加分词路径。而普通切分时,系统会根据CustomDictionary.txt合并路径。这也就是为何有的时候明明已经在CustomDictionary.txt中添加了新词却不生效的缘由,由于一旦根据CoreNatureDictionary.txt构建了词图就不会再有新的路径插到已有分词路径中间,此时就去查找并修改CoreNatureDictionary.txt中的相关字或词吧。

 

3)维特比选择最优路径

对应方法为List<Vertex> viterbi(WordNet wordNet),至此就获得了一个粗分的分词结果。须要注意HanLP的Viterbi分词只是用viterbi方法求解最优路径,并非隐马。

3. 数字识别

若是配置变量numberQuantifierRecognize为true,则在粗分结果的基础上进行数字合并操做,不然直接跳过该步。对应方法为

void mergeNumberQuantifier(List<Vertex> termList, WordNet wordNetAll, Config config)。

4. 实体识别

配置变量ner为true时,则须要进行各类实体的识别,继续向下执行。须要注意该变量受其余实体识别变量影响,只要其余任意实体配置变量为true,则ner就会为true。若是ner为false,则跳过下边各项实体识别继续词性标注环节。

1)中国人名识别

执行此步,配置变量nameRecognize必须为true。调用方法为

PersonRecognition.recognition(vertexList, wordNetOptimum, wordNetAll)。人名使用隐马,所以有转移矩阵nr.tr.txt和发射矩阵nr.txt。因为HanLP不提供训练语料,咱们本身也很可贵到有角色标注的语料,所以咱们通常只修改nr.txt文件,删除nr.txt.bin文件后生效。

2)音译人名识别

执行此步,配置变量translatedNameRecognize必须为true。调用方法为

TranslatedPersonRecognition.recognition(vertexList, wordNetOptimum, wordNetAll)。须要注意音译人名的识别没有用隐马,就是匹配分词法。涉及到的词典为nrf.txt,若是用户修改该词典,则须要删除nrf.txt.trie.dat使其生效。

3)日本人名识别

执行此步,配置变量japaneseNameRecognize必须为true。调用方法为

JapanesePersonRecognition.recognition(vertexList, wordNetOptimum, wordNetAll)。须要注意日本人名的识别没有用隐马,就是匹配分词法。涉及到的词典为nrj.txt,若是用户修改该词典,则须要删除nrj.txt.trie.dat和nrj.txt.value.dat使其生效。

4)地名识别

执行此步,配置变量placeRecognize必须为true。调用方法为

PlaceRecognition.recognition(vertexList, wordNetOptimum, wordNetAll)。地名使用隐马,所以有转移矩阵ns.tr.txt和发射矩阵ns.txt。因为HanLP不提供训练语料,咱们本身也很可贵到有角色标注的语料,所以咱们通常只修改ns.txt文件,删除ns.txt.bin文件后生效。

5)机构名识别

执行此步,配置变量organizationRecognize必须为true。调用方法为

OrganizationRecognition.recognition(vertexList, wordNetOptimum, wordNetAll)。注意这里在调用机构名识别以前先进行了一次识别,也就是层叠隐马,而人名和地名的识别就是普通的隐马。机构名的识别使用层叠隐马,涉及的文件有转移矩阵nt.tr.txt和发射矩阵nt.txt。因为HanLP不提供训练语料,咱们本身也很可贵到有角色标注的语料,所以咱们通常只修改nt.txt文件,删除ns.txt.bin文件后生效。机构名的识别须要人名地名识别具备较高准确率。

至此,分词流程已所有介绍了。

还须要注意下边的内容

 

其余没有在系统中使用的词典有

机构名词典.txt

全国地名大全.txt

人名词典.txt

上海地名.txt

现代汉语补充词库.txt

这些词典是对系统中的词典的更新记录,若是你添加了新的人名、地名、机构名能够在这里添加保存。

另外,若是须要添加人名、地名、机构名能够直接在CoreNatureDictionary.txt中添加,最好是3字以上实体,

若是要去掉错误识别的命名实体能够直接在相应的nr.txt,ns.txt,nt.txt中添加。

3. 多线程分词

HanLP的ViterbiSegment分词器类是支持多线程的,线程数量由配置变量threadNumber决定的,该变量默认为1。HanLP做者说ViterbiSegmet分词效率最高的缘由确定也有ViterbiSegment分词器支持多线程分词这个因素。另外因为ViterbiSegment分词器内部所具备的相关命名实体功能,所以这些命名实体识别的效率也会很高。在哪里实现的多线程分词呢,在Segment类的List<Term> seg(String text)这个方法中实现的,须要注意HanLP的多线程分词指的是一次输入了一个长文本,而不是一次处理多个输入文本。

本文分享自 baiziyu 的专栏,正文内容已经作了部分修改,便于你们阅读,欢迎一块儿交流学习!

相关文章
相关标签/搜索