zoukankan      html  css  js  c++  java
  • CRF++中文分词使用指南

    http://blog.csdn.net/marising/article/details/5769653

    前段时间写了中文分词的一些记录里面提到了CRF的分词方法,近段时间又研究了一下,特把方法写下来,以备忘,另外,李沫南同学优化过CRF++,见:http://www.coreseek.cn/opensource/CRF/。我觉得CRF++还有更大的优化空间,以后有时间再搞。

    人民日报语料是分好词的,我下面贴出的代码就是把语料整理为CRF需要的训练数据,直接修改模板训练即可。不过有下面的同学给出了更加详细的资料,请各位可以参考:

    原始程序确实有一些问题,估计作者没时间修正了,我修改好了楼主的程序并分词成功,并上传了代码和处理好的训练、测试语料:

    http://x-algo.cn/index.php/2016/02/27/crf-of-chinese-word-segmentation/ 

    1 下载和安装

    CRF的概念,请google,我就不浪费资源啦。官方地址如下:http://crfpp.sourceforge.net/

    我用的是Ubutnu,所以,下载的是源码:http://sourceforge.net/projects/crfpp/files/ 下载CRF++-0.54.tar.gz

    没有gcc/g++/make请安装
    % ./configure 
    % make
    % sudo make install

    2 测试和体验 
    在源码包中有example,可以执行./exec.sh体验一下
    exec.sh   #训练和测试脚本
    template #模板文件
    test.data #测试文件
    train.data #训练文件
    可以打开看看


    3 语料整理和模板编写 

    我采用的是6Tag和6Template的方式
    S,单个词;B,词首;E,词尾;M1/M2/M,词中


    1个字的词:
    和 S
    2个字的词(注意是实际上是一个字一行,我为了排版,改为横排的了):
    中 B 国 E
    3个字的词:
    进 B 一 M 步 E
    5个字的词:
    发 B 展 M1 中 M2 国 M 家 E
    跟多字的词
    中 B 华 M1 人 M2 民 M 共 M 和 M国 E
    标点符号作为单词(S表示)

    bamboo 项目中下载:people-daily.txt.gz
    pepoledata.py文件

    [python] view plain copy
     
    1. #!/usr/bin/<a href="http://lib.csdn.net/base/11" class='replace_word' title="undefined" target='_blank' style='color:#df3434; font-weight:bold;'>Python</a>  
    2.   
    3. # -*- coding: utf-8 -*-  
    4.   
    5.   
    6.   
    7. import sys  
    8.   
    9.   
    10.   
    11. #home_dir = "D:/source/NLP/people_daily//"  
    12.   
    13. home_dir = "/home/lhb/workspace/CRF_data/"  
    14. def splitWord(words):  
    15.     uni = words.decode('utf-8')  
    16.     li = list()      
    17.     for u in uni:  
    18.     li.append(u.encode('utf-8'))  
    19.     return li     
    20.   
    21.   
    22. #4 tag  
    23.   
    24. #S/B/E/M  
    25. def get4Tag(li):  
    26.     length = len(li)  
    27.     #print length  
    28.     if length   == 1:  
    29.     return ['S']  
    30.     elif length == 2:  
    31.     return ['B','E']  
    32.     elif length > 2:  
    33.     li = list()  
    34.     li.append('B')  
    35.     for i in range(0,length-2):  
    36.         li.append('M')  
    37.     li.append('E')  
    38.     return li  
    39. #6 tag  
    40. #S/B/E/M/M1/M2  
    41. def get6Tag(li):  
    42.     length = len(li)  
    43.     #print length  
    44.     if length   == 1:  
    45.     return ['S']  
    46.     elif length == 2:  
    47.     return ['B','E']  
    48.     elif length == 3:  
    49.     return ['B','M','E']  
    50.     elif length == 4:  
    51.     return ['B','M1','M','E']  
    52.     elif length == 5:  
    53.     return ['B','M1','M2','M','E']  
    54.     elif length > 5:  
    55.     li = list()  
    56.     li.append('B')  
    57.     li.append('M1')  
    58.     li.append('M2')  
    59.     for i in range(0,length-4):  
    60.         li.append('M')  
    61.     li.append('E')  
    62.     return li  
    63.   
    64. def saveDataFile(trainobj,testobj,isTest,word,handle,tag):  
    65.     if isTest:  
    66.     saveTrainFile(testobj,word,handle,tag)  
    67.     else:  
    68.     saveTrainFile(trainobj,word,handle,tag)  
    69.   
    70. def saveTrainFile(fiobj,word,handle,tag):   
    71.     if len(word) > 0:  
    72.     wordli = splitWord(word)  
    73.     if tag == '4':  
    74.         tagli = get4Tag(wordli)  
    75.     if tag == '6':  
    76.         tagli = get6Tag(wordli)  
    77.     for i in range(0,len(wordli)):  
    78.         w = wordli[i]  
    79.         h = handle  
    80.         t = tagli[i]  
    81.         fiobj.write(w + '/t' + h + '/t' + t + '/n')  
    82.     else:  
    83.     #print 'New line'  
    84.     fiobj.write('/n')  
    85.   
    86. #B,M,M1,M2,M3,E,S  
    87. def convertTag(tag):      
    88.     fiobj    = open( home_dir + 'people-daily.txt','r')  
    89.     trainobj = open( home_dir + tag + '.train.data','w' )  
    90.     testobj  = open( home_dir + tag + '.test.data','w')  
    91.   
    92.     arr = fiobj.readlines()  
    93.     i = 0  
    94.     for a in arr:  
    95.     i += 1  
    96.     a = a.strip('/r/n/t ')  
    97.     words = a.split(' ')  
    98.     test = False  
    99.     if i % 10 == 0:  
    100.         test = True  
    101.     for word in words:  
    102.         word = word.strip('/t ')  
    103.         if len(word) > 0:          
    104.         i1 = word.find('[')  
    105.         if i1 >= 0:  
    106.             word = word[i1+1:]  
    107.         i2 = word.find(']')  
    108.         if i2 > 0:  
    109.             word = word[:i2]  
    110.         word_hand = word.split('/')  
    111.         w,h = word_hand  
    112.         #print w,h  
    113.         if h == 'nr':    #ren min  
    114.             #print 'NR',w  
    115.             if w.find('·') >= 0:  
    116.             tmpArr = w.split('·')  
    117.             for tmp in tmpArr:  
    118.                 saveDataFile(trainobj,testobj,test,tmp,h,tag)  
    119.             continue  
    120.         if h != 'm':  
    121.             saveDataFile(trainobj,testobj,test,w,h,tag)  
    122.           
    123.         if h == 'w':  
    124.             saveDataFile(trainobj,testobj,test,"","",tag) #split  
    125.   
    126.     trainobj.flush()  
    127.     testobj.flush()  
    128.   
    129. if __name__ == '__main__':      
    130.     if len(sys.argv) < 2:  
    131.     print 'tag[6,4] convert raw data to train.data and tag.test.data'  
    132.     else:  
    133.     tag = sys.argv[1]  
    134.     convertTag(tag)  


    下载下来并解压,然后用脚本整理数据,注意home_dir改为语料的目录:
    python ./peopledata.py 6

    90%数据作为训练数据,10%的数据作为测试数据,生成的文件如:
    6.test.data
    6.train.data

    模板文件的写法如下
    template:

    [python] view plain copy
     
    1. # Unigram  
    2. U00:%x[-1,0]  
    3. U01:%x[0,0]  
    4. U02:%x[1,0]  
    5. U03:%x[-1,0]/%x[0,0]  
    6. U04:%x[0,0]/%x[1,0]  
    7. U05:%x[-1,0]/%x[1,0]  
    8.   
    9. # Bigram  
    10. B  



    %x[row,column]代表的是行和列,[-1,0]表示前1个字的第1列,[0,0]当前字的第1列,[1,0]后1个字的第1列

    4 执行和结果查看 
    6exec.sh文件

    [python] view plain copy
     
    1. #!/bin/sh  
    2. ./crf_learn -f 3 -c 4.0 template 6.train.data 6.model > 6.train.rst  
    3. ./crf_test -m 6.model 6.test.data > 6.test.rst  
    4. ./crfeval.py 6.test.rst  
    5.   
    6. #./crf_learn -a MIRA -f 3 template train.data model  
    7. #./crf_test -m model test.data  
    8. #rm -f model  



    WordCount from test result: 109805
    WordCount from golden data: 109948
    WordCount of correct segs : 106145
    P = 0.966668, R = 0.965411, F-score = 0.966039 

    5 调整Tag和模板
    4 Tag S/B/M/E 比 6Tag 去掉了M1和M2
    python ./peopledata.py 4
    4exec.sh文件为

    [python] view plain copy
     
    1. #!/bin/sh  
    2. ./crf_learn -f 3 -c 4.0 template 4.train.data 4.model > 4.train.rst  
    3. ./crf_test -m 4.model 4.test.data > 4.test.rst  
    4. ./crfeval.py 4.test.rst  


    4Tag的效果为
    lhb@localhost:~/workspace/CRF_data$ ./crfeval.py 4.test.rst 
    ordCount from test result: 109844
    WordCount from golden data: 109948
    WordCount of correct segs : 105985
    P = 0.964868, R = 0.963956, F-score = 0.964412

    6Tag的效果比4Tag有细微的差距,当然是6Tag好。


    6Tag 训练时间为
    10062.00s
    4tag的训练时间为
    4208.71s

    6Tag的标注方法差异

    1)把M放在E之前:
    发 B 展 M1 中 M2 国 M 家 E
    2)把M放在B后
    发 B 展 M 中 M1 国 M2 家 E
    3)把M放在M1和M2之间:
    发 B 展 M1 中 M 国 M2 家 E
    第1种方式效果最好,有细微的差距。
    template的编写

    我尝试过12行模板的编写,把词性作为一个计算因素,但是速度实在是很慢,没跑完,我就关机了。效果应该比6 template要好,可以尝试以下。

    [python] view plain copy
     
    1. # Unigram  
    2. U00:%x[-1,1]  
    3. U01:%x[0,1]  
    4. U02:%x[1,1]  
    5. U03:%x[-1,1]/%x[0,1]  
    6. U04:%x[0,1]/%x[1,1]  
    7. U05:%x[-1,1]/%x[1,1]  
    8. U06:%x[-1,0]  
    9. U07:%x[0,0]  
    10. U08:%x[1,0]  
    11. U09:%x[-1,0]/%x[0,0]  
    12. U010:%x[0,0]/%x[1,0]  
    13. U011:%x[-1,0]/%x[1,0]  
    14.   
    15. # Bigram  
    16. B  

    有某位同学问我要crfeval.py文件,特放出如下:

    [python] view plain copy
     
    1. #!/usr/bin/python  
    2. # -*- coding: utf-8 -*-  
    3.   
    4. import sys  
    5.   
    6. if __name__=="__main__":  
    7.     try:  
    8.         file = open(sys.argv[1], "r")  
    9.     except:  
    10.         print "result file is not specified, or open failed!"  
    11.         sys.exit()  
    12.       
    13.     wc_of_test = 0  
    14.     wc_of_gold = 0  
    15.     wc_of_correct = 0  
    16.     flag = True  
    17.       
    18.     for l in file:  
    19.         if l=='/n': continue  
    20.       
    21.         _, _, g, r = l.strip().split()  
    22.        
    23.         if r != g:  
    24.             flag = False  
    25.       
    26.         if r in ('E', 'S'):  
    27.             wc_of_test += 1  
    28.             if flag:  
    29.                 wc_of_correct +=1  
    30.             flag = True  
    31.       
    32.         if g in ('E', 'S'):  
    33.             wc_of_gold += 1  
    34.   
    35.     print "WordCount from test result:", wc_of_test  
    36.     print "WordCount from golden data:", wc_of_gold  
    37.     print "WordCount of correct segs :", wc_of_correct  
    38.               
    39.     #查全率  
    40.     P = wc_of_correct/float(wc_of_test)  
    41.     #查准率,召回率  
    42.     R = wc_of_correct/float(wc_of_gold)  
    43.       
    44.     print "P = %f, R = %f, F-score = %f" % (P, R, (2*P*R)/(P+R))  
  • 相关阅读:
    python数据分析之ipython
    Django之文件下载
    mongodb学习之:主从复制
    Django之高级视图与URL
    Django之request对象
    tornado安全应用之用户认证
    tornado安全应用之cookie
    tornado之异步web服务二
    【原创】Linux基础之测试域名IP端口连通性
    【原创】大数据基础之Mesos+Marathon+Docker部署nginx
  • 原文地址:https://www.cnblogs.com/DjangoBlog/p/6207617.html
Copyright © 2011-2022 走看看