HanLP中人名识别分析
HanLP中人名识别分析
在看源码之前,先看几遍论文《基于角色标注的中国人名自动识别研究》
关于命名识别的一些问题,可参考下列一些issue:
HanLP参考博客:
分词
分词:给定一个字的序列,找出最可能的标签序列(断句符号:[词尾]或[非词尾]构成的序列)。结巴分词目前就是利用BMES标签来分词的,B(开头),M(中间),E(结尾),S(独立成词)
分词也是采用了维特比算法的动态规划性质求解的,具体可参考:文本挖掘的分词原理
角色观察
以“唱首张学友的歌情已逝”为例,
先将起始顶点 始##始,角色标注为:NR.A 和 NR.K,频次默认为1
iterator.next();
tagList.add(new EnumItem<NR>(NR.A, NR.K)); // 始##始 A K

对于第一个词“唱首”,它不存在于 nr.txt中,EnumItem<NR> nrEnumItem = PersonDictionary.dictionary.get(vertex.realWord);返回null,于是根据它本身的词性猜一个角色标注:
switch (vertex.guessNature()){
case nr:
case nnt:
default:{
nrEnumItem = new EnumItem<NR>(NR.A, PersonDictionary.transformMatrixDictionary.getTotalFrequency(NR.A));
}
}

由于"唱首"的Attribute为 nz 16,不是nr 和 nnt,故默认给它指定一个角色NR.A,频率为nr.tr.txt中 NR.A 角色的总频率。
此时,角色列表如下:

接下来是顶点“张”,由于“张”在nr.txt中,因此PersonDictionary.dictionary.get(vertex.realWord)返回EnumItem对象,直接将它加入到角色列表中:

EnumItem<NR> nrEnumItem = PersonDictionary.dictionary.get(vertex.realWord);
tagList.add(nrEnumItem);
加入“张”之后的角色列表如下:

“唱首张学友的歌情已逝” 整句的角色列表如下:

至此,角色观察 部分 就完成了。
总结一下,对句子进行角色观察,首先是通过分词算法将句子分成若干个词,然后对每个词查询人名词典(PersonDictionary)。
- 若这个词在人名词典中(nr.txt),则记录该词的角色,所有的角色在
com.hankcs.hanlp.corpus.tag.NR.java中定义。 - 若这个词不在人名词典中,则根据该词的Attribute “猜一个角色”。在猜的过程中,有些词在核心词典中可能已经标注为nr或者nnt了,这时会做分裂处理。其他情况下则是将这个词标上NR.A角色,频率为 NR.A 在转移矩阵中的总词频。
维特比算法(动态规划)求解最优路径
在上图中,给每个词都打上了角色标记,可以看出,一个词可以有多个标记。而我们需要将这些词选择一条路径最短的角色路径。参考隐马尔可夫模型维特比算法详解
List<NR> nrList = viterbiComputeSimply(roleTagList);
//some code....
return Viterbi.computeEnumSimply(roleTagList, PersonDictionary.transformMatrixDictionary);
而这个过程,其实就是:维特比算法解码隐藏状态序列。在这里,五元组是:
隐藏状态集合 com.hankcs.hanlp.corpus.tag.NR.java 定义的各个人名标签
观察状态集合 已经分好词的各个tagList中元素(相当于分词结果)

