
前几天的召开的2019年大数据生态产业大会不知道大家关注到没有,看到消息是hanlp2.0版本发布了。不知道hanlp2.0版本又将带来哪些新的变化?准备近期看能够拿到一些hanlp2.0的资料,如果能顺利拿到的话,到时候分享给大家!今天分享这篇是关于将hanlp封装到web services服务中的。文章的部分内容有修改,阅读室请注意! Apache Axis2简要介绍 Apache Axis2 是一种web services的引擎。支持SOAP和WSDL协议。它有两种语言的实现分别是Java实现的Apache Axis2/Java,和Apache Axis2/C。 Apache Axis2不仅支持SOAP1.1和SOAP1.2协议,同时还支持REST风格的协议。比起Apache Axis1,Apache Axis2更加高效,支持XML格式数据传输等。非常容易嵌入模块插件从而提高安全性和稳定性。例如WS-Security和WS-Addressing。 Apache Axis2的主要特征:(1)快速。(2)内存占用小(3)热部署(4)异步Web Services。(5)支持MEP传输规范(6)灵活扩展(7)高稳定性(8)面向组建的部署(9)在SMTP、FTP传输层协议之上(10)支持WSDL协议,易于构建stubs访问服务(11)方便组合和扩展。 将HanLP封装为web services服务的过程 (1)准备工作 由于axis2要运行于servlet容器所以必须下载tomcat,解压到C:Program Filesapache-tomcat-7.0.92,配置环境变量如下Path=C:Program Filesapache-tomcat-7.0.92bin下载axis2的binary安装包,解压到C:Program Filesaxis2-1.7.9,配置环境变量如下AXIS2_HOME=C:Program Filesaxis2-1.7.9Path=%AXIS2_HOME%bin(2)编写服务类 新建一个service包,并向其中添加服务类 package service; import com.hankcs.hanlp.HanLP; public class AnyService { // 服务接口函数1 public String fun1(String text) { if(text == null) { return ""; }else { return HanLP.XXX(text); } } // 服务接口函数2 public void fun2(String text) { if(text == null) { HanLP.XXX(text); } } }可以定义任意个服务接口函数,函数内部可以实例化任意HanLP的类实例。 (3)编写services.xml 新建一个目录META-INF,并在该目下新建services.xml(文件名不可修改),输入下边内容 <parameter name="ServiceClass"> service.AnyService </parameter> <messageReceivers> <messageReceiver mep="http://www.w3.org/ns/wsdl/in-out" class="org.apache.axis2.rpc.receivers.RPCMessageReceiver" /> <messageReceiver mep="http://www.w3.org/ns/wsdl/in-only" class="org.apache.axis2.rpc.receivers.RPCInOnlyMessageReceiver" /> </messageReceivers> 其中AnyService可以更换成自己想要的服务名称,但是服务类的名称必须与前边定义的服务类的名称一致。 (4)导出为 "任意服务名.aar",存储到C:Program Filesapache-tomcat-7.0.92webappsaxis2WEB-INFservices。 (5)将HanLP配置文件hanlp.properties拷贝到C:Program Filesapache-tomcat-7.0.92webappsaxis2WEB-INFclasses,配置HanLP的data所在目录位置 root=填写你的data所在的目录HanLP会在C:Program Filesapache-tomcat-7.0.92webappsaxis2WEB-INFclasses目录下寻找配置文件,然后读取。 (5)启动tomcat,使用浏览器访问服务 双击C:Program Filesapache-tomcat-7.0.92bin目录下的startup.bat文件。在浏览器中访问服务,地址栏输入 http://localhost:8080/axis2/services/AnyService/fun1?text=任意想出入的内容
本篇文章将重点讲解HanLP的ViterbiSegment分词器类,而不涉及感知机和条件随机场分词器,也不涉及基于字的分词器。因为这些分词器都不是我们在实践中常用的,而且ViterbiSegment也是作者直接封装到HanLP类中的分词器,作者也推荐使用该分词器,同时文本分类包以及其他一些自然语言处理任务包中的分词器也都间接使用了ViterbiSegment分词器。今天的文章还会介绍各分词词典文件的使用位置以及作用,相信小伙伴们看了今天的文章应该不会再在github上提出干预自定义不生效的问题了。进入正题,本篇的内容比较多,建议收藏后再细读。 分词器配置变量分词器的相关配置定义在Config.java类中,这里我们将分词相关的所有配置变量列于下表 这种配置类什么时候实例化呢,不用想肯定是分词开始前就会实例化,拿HanLP类中的ViterbiSegment分词类举例。该类的继承关系用如下图所示: 由继承关系图可以看到,只要实例化ViterbiSegment则首先会执行Segment()初始化,在该方法中实例化分词器配置对象config。这些配置变量都是公有变量,因此可以在ViterbiSegment类实例化为对象后直接在外部修改。那么什么时候来使用这些配置变量呢,当然是在分词的时候,具体是哪个类的哪个方法呢,当然是ViterbiSegment类的List segSentence(char[] sentence)方法。另外请注意上边的3个类,所有ViterbiSegment的分词方法都集中在这3个类中。 词典的使用条件和先后顺序(也介绍分词流程)我们知道了词典配置变量使用的位置后,就可以确定每个词典的使用条件了以及每个词典的使用顺序 词语粗分(1)构建词图 对应方法为void generateWordNet(final WordNet wordNetStorage),在此方法中系统使用CoreNatureDictionary.txt文件切分出所有可能的分词路径。此时如果配置变量useCustomDictionary为true,则将CustomDictionary.txt中的词也考虑进来,说明CustomDictionary.txt优先级会高。另外大家可以看到CoreNatureDictionary.txt实际上也充当了隐马词性标注的发射矩阵,里边某些多词性词也列出了词性序列以及各词性对应的频次。(2)用户定制词典干预如果配置变量useCustomDictionary为true,即需要使用CustomDictionary.txt进行干预,则执行下边对应的方法,否则跳过该步骤。用户词典干预根据是否进行全切分有两种不同方法:当配置变量indexMode>0时,即系统处于全切分模式时,对应方法为 List combineByCustomDictionary(List vertexList, DoubleArrayTrie dat, final WordNet wordNetAll), 如果indexMode=0,即系统处于普通分词模式,对应方法为 List combineByCustomDictionary(List vertexList, DoubleArrayTrie dat)。 从调用的方法我们不难看出,全切分时系统会根据CustomDictionary.txt添加分词路径。而普通切分时,系统会根据CustomDictionary.txt合并路径。这也就是为什么有的时候明明已经在CustomDictionary.txt中添加了新词却不生效的原因,因为一旦根据CoreNatureDictionary.txt构建了词图就不会再有新的路径插到已有分词路径中间,此时就去查找并修改CoreNatureDictionary.txt中的相关字或词吧。 (3)维特比选择最优路径对应方法为List viterbi(WordNet wordNet),至此就得到了一个粗分的分词结果。需要注意HanLP的Viterbi分词只是用viterbi方法求解最优路径,并不是隐马。 数字识别如果配置变量numberQuantifierRecognize为true,则在粗分结果的基础上进行数字合并操作,否则直接跳过该步。对应方法为 void mergeNumberQuantifier(List termList, WordNet wordNetAll, Config config)。 实体识别配置变量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中添加。 多线程分词HanLP的ViterbiSegment分词器类是支持多线程的,线程数量由配置变量threadNumber决定的,该变量默认为1。HanLP作者说ViterbiSegmet分词效率最高的原因肯定也有ViterbiSegment分词器支持多线程分词这个因素。另外由于ViterbiSegment分词器内部所具有的相关命名实体功能,因此这些命名实体识别的效率也会很高。在哪里实现的多线程分词呢,在Segment类的List seg(String text)这个方法中实现的,需要注意HanLP的多线程分词指的是一次输入了一个长文本,而不是一次处理多个输入文本。 本文分享自 baiziyu 的专栏,正文内容已经做了部分修改,便于大家阅读,欢迎一起交流学习!
HanLP收词特别是实体比较多,因此特别容易造成误识别。下边举几个地名误识别的例子,需要指出的是,后边的机构名识别也以地名识别为基础,因此,如果地名识别不准确,也会导致机构名识别不准确。 类型1 数字+地名[1] 暗访哈尔滨网约车:下10单来7辆“黑车” 1辆套牌 [2] 房天下每日成交5月12日海宁商品房销售备案43套 [3] 广西近视手术专家-黄明汉院长9月9日百色见面会 类型2 前词+地名首词成词或地名尾词+后词成词 [1] 西安国企4000元工资相当于私企多少钱? [2] 七月份从包头到山东,十五天左右,有自驾游路线推荐吗? [3] 最受考研人欢迎的城市,有你报考高校所在的城市吗? 类型3 地名本身成词 [1] 滴滴司机接跨省天价订单 乘客半路改道至今未付款 [2] 上联:山水不曾随我老,如何对下联? [3] 上联:柳着金妆闲钓水,如何对下联? Badcase分析及修正 下边介绍一下排查误判原因以及修正的方法首先需要明确以下几点注意事项:1.实体识别受分词精度影响。2.实体识别同样涉及消歧的问题。3.HanLP收录了一些不常见的实体词,会造成错误率升高。4.HanLP基于隐马的命名实体识召回率没有特别要求的话,不需要再去训练。 这里我们以下边这个badcase的分析过程为例来说明 [5] 上联:山水不曾随我老,如何对下联? 打开提示模式 HanLP.Config.enableDebug() 运行人名识别代码 HanLP命名实体识别 def hanlp_ner(text, ner_type): global segment ner_li = [] for term in segment.seg(text): if str(term.nature) == ner_type: ner_li.append(str(term.word)) return ner_li 这里ner_type为你要识别的实体类型,如果是人名则ner_type='nr',地名ner_type='ns',机构名ner_type='nt'。text为要抽取实体的文本。 识别结果,这里为了清晰,只截取了部分输出: 粗分结果[上联/n, :/w, 山水/n, 不/d, 曾随/ns, 我/rr, 老/a, ,/w, 如何/ryv, 对/p, 下联/n, ?/w]地名角色观察: S 1163565 : A 2701 B 439 X 11 不 B 214 A 3 C 3 我 A 47 B 26 , A 40525 B 10497 X 418 对 A 2896 B 454 X 215 ? B 82 地名角色标注:[ /S ,上联/Z ,:/B ,山水/A ,不/C ,曾随/H ,我/B ,老/B ,,/A ,如何/B ,对/A ,下联/Z ,?/B , /S]识别出地名:不曾随 CHhanlp_ns ['不曾随'] 显然,曾随被认为是地名了,而且粗分结果表示的是未经地名识别模块分词和词性标注的结果,显然这是由于词表导致的。由于没有经过地名识别模块,所以不需要去地名的发射词表ns.txt中去找词语,只需要看核心词表CoreNatureDictionary.txt中去找 显然,在核心词表中“曾随“被标记为一个地名,把”曾随“从词表中删除掉,并删除词表文件CoreNatureDictionary.txt.bin,之后再次运行程序得到下边的输出结果 hanlp_ns [] 从这个实例,我们也可以看出一些不常见地名如果做成地名词表,就有导致错误识别实体。因此,我们应该保留一份评测语料,每当修改了实体词表后,需要跑一下测试语料查看准确率,如果降低的太多,则表示这样加进来是不可行的。同时填加的实体名也有可能会造成分词错误。 下边说明一下HanLP中有关实体的词表文件名: 1.CoreNatureDictionary.mini.txt2.CoreNatureDictionary.txt3.CustomDictionary.txt4.机构名词典.txt5.全国地名大全.txt6.人名词典.txt7.上海地名.txt8.现代汉语补充词库.txt9.ns.txt10.nr.txt11.nt.txt 当然这里列出的是通常最有可能导致误识别的词表,如果这些词表都没有找到,还需要在HanLP其他词典文件中寻找。 希望今天的内容对使用HanLP并对隐马情有独钟的小伙伴有帮助。这两天的一点小体会是,实体识别其实跟分词是密不可分的,两者都有共同的处理难点,比如词义消歧(边界的确定),词法分析其实才是真正的NLP的内容之一,而词法分析跟机器学习其实没有太大关系。上边的badcase解决方法不是根本方法,直接去除掉某些词,会导致一些生僻实体识别不出来。我们是否可以考虑左右信息熵之类的测度函数来解决这种词是否需要拆开与其前后构成词。针对词法分析推荐大家使用深度学习的方法,毕竟了解这些方法也是必须的,虽然你可以在实际种不用,但是不代表你可以偷懒而不去学习。
Hanlp自然语言处理包中的基于HMM-Viterbi处理人名识别的内容大概在年初的有分享过这类的文章,时间稍微久了一点,有点忘记了。看了 baiziyu 分享的这篇比我之前分享的要简单明了的多。下面就把文章分享给大家交流学习之用,部分内容有做修改。 本文主要介绍一下HanLP是如何利用HMM来做人名识别的。基本思想是把词语序列作为观测序列,将角色序列作为隐藏序列,当模型预测出最佳隐藏状态序列后,利用模式最大匹配法,匹配出人名实体。下边说一模型的三要素在这个应用中所对应的内容,因为训练阶段就是要求解这三个要素的值。假设有下边的观测序列和其对应的隐藏序列 观察值序列:词1 词2 … 词n隐变量序列:角色1 角色2 … 角色n训练阶段:统计三个要素(三个矩阵的元素值)初始概率分布: 各隐变量标记意义列于下表 状态转移概率分布: 观测概率分布: 有关这些概率值的计算都很简单,就是频率法,留在后边介绍隐马时候再写。预测阶段:根据训练得到的三个要素,利用Viterbi算法求解得到了最优隐藏变量序列 角色1 角色2 ... 角色n*最大模式匹配阶段:利用下边的模式串匹配出人名{ BBCD, BBE, BBZ, BCD, BEE,BE,BG,BXD,BZ,CD,EE,FB, Y,XD}基于HMM-Viterbi标注的人名识别原理就介绍到这里,我想我应该把原理说清楚了,如果你还没有看明白,就等着后续的有关隐马的文章吧。基于隐马的人名识别会很容把以一个姓氏作为开头的词语识别为人名,特别是地名中有人名字的,因此,人工整理出一些非常用姓氏的人名,以及一些地名是必要的,后续如果整理出来了,我应该会放到专栏文章里,毕竟都是公开语料上做的,也就不涉及什么别的不方便。
HanLP收词特别是实体比较多,因此特别容易造成误识别。下边举几个地名误识别的例子,需要指出的是,后边的机构名识别也以地名识别为基础,因此,如果地名识别不准确,也会导致机构名识别不准确。 类型1 数字+地名[1] 暗访哈尔滨网约车:下10单来7辆“黑车” 1辆套牌[2] 房天下每日成交5月12日海宁商品房销售备案43套[3]广西近视手术专家-黄明汉院长9月9日百色见面会 类型2 前词+地名首词成词或地名尾词+后词成词[1] 西安国企4000元工资相当于私企多少钱?[2] 七月份从包头到山东,十五天左右,有自驾游路线推荐吗?[3] 最受考研人欢迎的城市,有你报考高校所在的城市吗? 类型3 地名本身成词[1] 滴滴司机接跨省天价订单 乘客半路改道至今未付款[2] 上联:山水不曾随我老,如何对下联?[3] 上联:柳着金妆闲钓水,如何对下联? Badcase分析及修正下边介绍一下排查误判原因以及修正的方法首先需要明确以下几点注意事项1.实体识别受分词精度影响。2.实体识别同样涉及消歧的问题。3.HanLP收录了一些不常见的实体词,会造成错误率升高。4.HanLP基于隐马的命名实体识召回率没有特别要求的话,不需要再去训练。这里我们以下边这个badcase的分析过程为例来说明[5] 上联:山水不曾随我老,如何对下联?打开提示模式 HanLP.Config.enableDebug() 运行人名识别代码 HanLP命名实体识别 def hanlp_ner(text, ner_type): global segment ner_li = [] for term in segment.seg(text): if str(term.nature) == ner_type: ner_li.append(str(term.word)) return ner_li 这里ner_type为你要识别的实体类型,如果是人名则ner_type='nr',地名ner_type='ns',机构名ner_type='nt'。text为要抽取实体的文本。 识别结果,这里为了清晰,只截取了部分输出。 粗分结果[上联/n, :/w, 山水/n, 不/d, 曾随/ns, 我/rr, 老/a, ,/w, 如何/ryv, 对/p, 下联/n, ?/w]地名角色观察: S 1163565 : A 2701 B 439 X 11 不 B 214 A 3 C 3 我 A 47 B 26 , A 40525 B 10497 X 418 对 A 2896 B 454 X 215 ? B 82 地名角色标注:[ /S ,上联/Z ,:/B ,山水/A ,不/C ,曾随/H ,我/B ,老/B ,,/A ,如何/B ,对/A ,下联/Z ,?/B , /S]识别出地名:不曾随 CHhanlp_ns ['不曾随'] 显然,曾随被认为是地名了,而且粗分结果表示的是未经地名识别模块分词和词性标注的结果,显然这是由于词表导致的。由于没有经过地名识别模块,所以不需要去地名的发射词表ns.txt中去找词语,只需要看核心词表CoreNatureDictionary.txt中去找 显然,在核心词表中“曾随“被标记为一个地名,把”曾随“从词表中删除掉,并删除词表文件CoreNatureDictionary.txt.bin,之后再次运行程序得到下边的输出结果 hanlp_ns []从这个实例,我们也可以看出一些不常见地名如果做成地名词表,就有导致错误识别实体。因此,我们应该保留一份评测语料,每当修改了实体词表后,需要跑一下测试语料查看准确率,如果降低的太多,则表示这样加进来是不可行的。同时填加的实体名也有可能会造成分词错误。 下边说明一下HanLP中有关实体的词表文件名1.CoreNatureDictionary.mini.txt2.CoreNatureDictionary.txt3.CustomDictionary.txt4.机构名词典.txt5.全国地名大全.txt6.人名词典.txt7.上海地名.txt8.现代汉语补充词库.txt9.ns.txt10.nr.txt11.nt.txt 当然这里列出的是通常最有可能导致误识别的词表,如果这些词表都没有找到,还需要在HanLP其他词典文件中寻找。希望今天的内容对使用HanLP并对隐马情有独钟的小伙伴有帮助。这两天的一点小体会是,实体识别其实跟分词是密不可分的,两者都有共同的处理难点,比如词义消歧(边界的确定),词法分析其实才是真正的NLP的内容之一,而词法分析跟机器学习其实没有太大关系。上边的badcase解决方法不是根本方法,直接去除掉某些词,会导致一些生僻实体识别不出来。我们是否可以考虑左右信息熵之类的测度函数来解决这种词是否需要拆开与其前后构成词。针对词法分析推荐大家使用深度学习的方法,毕竟了解这些方法也是必须的,虽然你可以在实际种不用,但是不代表你可以偷懒而不去学习。