中文姓名识别算法

jcseg支持中文姓名的识别。

但是并不是什么很具有新意的算法,或者说需要经过一大版的数学公式计算才能实现的。

jcseg的姓名识别算法很简单,但是从实际效益来看,确实达到了我预期的效果。

首先说明:jcseg使用的不是机械匹配中文姓名(虽然词库里面有一些人名,cc-cedict里面整理出来的),本人觉得这样太机械了,这么的中文姓名不可能都存词库。

如何实现的呢? 

巧妙利用mmseg算法的特性和中文姓名的特性。(那么哥们你就要熟悉mmseg算法了)

每一次调用next方法去获取下一个切分结果,jcseg会首先返回经过四种过滤算法筛选出来的最好的一个chunk。

例如:A_B_C

mmseg每确认一个切分结果都会向后查找三个词,然后吧所有可能的组合情况组合成很多chunk,但是经过四种过滤算法后,最终会返回一个最好的chunk(组合)(也有返回多个的情况,但是这个不是问题)。

在这个过程中,mmseg已经告诉我们了,所有可能情况我都试过了,这个是最好的,也就是能成词的已经成词了,没有成词的单字,通常是“成词自由语素度”很高的词,例如:的,是。。。

(想象以下,如果在返回的chunk中,能够找到中文姓氏,而且大部分的中文姓氏的“成词自由语素度”都很低,这说明什么?说明这个地方隐藏了一个中文姓名)。

姓名识别过程:

1.先确认整个chunk的长度(词语的个数)大于1,并且这个chunk的第一个词长度(字的个数)小于等于2。(大于2那就没必要分析了,说明是从词库里面匹配的结果,而一个中文姓氏最长为2,当然是指汉族)

jcseg中代码:

 
01IChunk chunk = getBestCJKChunk(chars, cjkidx);
02w = chunk.getWords()[0];
03                      
04int T = -1;
05if ( Config.I_CN_NAME
06    && w.getLength() <= 2 && chunk.getWords().length > 1  ) {
07    StringBuilder sb = new StringBuilder(); 
08    sb.append(w.getValue());
09    String str = null;
10                          
11    if ( dic.match(ILexicon.CN_LNAME, w.getValue()) && (str = findCHName(chars, 0, chunk)) != null) {
12        T = IWord.T_CN_NAME;
13        sb.append(str);
14    }
15    else if ( dic.match(ILexicon.CN_LNAME_ADORN, w.getValue())
16        && chunk.getWords()[1].getLength() <= 2 
17        && dic.match(ILexicon.CN_LNAME, 
18              chunk.getWords()[1].getValue())) {
19        T = IWord.T_CN_NICKNAME;
20        sb.append(chunk.getWords()[1].getValue());
21    }
22    /*
23     * the length of the w is 2:
24     * the last name and the first char make up a word
25     * for the double name. 
26     */
27    /*else if ( w.getLength() > 1 && findCHName( w, chunk ))  {
28        T = IWord.T_CN_NAME;
29        sb.append(chunk.getWords()[1].getValue().charAt(0));
30    }*/
31                          
32    if ( T != -1 ) w = new Word(sb.toString(), T);
33  
34}


第一个if里面的条件就是进行相关的判断。


2.判断是否满足中文姓名查找条件:

(1).中文姓氏确认。

 
1if ( dic.match(ILexicon.CN_LNAME, w.getValue()) 
2    && (str = findCHName(chars, 0, chunk)) != null)

获取word的值:w.getValue()返回这词的字符串表示形式,去姓氏词库里面查找(ILexicon.CN_LNAME,表示姓氏词库,词库文件为:lex-lname.lex,包含中文姓名中所有单复姓氏),确认w是否为一个中文姓氏,这是姓名查找最基本的要求吧。(记得有双字姓氏哦,所以“诸葛亮”可以识别出来)


(2).姓氏修饰词确认,这个我们通常老叫,老陈,老张,小陈。。。 对,就是前面的老,小什么的。这种姓氏修饰有一个专门的词库:lex-ln-adorn.lex

 

