放牧代码和思想
专注自然语言处理、机器学习算法

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

目录

这几天写完了人名识别模块,与分词放到一起形成了两层隐马模型。虽然在算法或模型上没有什么新意,但是胜在训练语料比较新,对质量把关比较严,实测效果很满意。比如这句真实的新闻“签约仪式前秦光荣李纪恒仇和等一同会见了参加签约的企业家。”,分词结果:[签约/v, 仪式/n, 前/f, ,/w, 秦光荣/nr, 、/w, 李纪恒/nr, 、/w, 仇和/nr, 等/u, 一同/d, 会见/v, 了/ul, 参加/v, 签约/v, 的/uj, 企业家/n, 。/w],三个人名“秦光荣”“李纪恒”“仇和”一个不漏。一些比较变态的例子也能从容应对,比如下面:

原始句子 HanLP ansj分词
签约仪式前,秦光荣、李纪恒、仇和等一同会见了参加签约的企业家。 [签约/v, 仪式/n, 前/f,   ,/w, 秦光荣/nr, 、/w, 李纪恒/nr, 、/w, 仇和/nr, 等/u, 一同/d, 会见/v, 了/ul, 参加/v, 签约/v,   的/uj, 企业家/n, 。/w] [签约/v, 仪式/n, 前/f,   ,/w, 秦/nr, 光荣/a, 、/w, 李纪恒/nr, 、/w, 仇/nr, 和/c, 等/u, 一同/d, 会见/v, 了/ul, 参加/v,   签约/v, 的/uj, 企业家/n, 。/w]
王国强、高峰、汪洋、张朝阳、韩寒、小四 [王国强/nr, 、/w, 高峰/n,   、/w, 汪洋/n, 、/w, 张朝阳/nr, 、/w, 韩寒/nr, 、/w, 小/a, 四/m] [王国/n, 强/a, 、/w,   高峰/n, 、/w, 汪洋/n, 、/w, 张/q, 朝阳/ns, 、/w, 韩寒/nr, 、/w, 小/a, 四/m]
张浩和胡健康复员了 [张浩/nr, 和/c, 胡健康/nr,   复员/vn, 了/ul] [张浩/nr, 和/c, 胡/nr,   健康/a, 复员/vn, 了/ul]
王总和小丽结婚了 [王总/nr, 和/c, 小丽/nr,   结婚/v, 了/ul] [王/nr, 总和/n, 小丽/nr,   结婚/v, 了/ul]
编剧邵钧林和稽道青说 [编剧/n, 邵钧林/nr, 和/c,   稽道青/nr, 说/v] [编剧/n, 邵钧林/nr, 和/c,   稽/nr, 道青/nr, 说/v]
这里有关天培的壮烈 [这里/r, 有/v, 关天培/nr,   的/uj, 壮烈/a] [这里/r, 有关/vn, 天培/nr,   的/uj, 壮烈/a]
龚学平等领导,邓颖超生前 [龚学平/nr, 等/u, 领导/n,   ,/w, 邓颖超/nr, 生前/t] [龚学平/nr, 等/nw, 领导/n,   ,, 邓颖超/nr, 生前/t]

这是我将自己的分词与ansj作比较得出的结果,由于自己可以随时调整算法,所以主场占了很大便宜。但是第一句绝对没有放水,说实话能识别出“仇和”这么冷僻的名字着实让我惊喜了一下。

开源项目

本文代码已集成到HanLP中开源:http://www.hankcs.com/nlp/hanlp.html

原理

推荐仔细阅读《基于角色标注的中国人名自动识别研究这篇论文,该论文详细地描述了算法原理和实现。从语料库的整理、标注到最后的模式匹配都讲得清清楚楚。我在这篇论文的基础上做了改进,主要步骤我总结如下:

1、对熟语料库自动标注,将原来的标注转化为角色标注。角色标注一共有如下几种:

编码

代码

意义

例子

B

Pf

姓氏

华平先生