转移概率矩阵 由 nr.tr.txt 文件生成得到。具体可参考:
发射概率 某个人名标签(隐藏状态)出现的次数 除以 所有标签出现的总次数
Math.log((item.getFrequency(cur) + 1e-8) / transformMatrixDictionary.getTotalFrequency(cur)初始状态(始##始) 和 结束状态(末##末)

维特比解码隐藏状态的动态规划求解核心代码如下:
for (E cur : item.labelMap.keySet())
{
double now = transformMatrixDictionary.transititon_probability[pre.ordinal()][cur.ordinal()] - Math.log((item.getFrequency(cur) + 1e-8) / transformMatrixDictionary.getTotalFrequency(cur));
if (perfect_cost > now)
{
perfect_cost = now;
perfect_tag = cur;
}
}
transformMatrixDictionary.transititon_probability[pre.ordinal()][cur.ordinal()] 是前一个隐藏状态 pre.ordinal()转换到当前隐藏状态cur.ordinal()的转移概率。Math.log((item.getFrequency(cur) + 1e-8) / transformMatrixDictionary.getTotalFrequency(cur)是当前隐藏状态的发射概率。二者“相减”得到一个概率 保存在double now变量中,然后通过 for 循环找出 当前观察状态 对应的 最可能的(perfect_cost最小) 隐藏状态 perfect_tag。
至于为什么是上面那个公式来计算转移概率和发射概率,可参考论文:《基于角色标注的中国人名自动识别研究》
在上面例子中,得到的最优隐藏状态序列(最优路径)K->A->K->Z->L->E->A->A 如下:
nrList = {LinkedList@1065} size = 8
"K" 始##始
"A" 唱首
"K" 张
"Z" 学友
"L" 的
"E" 歌
"A" 情已逝
"A" 末##末
例如:
隐藏状态---观察状态
"K"----------始##始
最大匹配
有了最优隐藏序列:KAKZLEAA,接下来就是:后续的“最大匹配处理”了。
PersonDictionary.parsePattern(nrList, pWordSegResult, wordNetOptimum, wordNetAll);
在最大匹配之前,会进行“模式拆分”。在com.hankcs.hanlp.corpus.tag.NR.java 定义了隐藏状态的具体含义。比如说,若最优隐藏序列中 存在 'U' 或者 'V',
U Ppf 人名的上文和姓成词 这里【有关】天培的壮烈
V Pnw 三字人名的末字和下文成词 龚学平等领导, 邓颖【超生】前
则会做“拆分处理”
switch(nr)
{
case U:
//拆分成K B
case V:
//视情况拆分
}
拆分完成之后,重新得到一个新的隐藏序列(模式)
String pattern = sbPattern.toString();
接下来,就用AC自动机进行最大模式匹配了,并将匹配的结果存储到“最优词网”中。当然,在这里就可以自定义一些针对特定应用的 识别处理规则
trie.parseText(pattern, new AhoCorasickDoubleArrayTrie.IHit<NRPattern>(){
//.....
wordNetOptimum.insert(offset, new Vertex(Predefine.TAG_PEOPLE, name, ATTRIBUTE, WORD_ID), wordNetAll);
}
将识别出来的人名保存到最优词网后,再基于最优词网调用一次维特比分词算法,得到最终的分词结果---细分结果。
if (wordNetOptimum.size() != preSize)
{
vertexList = viterbi(wordNetOptimum);
if (HanLP.Config.DEBUG)
{
System.out.printf("细分词网:\n%s\n", wordNetOptimum);
}
}
总结
源码上的人名识别基本上是按照论文中的内容来实现的。对于一个给定的句子,先进行下面三大步骤处理:
- 角色观察
- 维特比算法解码求解隐藏状态(求解各个分词 的 角色标记)
- 对角色标记进行最大匹配(可做一些后处理操作)
最后,再使用维特比算法进行一次分词,得到细分结果,即为最后的识别结果。
这篇文章里面没有写维特比分词算法的详细过程,以及转移矩阵的生成过程,以后有时间再补上。看源码,对隐马模型的理解又加深了一点,感受到了理论的东西如何用代码一步步来实现。由于我也是初学,对源码的理解不够深入或者存在一些偏差,欢迎批评指正。
关于动态规划的一个简单示例,可参考:动态规划之Fib数列类问题应用
原文链接:http://www.cnblogs.com/hapjin/p/9026461.html
HanLP中人名识别分析的更多相关文章
- HanLP中人名识别分析详解
HanLP中人名识别分析详解 在看源码之前,先看几遍论文<基于角色标注的中国人名自动识别研究> 关于命名识别的一些问题,可参考下列一些issue: l ·名字识别的问题 #387 l ·机 ...
- HanLP中的人名识别分析详解
在看源码之前,先看几遍论文<基于角色标注的中国人名自动识别研究> 关于命名识别的一些问题,可参考下列一些issue: u u名字识别的问题 #387 u u机构名识别错误 u u关于层叠H ...
- ICTCLAS中的HMM人名识别
http://www.hankcs.com/nlp/segment/ictclas-the-hmm-name-recognition.html 本文主要从代码的角度分析标注过程中的细节,理论谁都能说, ...
- Hanlp实战HMM-Viterbi角色标注中国人名识别
这几天写完了人名识别模块,与分词放到一起形成了两层隐马模型.虽然在算法或模型上没有什么新意,但是胜在训练语料比较新,对质量把关比较严,实测效果很满意.比如这句真实的新闻“签约仪式前,秦光荣.李纪恒.仇 ...
- hanlp自然语言处理包的人名识别代码解析
HanLP发射矩阵词典nr.txt中收录单字姓氏393个.袁义达在<中国的三大姓氏是如何统计出来的>文献中指出:当代中国100个常见姓氏中,集中了全国人口的87%,根据这一数据我们只保留n ...
- 自然语言处理工具python调用hanlp中文实体识别
Hanlp作为一款重要的中文分词工具,在GitHub的用户量已经非常之高,应该可以看得出来大家对于hanlp这款分词工具还是很认可的.本篇继续分享一篇关于hanlp的使用实例即Python调用hanl ...
- 【文智背后的奥秘】系列篇——基于CRF的人名识别
版权声明:本文由文智原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/133 来源:腾云阁 https://www.qclou ...
- 基于分布式的短文本命题实体识别之----人名识别(python实现)
目前对中文分词精度影响最大的主要是两方面:未登录词的识别和歧义切分. 据统计:未登录词中中文姓人名在文本中一般只占2%左右,但这其中高达50%以上的人名会产生切分错误.在所有的分词错误中,与人名有关的 ...
- HanLP-基于HMM-Viterbi的人名识别原理介绍
Hanlp自然语言处理包中的基于HMM-Viterbi处理人名识别的内容大概在年初的有分享过这类的文章,时间稍微久了一点,有点忘记了.看了 baiziyu 分享的这篇比我之前分享的要简单明了的多.下面 ...
随机推荐
- 使用Coverage进行代码覆盖率的测试
软件测试实验报告 一.实验目的: 使用软件测试代码覆盖率. 二.实验工具: Windows10.Python3.6.3.Coverage. 三.实验内容: 1.编写准备测试的代码main.py和测试代 ...
- NOIp2018 游记
作为一名蒟蒻,对于NOIp当然是不抱什么希望.所以就只能在比赛中吸取经验咯... Day0 害怕书到用时方恨少,疯狂打板子(玩电脑) Day1 来到考场了,发现键盘空格按不起,觉得非常尴尬,然后他告诉 ...
- [NOI2005]聪聪与可可(期望dp)
题意:给一张无向图,有一只猫和一只老鼠,猫每秒会向老鼠的方向移动两个单位,若它们的距离为一,那么只会移动一个单位,老鼠会等概率向周围移动一步或不动,求猫抓到老鼠的期望时间. Solutionluogu ...
- 【洛谷P1060 开心的金明】
题目描述 金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间.更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过NNN元钱就行”. ...
- interface 界面&接口
接口:接口(硬件类接口)是指同一计算机不同功能层之间的通信规则称为接口.接口(软件类接口)是指对协定进行定义的引用类型. 界面:窗口,显示 英文解释都是interface partly from ba ...
- haploview出现“results file must contain a snp column”的解决方法
将plink文件用“--recode HV ”的参数生成即可 /software/plink --file yourfile --recode HV --snps-only just-acgt --o ...
- 交叉编译jpeglib遇到的问题
由于要在开发板中加载libjpeg,不能使用gcc编译的库文件给以使用,需要自己配置使用另外的编译器编译该库文件. /usr/bin/ld: .libs/jaricom.o: Relocations ...
- java中Comparatable接口和Comparator接口的区别
1.不同类型的排序规则 .自然排序是什么? 自然排序是一种升序排序.对于不同的数据类型,升序规则不一样: BigDecimal BigInteger Byte Double Float Int ...
- hystrix实战总结;
HystrixCircuitBreaker 有三种状态 : 断路器默认是20个请求失败才打开短路器,可以进行配置: CLOSED :关闭 OPEN :打开 HALF_OPEN :半开 1.接口正确,接 ...
- (链表 双指针) leetcode 141. Linked List Cycle
Given a linked list, determine if it has a cycle in it. To represent a cycle in the given linked lis ...