预览文章: 介绍一种基于角色标注+字词体位法的人名识别方式-Ansj中文分词

ansj 发布于 2012/09/19 10:21
阅读 208
收藏 1
      大家好.最近在做分词.在分词中遇到了各种各样的问题.在这里选择一个比较有意思的与大家分享. 
      在这里说分词有点老生常谈了.的确.中文分词已经非常成熟了.但是在实体名识别上一直是中文分词的软肋.最近通过对ictclas的学习,和自己的总结.得出了一个还算不错的人名识别系统. 

      目前这种方式已经开源.大家可以参看: https://github.com/ansjsun/ansj_seg , 在线测试: http://www.ansj.org/demo/seg.jsp 


    主要思路是..先粗分,粗分的办法很多.但是在粗分的时候要尽量减少歧异. 
比如 
Java代码
  1. 祝海林在孙健的右面!  
  2. 进过第一步粗分的结果应当为  
  3. 祝/ 海/ 林/ 在/ 孙/ 健/ 的/ 右面/  

在得到以上的粗分序列我们就可以进行进一步分析了 
首先我们得准备一些语料库.对其进行训练. 
比如语料库里面有如下词 
Java代码
  1. 孙强  
  2. 朱海林  
  3. 孙海林  
  4. 祝健  
  5. ....  

因为一般这种语料库在几十万级别.不能一一例举.所以我们拿具有针对这段文本的是个词做分析 
利用字体位方式,我们得到如下统计结果 
Java代码
  1. 孙  2:1:1 \\戴表 孙 这个字在 2个词中的第1个位置出现了1次  
  2. 孙  3:1:1  
  3. 强  2:2:1  
  4. 朱  3:1:1  
  5. 海  3:2:2  
  6. ,,,  


得到如上频率后.我们就可以进行人名识别了 

下面我尽量用伪代码的方式来进行一次分解 
Java代码
  1. public void recogntion(Term[] terms) {  
  2.   
  3.         for (int i = 0; i < terms.length; i++) {  
  4.                         //已名识别从前向后进行识别,4个字,3个字2字人名分别识别  
  5.             for (int j = 4; j > 1; j--) {  
  6.                 freq = term.getTermNatures().personAttr.getFreq(j, 0);\\取得词位频率  
  7.                 if ((freq > 0) ) {//进行初始化校验.当此字在识别位置出现时  
  8.                     tempTerm = nameFind(i, beginFreq, j);//以序列结构进行人名识别  
  9.                 }  
  10.             }  
  11.         }  
  12.     }  
  13.   
  14. public Term nameFnd(int i , int beginFreq , j){  
  15.   
  16. StringBuilder sb = new StringBuilder();  
  17.         int undefinite = 0;  
  18.         skip = false;  
  19.         PersonNatureAttr pna = null;  
  20.         int index = 0;  
  21.         int freq = 0;  
  22.         double allFreq = 0;  
  23.         Term term = null;  
  24.         int i = offe;  
  25.         for (; i < terms.length; i++) {  
  26.             // 走到结尾处识别出来一个名字.  
  27.             term = terms[i] ;  
  28.             pna = term.getTermNatures().personAttr;  
  29.             // 在这个长度的这个位置的词频,如果没有可能就干掉,跳出循环  
  30.             if ((freq = pna.getFreq(size, index)) == 0) {  
  31.                     return null;  
  32.             }  
  33.   
  34.             //增加人名  
  35.             sb.append(term.getName());  
  36.                        //这个人名的概率计算  
  37.             allFreq += Math.log(term.getTermNatures().allFreq+1) ;  
  38.             allFreq += -Math.log((double) (freq));  
  39.             index++;  
  40.   
  41.   
  42.             if (index == size + 2) {//当达到制定长度后跳出循环  
  43.                 break;  
  44.             }  
  45.         }  
  46.   
  47.         double score = -Math.log(factory[size]);  
  48.         score += allFreq ;  
  49.         double endFreq = 0;  
  50.         // 开始寻找结尾词,  
  51.         endFreq = terms[i].getTermNatures().personAttr.end + 1;  
  52.   
  53.         //根据上下文.概率进行公司计算  
  54.         score -= Math.log(endFreq);  
  55.         score -= Math.log(beginFreq);  
  56.           
  57.       
  58.           
  59.         term = new Term(sb.toString(), offe, TermNatures.NR);  
  60.         term.selfScore = score;  
  61.   
  62.                //识别出来的人名进行返回  
  63.         return term;  
  64. }  

进行如上步骤之后.我么得到了一个二维数组的序列: 
如下: 
Java代码
  1. -------->祝  
  2. 祝海林--->海林  
  3. 在  
  4. 孙建  
  5. 的  
  6. 右面  

下一个步骤就是比较 祝海林 和 祝+海林 哪个更像人名的过程了 
其实在这里我们已经构成了隐马尔科夫链.通过贝叶斯定理公式:P(A|B)=P(B|A)*P(A)/P(B) 
加上频率完全可以正确识别出来我们要找的实体名. 
好了大概介绍到这里.有兴趣的朋友可以通过: http://www.ansj.org/ 获取代码.也希望能在这里认识一些志同道合的朋友共同勉励交流
加载中
返回顶部
顶部