C

Pm

双名的首字

平先生

D

Pt

双名的末字

张华先生

E

Ps

单名

说:“我是一个好人”

F

Ppf

前缀

刘、  

G

Plf

后缀

、刘、肖、吴、叶

K

Pp

人名的上文

来到于洪洋的家。

L

Pn

人名的下文

新华社记者黄文

M

Ppn

两个中国人名之间的成分

编剧邵钧林稽道青说

U

Ppf

人名的上文和姓成词

这里有关天培的壮烈

V

Pnw

人名的末字和下文成词

龚学平等领导, 邓颖超生

X

Pfm

姓与双名的首字成词

王国

Y

Pfs

姓与单名成词

高峰汪洋

Z

Pmt

双名本身成词

朝阳

A

Po

以上之外其他的角色

1 中国人名的构成角色表

我在此基础上拓展了一个S,代表句子的开始。

2、统计标签的出现频次,标签的转移矩阵。

3、对粗分结果角色标注,模式匹配。

我对论文中的几个模式串做了拓充,并且采用了AC模式匹配算法。

体会

论文中将三字名称拆分为BCD,我实测在2-gram模型下,C很容易被识别为E,导致人名缺一半。

人民日报2014中的人名并不能覆盖所有常用字,所以我去别的地方找了个人名库,拆成BCD或BE补充了进去。

人民日报2014语料库中有很多错误,比如

去/vf 年老/vi 张中秋/nr 去/vf “/w 泡茶/vi ”/w ,/w 送礼/vi 遭到/v 了/ule 拒绝/v ,/w 老张/nz 担心/v 金额/n 不够/a

中秋很明显不是人名的组成部分,这个必须手工剔除。

“中秋安全”会识别出“中 秋安全”来,因为2-gram词典中没有“中秋@安全”这种接续,而有“中@未##人”这种接续。初步的解决方法是手工往2-gram词典里面加一条“中秋@安全”。这反映了这种方法的局限性,另一方面也说明词典的重要性。

nlp.jpg

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » 实战HMM-Viterbi角色标注中国人名识别

分享到:更多 ()

