HanLP中人名识别分析

在看源码之前,先看几遍论文《基于角色标注的中国人名自动识别研究》

关于命名识别的一些问题,可参考下列一些issue:

HanLP参考博客:

词性标注

层叠HMM-Viterbi角色标注模型下的机构名识别

分词

HMM与分词、词性标注、命名实体识别中说:

分词:给定一个字的序列,找出最可能的标签序列(断句符号:[词尾]或[非词尾]构成的序列)。结巴分词目前就是利用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中人名识别分析的更多相关文章

  1. HanLP中人名识别分析详解

    HanLP中人名识别分析详解 在看源码之前,先看几遍论文<基于角色标注的中国人名自动识别研究> 关于命名识别的一些问题,可参考下列一些issue: l ·名字识别的问题 #387 l ·机 ...

  2. HanLP中的人名识别分析详解

    在看源码之前,先看几遍论文<基于角色标注的中国人名自动识别研究> 关于命名识别的一些问题,可参考下列一些issue: u u名字识别的问题 #387 u u机构名识别错误 u u关于层叠H ...

  3. ICTCLAS中的HMM人名识别

    http://www.hankcs.com/nlp/segment/ictclas-the-hmm-name-recognition.html 本文主要从代码的角度分析标注过程中的细节,理论谁都能说, ...

  4. Hanlp实战HMM-Viterbi角色标注中国人名识别

    这几天写完了人名识别模块,与分词放到一起形成了两层隐马模型.虽然在算法或模型上没有什么新意,但是胜在训练语料比较新,对质量把关比较严,实测效果很满意.比如这句真实的新闻“签约仪式前,秦光荣.李纪恒.仇 ...

  5. hanlp自然语言处理包的人名识别代码解析

    HanLP发射矩阵词典nr.txt中收录单字姓氏393个.袁义达在<中国的三大姓氏是如何统计出来的>文献中指出:当代中国100个常见姓氏中,集中了全国人口的87%,根据这一数据我们只保留n ...

  6. 自然语言处理工具python调用hanlp中文实体识别

    Hanlp作为一款重要的中文分词工具,在GitHub的用户量已经非常之高,应该可以看得出来大家对于hanlp这款分词工具还是很认可的.本篇继续分享一篇关于hanlp的使用实例即Python调用hanl ...

  7. 【文智背后的奥秘】系列篇——基于CRF的人名识别

    版权声明:本文由文智原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/133 来源:腾云阁 https://www.qclou ...

  8. 基于分布式的短文本命题实体识别之----人名识别(python实现)

    目前对中文分词精度影响最大的主要是两方面:未登录词的识别和歧义切分. 据统计:未登录词中中文姓人名在文本中一般只占2%左右,但这其中高达50%以上的人名会产生切分错误.在所有的分词错误中,与人名有关的 ...

  9. HanLP-基于HMM-Viterbi的人名识别原理介绍

    Hanlp自然语言处理包中的基于HMM-Viterbi处理人名识别的内容大概在年初的有分享过这类的文章,时间稍微久了一点,有点忘记了.看了 baiziyu 分享的这篇比我之前分享的要简单明了的多.下面 ...

随机推荐

  1. 使用Coverage进行代码覆盖率的测试

    软件测试实验报告 一.实验目的: 使用软件测试代码覆盖率. 二.实验工具: Windows10.Python3.6.3.Coverage. 三.实验内容: 1.编写准备测试的代码main.py和测试代 ...

  2. NOIp2018 游记

    作为一名蒟蒻,对于NOIp当然是不抱什么希望.所以就只能在比赛中吸取经验咯... Day0 害怕书到用时方恨少,疯狂打板子(玩电脑) Day1 来到考场了,发现键盘空格按不起,觉得非常尴尬,然后他告诉 ...

  3. [NOI2005]聪聪与可可(期望dp)

    题意:给一张无向图,有一只猫和一只老鼠,猫每秒会向老鼠的方向移动两个单位,若它们的距离为一,那么只会移动一个单位,老鼠会等概率向周围移动一步或不动,求猫抓到老鼠的期望时间. Solutionluogu ...

  4. 【洛谷P1060 开心的金明】

    题目描述 金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间.更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过NNN元钱就行”. ...

  5. interface 界面&接口

    接口:接口(硬件类接口)是指同一计算机不同功能层之间的通信规则称为接口.接口(软件类接口)是指对协定进行定义的引用类型. 界面:窗口,显示 英文解释都是interface partly from ba ...

  6. haploview出现“results file must contain a snp column”的解决方法

    将plink文件用“--recode HV ”的参数生成即可 /software/plink --file yourfile --recode HV --snps-only just-acgt --o ...

  7. 交叉编译jpeglib遇到的问题

    由于要在开发板中加载libjpeg,不能使用gcc编译的库文件给以使用,需要自己配置使用另外的编译器编译该库文件. /usr/bin/ld: .libs/jaricom.o: Relocations  ...

  8. java中Comparatable接口和Comparator接口的区别

    1.不同类型的排序规则 .自然排序是什么?   自然排序是一种升序排序.对于不同的数据类型,升序规则不一样:   BigDecimal BigInteger Byte Double Float Int ...

  9. hystrix实战总结;

    HystrixCircuitBreaker 有三种状态 : 断路器默认是20个请求失败才打开短路器,可以进行配置: CLOSED :关闭 OPEN :打开 HALF_OPEN :半开 1.接口正确,接 ...

  10. (链表 双指针) 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 ...