1//the w is Chinese last name adorn
2else if ( dic.match(ILexicon.CN_LNAME_ADORN, w.getValue()) {
3  
4}

获取word的值:w.getValue()返回这个词的字符串表示形式,去姓氏修饰词库里面查找(ILexicon.CN_LNAME_ADORN,表示姓氏修饰词库),确认w是否为一个中文姓氏修饰词。这个也可以有吧。


3.姓名查找:

满足姓名查找条件(满足上述中一个就可以了),就可以开始查找姓名了。

由:findCHName( chars, 0, chunk )这个方法来实现。

主要思路为:(需要根据chunk的词数和词长分情况分析,这里笼统的概述下)

(1).查看是否满足双姓名的要求(很简单,加上姓氏至少需要三个字吧),

--》确认是否为双姓名,查看姓氏后的第一个词是否为双姓名首字(词库:lex-dname-1.lex保存了所有可能做双姓名的首字的单字)

 
1d1表示姓氏后的第一个词。
2d2表示姓氏后的第二个词。
3if ( dic.match(ILexicon.CN_DNAME_1, d1)
4    && dic.match(ILexicon.CN_DNAME_2, d2)) {
5    //可能为双姓名。
6}

(2).如果不是双姓名,则看看是否为单姓名:


-》先确认姓氏后的第一个词是否为单字姓名词库(lex-sname.lex)中的词。

-》如果是,在确认姓氏后的第二个词的“成词自由语素度”是否满足要求(通常情况下,姓名都和哪些“成词自由语素度”很高的词在一起,而且姓名中的词“自由语素度”都偏低,这个假设得到了统计的验证)。

Config.NANE_SINGLE_THRESHOLD这个就是阕值。可以更改jcseg.properties配置文件实现自主更改。

 

1else if ( dic.match(ILexicon.CN_SNAME, d1) 
2    && dic.get(ILexicon.CJK_WORDS, d2).getFrequency() >= Config.NAME_SINGLE_THRESHOLD ) {
3    //确认为单姓名。
4}


上面的情况,我忽略了能够进行这么判断的条件,具体可以查看jcseg源码。

(而且具体还需要更具chunk的词长和词数来分情况判断。)


剩下就是一些歧义情况:(姓名中的词和周围的词成词的情况)

1.双姓名:

-》双字姓名中,首字和尾字成词的情况,例如:“陈美丽是对的”

chunk: 陈_美丽_是(美丽成词)

这个jcseg是基于姓名周围的单字“成词自由语素度”偏高的情况,来去除歧义的。也就是查看最后一个词的最后一个字的“语素自由度“和Config.NANE_SINGLE_THRESHOLD对比结果。

-》双字姓名中,尾字和后面的词成词的情况,例如:“这本书是陈志高的”,“ 陈美丽的人生”

chunk:陈_志_高的

chunk:陈_美丽的_人生

同上一解决办法一样。


2.单姓名:

-》尾字和其后的一个字成词的情况:例如:"这本书是陈高的"

chunk:陈_高的 (single name)

通过查看“自由语素度”可以去除歧义。


3.不知是“单字姓名”还是”双字姓名“的情况:就是chunk的词数为3,但是中间这个词的字数为1的情况。

例如:“ 陈志高兴奋极了”,“陈高兴奋极了”,“陈志高的”

chunk:陈_志_高兴 (兴和后面成词)

chunk:陈_高_兴奋 (single name)

chunk:陈_志_高的

同时这也是这个算法的最麻烦的部分之一,按照上述过程这个姓名到底是”陈高“这个单字姓名还是“陈高兴”这个双子姓名呢?正确的结果我们都知道。

jcseg是通过在此位置再向后取一个chunk来分析去除歧义的。

基于这样的一个情况,如果最后一个词语(高兴,兴奋,高的)中的“兴,奋,的”和后面的词成词,并且这个词的首字,("高", “兴”, “高”)又是lex-double-2.lex(双字姓名的尾字词库)中的词,则认为是双字姓名,如果不成词,并且这些词的“自由语素度”偏高则,认为为单字姓名,如果“自由语素度”偏高本身就可以成词,至少可以达到统计的切分效果。


另外还有一种情况是jcseg目前没有处理的,就是双字姓名中的姓氏和双字姓名的首字成词的情况:

例如:“我很喜欢陈述高的演讲,我很不喜欢陈述高调的样子。”

中的第一个名字“陈述高”中“陈述”成词。但是第二个不成词。

目前我见过的所有支持中文姓名识别的分词系统中,没有一个可以正确识别的,包括“哈尔滨工程学院”的NLP系统。

都识别成:“陈述”。

本文的第一断代码中有一块注释就是用来处理这种情况的,但是反而带来了更多的负面影响,很不成熟,所以我注释掉了。


jcseg姓名识别歧义:

首先声明我不是什么语言处理专家,我的语文一直都是及格的水平。只是顺着逻辑这么分析而已,总有考虑不到的情况,所以这个算法也有不少识别错误的情况。

1.多个姓氏排在一起的情况就会出问题,

例如:“向林俊德同志表示问候”,

中的向和林都是姓氏,jcseg目前处理这种情况会出问题。(目前我吧“向”这个姓氏在词库中注释掉了)。

2.姓氏用作非姓氏的情况:

例如:心血和智慧,

中的“和”是姓氏词,但是在这个地方不是用作姓氏。

导致分词结果为:心血|和智慧 (目前我吧“和”这个姓氏在词库中注释掉了)。


这些问题会可能会在jcseg 1.7.2版本中解决。那么jcseg的姓名识别正确率可以达到94%以上。

目前我通过使用人名日报的新闻进行姓名识别,有90%以上的正确率。

中文信息计算机自动处理的研究已有几十年的 历史 , 但至今仍有许多技术难题没有得到很好解 决 , 中文姓名自动识别问题就是其中的一个。由于 它与中文文本的自动分词一样 , 属于中文信息处理 的基础研究领域 , 因而它的研究成果直接影响到中 文信息的深层次研究。汉语的自身特点使得中文信 息自动处理大多是先对要处理的文本进行自动分词 (加入显式分割符) , 然后再在分词的基础上进行词 法、语法、语义等方面的深入分析。而在分词阶 段 , 文本中的人名、地名以及其它专有名词和生词 大多被切分成单字词 , 在这种情形下如不能很好地 解决汉语文本中专有名词生词的识别问题 , 将给其 后的汉语文本的深入分析带来难以逾越的障碍。中 文姓名的自动识别问题就是在这种背景下提出来 的。对这一问题的研究目前采用的技术中主要利用 以下几方面的信息: 姓名用字的频率信息、上下文 信息[1 ,2 ] 、语料库统计信息[2 ] 、词性信息等[3 ] 。本 文的方法是 , 首先对中文人名的构成、姓名用字的 规律及上下文文本信息特征进行充分分析 , 在此基 础上建立起两组规则集 , 将其作用于测试文本 , 获 得初步识别结果 , 再利用大规模语料库的统计信息 对初步识别结果进行概率筛选 , 设定合适的阈值 , 输出最终识别结果。经对 50 多万字的开放语料测 试 , 系统自动识别出 1781 个中文人名 , 在不同的 筛选阈值下获得 90 %以上的识别准确率 , 而召回 率高于 91 %。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值