评论 54

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
  1. #23

    博主 你好,你只是训练了分词模型,对吗,实体识别只是在分词的基础上用veterbi

    朱永魁5个月前 (03-23)回复
  2. #22

    博主你好,在做人名识别的时候,发现复姓人名识别不是很准确,比如:欧阳娜娜借了长孙无忌三块钱,识别为:[欧阳/nz, 娜娜/nz, 借/v, 了/ule, 长/a, 孙无忌/nr, 三/m, 块钱/q],复姓人名识别该如何优化,请指点一二,不胜感激。

    拎壶冲_风风6个月前 (02-28)回复
  3. #21

    博主你好,最近在用HanLP做人名识别,发现当三字人名中的前两个字也是一个人名时,识别不出来三字人名。例如:证明原告孙辉与被告姚宏旭之间的借款关系。识别为:[证明/v, 原告/n, 孙辉/nr, 与/p, 被告/n, 姚宏/nr, 旭/nr, 之间/f, 的/uj, 借款/n, 关系/n]

    姚宏旭被拆开了,怎么处理下呢?

    龙卷风7个月前 (01-09)回复
    • nShortSegment.seg可以用这个试一下

      HRUIKAI7个月前 (01-16)回复
  4. #20

    博主你好,为什么我直接跑 DemoChineseNameRecognition.java 跑出来的人名都是 /nz呢?

  5. #19

    博主,您好,我想咨询下,在训练转移概率的时候。XYZA怎么标识。比如王/B 国/C 维/D。那其他位置的王国/X。如果这样,康复 X 2 L 1这种只是优先统计出来,并不是角色语料中统计出来的吗?

    Stardust洲9个月前 (11-14)回复
  6. #18

    博主,您好。我目前尝试自定义一个人物关系词典作追加词典,然后用hanlp的词性标注功能将关系词识别出来。但是结果不理想。例如,实验结果还是将“丈夫”识别为n,而不是自定义词典中我自己定义的词性,似乎自定义词典并没有起到作用?请问这个问题要怎么解决?谢谢!

    hyc10个月前 (10-26)回复
  7. #17

    楼主,您好,我想问您人名、地名、机构名由普通词类标注转成角色标注的代码在哪里?

    王宇10个月前 (10-07)回复
  8. #16

    楼主你好,我再用HanLP进行命名实体识别时,效果感觉不太好,远远低于我的预期,尤其是人名识别和机构名识别。我采用的语料主要集中在汽车行业。如果对于机构名识别效果差的话,我还能从测试语料与训练语料的差异上给出解释,但是对于人名识别,感觉比较难以理解。 比如 “都带长”,“都一处”,"那就来","方是为",咋一看感觉都是基于一些姓得到的。

    hao2041年前 (2016-05-29)回复
    • 感谢反馈,暂时的办法是用黑名单,长远的改进还在探索中。

      hankcs1年前 (2016-05-29)回复
  9. #15

    你好,我想识别出这句话中的人名“卢凡”,参考你的说明我添加在“data\dictionary\person\nrf.txt”中,并删除“data\dictionary\person\nrf.txt.trie.dat”,可是还是不能识别出“卢凡是先进事迹报告团的成员。”中的“卢凡”呢?

    飞星一念1年前 (2016-04-25)回复
    • 几个词典我都试了,好像只有加到data\dictionary\CoreNatureDictionary.txt才有效果?

      飞星一念1年前 (2016-04-25)回复
  10. #14

    有关天培养的猪,这句话也识别不了[嘻嘻]

    陽炎零1年前 (2016-04-22)回复
  11. #13

    测试了一个问题例子,反馈一下。可能是核心词典缺少工号之类的词。
    “谭彬林工号,杨军工号,陆江工号,姚曼青工号,”

    UB_吴斌2年前 (2016-01-19)回复
  12. #12

    博主,请问在通过维特比算法进行人名角色序列判断时,为何在前向计算的过程中即可以判断角色,而不是在回溯路径的时候判断角色

    旅行的风2年前 (2016-01-12)回复
    • 你还没理解,回溯是最短路算法的术语。

      hankcs2年前 (2016-01-14)回复
  13. #11

    博主,请问root目录必须是绝对路径吗?我用的是 web项目怎么写相对路径,能给个实例吗?thank you!

    不懂木2年前 (2015-12-25)回复
    • File file=new File(“.”);
      String path=file.getAbsolutePath();
      根据这个path配相对路径就行了。

      hankcs2年前 (2015-12-25)回复
  14. #10

    博主你好!你的标准分词利用了用户词典,而用户词典中很多是实体,导致分词时有好多单词组合在一起成为实体了,这样会不会不符合分词标准啊?

    small bird2年前 (2015-12-13)回复
    • 你好,没有绝对的标准,你觉得不标准可以关掉|修改用户词典,我只提供工具,不提供标准。

      hankcs2年前 (2015-12-13)回复
  15. #9

    博主,我自己定义的人名应该加到哪个词典里才能在分词的时候直接分出来。

    wjluck2年前 (2015-12-01)回复
    • 比如:“毛瑞斯莫曾在2006年夺得了澳网和温网的女单冠军” 这句话,分词结果是:“[毛/n, 瑞斯莫/nrf, 曾/d, 在/p, 2006/m, 年/qt, 夺得/v, 了/ule, 澳网/n, 和/cc, 温网/nz, 的/ude1, 女单/n, 冠军/nnt]”。
      我想让“毛瑞斯莫” 做为一个人名分割出来,应该怎么做?多谢博主了

      wjluck2年前 (2015-12-01)回复
      • data/dictionary/person/nrf.txt即可。

        hankcs2年前 (2015-12-01)回复
        • 博主,我这样加了还是不行?

          wjluck2年前 (2015-12-02)回复
          • 非常感谢博主,我最后是加在datadictionaryCoreNatureDictionary.txt中,并且删除缓存后使用的。着急用,没仔细看原理。多谢!

            wjluck2年前 (2015-12-02)
  16. #8

    博主你好,想请教一个小问题。多层隐马模型,若将人民识别放在第二层,那第二层的输入依赖于第一层的输出,此时语料库作为训练集,是不是需要经过第一层后再做标识?

    奇真亦假2年前 (2015-12-01)回复
    • 对,在粗分的基础上标注。

      hankcs2年前 (2015-12-01)回复
  17. #7

    请教博主:“人民日报2014语料库”是个怎样的语料库,好像只听说过1998的人民日报语料库。难道又有人大规模标注了2014年人民日报?这可是个大事,要花很多人力物力的。请博主指教,谢谢!

    赵凯2年前 (2015-09-17)回复
  18. #6

    博主你好,最近在拜读你的HanLP代码,在阅读人名识别这部分的代码时,有一个疑问:PersonRecognition中的Recognition在求最优标签序列时调用了viterbiExCompute,viterbiExCompute又调用了Viterbi.computeEnumSimply,但computeEnumSimply我看了一下应该是贪心算法,不是维特比,不知道是不是这里是个bug还是故意为之?烦请解答,谢谢!

    陆家凡2年前 (2015-09-16)回复
    • 你好,是为了效率才这么做的,去掉simply就是标准版的维特比。

      hankcs2年前 (2015-09-16)回复
    • 你好,是为了效率才这么做的,去掉simply就是标准版的维特比。

      hankcs2年前 (2015-09-16)回复
      • 哦,是这样啊,那从效果上来说两者差距并不大咯?

        陆家凡2年前 (2015-09-17)回复
        • 是的,主观上看是这样的

          hankcs2年前 (2015-09-17)回复
          • 好的,非常感谢!

            陆家凡2年前 (2015-09-17)
  19. #5

    博主你好,我在在阅读HanLP的人名识别这部分代码时发现了一个问题,Viterbi.java中的computeEnumSimply应该是贪心算法吧,而不是维特比?

    granzonmk22年前 (2015-09-15)回复
    • 你好,是为了效率才这么做的,去掉simply就是标准版的维特比。

      hankcs2年前 (2015-09-16)回复
  20. #4

    博主,你好,请问2gram词典是干嘛用的?

    学生一枚2年前 (2015-08-27)回复
    • 在序列标注模型中并无用处。

      hankcs2年前 (2015-08-27)回复
  21. #3

    博主,你好,我有一个疑问,观测状态为文本里面的词,隐含状态为角色序列。HMM的参数统计出来,即模型确定后。如果来了一个新的文本进行veterbi求解,而该文本里面有新的词,即新的观测状态,请问怎么标记?谢谢

    KerneLi2年前 (2015-05-31)回复
    • 新的词只能置为A,代表非人名成分。这也是HMM的缺陷,没有太大的泛化能力。

      hankcs2年前 (2015-08-27)回复
  22. #2

    不知道是我想错了还是你写错了,请大神指点

    快乐小子0092年前 (2015-05-26)回复
  23. #1

    你好,在你的PersonDictionary.java中我个人觉得发现了一个bug,就是在parsePattern这函数中,对V这个进行拆分时,感觉你拆分代码不对,个人觉得应该是这样的:listIterator.previous();
    String nowED = current.realWord.substring(0,1);
    String nowL = current.realWord.substring(1);
    listIterator.set(new Vertex(nowED));
    listIterator.next();
    listIterator.add(new Vertex(nowL));

    快乐小子0092年前 (2015-05-26)回复
    • 这样不对,标签并不是这样定义的,你可以试试 龚学平等领导说 。

      hankcs2年前 (2015-08-27)回复

我的开源项目

HanLP自然语言处理包基于DoubleArrayTrie的Aho Corasick自动机