CRF++使用

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

 

 

1 下载和安装

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

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

没有gcc/g++/make请安装
% ./configure
% make[如果path.h报错 加上#inlcude<iostream>头文件]
% 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/python
  2. #-*-coding:utf-8-*-
  3. importsys
  4. #home_dir="D:/source/NLP/people_daily//"
  5. home_dir="/home/lhb/workspace/CRF_data/"
  6. defsplitWord(words):
  7. uni=words.decode('utf-8')
  8. li=list()
  9. foruinuni:
  10. li.append(u.encode('utf-8'))
  11. returnli
  12. #4tag
  13. #S/B/E/M
  14. defget4Tag(li):
  15. length=len(li)
  16. #printlength
  17. iflength==1:
  18. return['S']
  19. eliflength==2:
  20. return['B','E']
  21. eliflength>2:
  22. li=list()
  23. li.append('B')
  24. foriinrange(0,length-2):
  25. li.append('M')
  26. li.append('E')
  27. returnli
  28. #6tag
  29. #S/B/E/M/M1/M2
  30. defget6Tag(li):
  31. length=len(li)
  32. #printlength
  33. iflength==1:
  34. return['S']
  35. eliflength==2:
  36. return['B','E']
  37. eliflength==3:
  38. return['B','M','E']
  39. eliflength==4:
  40. return['B','M1','M','E']
  41. eliflength==5:
  42. return['B','M1','M2','M','E']
  43. eliflength>5:
  44. li=list()
  45. li.append('B')
  46. li.append('M1')
  47. li.append('M2')
  48. foriinrange(0,length-4):
  49. li.append('M')
  50. li.append('E')
  51. returnli
  52. defsaveDataFile(trainobj,testobj,isTest,word,handle,tag):
  53. ifisTest:
  54. saveTrainFile(testobj,word,handle,tag)
  55. else:
  56. saveTrainFile(trainobj,word,handle,tag)
  57. defsaveTrainFile(fiobj,word,handle,tag):
  58. iflen(word)>0:
  59. wordli=splitWord(word)
  60. iftag=='4':
  61. tagli=get4Tag(wordli)
  62. iftag=='6':
  63. tagli=get6Tag(wordli)
  64. foriinrange(0,len(wordli)):
  65. w=wordli[i]
  66. h=handle
  67. t=tagli[i]
  68. fiobj.write(w+'/t'+h+'/t'+t+'/n')
  69. else:
  70. #print'Newline'
  71. fiobj.write('/n')
  72. #B,M,M1,M2,M3,E,S
  73. defconvertTag(tag):
  74. fiobj=open(home_dir+'people-daily.txt','r')
  75. trainobj=open(home_dir+tag+'.train.data','w')
  76. testobj=open(home_dir+tag+'.test.data','w')
  77. arr=fiobj.readlines()
  78. i=0
  79. forainarr:
  80. i+=1
  81. a=a.strip('/r/n/t')
  82. words=a.split('')
  83. test=False
  84. ifi%10==0:
  85. test=True
  86. forwordinwords:
  87. word=word.strip('/t')
  88. iflen(word)>0:
  89. i1=word.find('[')
  90. ifi1>=0:
  91. word=word[i1+1:]
  92. i2=word.find(']')
  93. ifi2>0:
  94. word=word[:i2]
  95. word_hand=word.split('/')
  96. w,h=word_hand
  97. #printw,h
  98. ifh=='nr':#renmin
  99. #print'NR',w
  100. ifw.find('·')>=0:
  101. tmpArr=w.split('·')
  102. fortmpintmpArr:
  103. saveDataFile(trainobj,testobj,test,tmp,h,tag)
  104. continue
  105. ifh!='m':
  106. saveDataFile(trainobj,testobj,test,w,h,tag)
  107. ifh=='w':
  108. saveDataFile(trainobj,testobj,test,"","",tag)#split
  109. trainobj.flush()
  110. testobj.flush()
  111. if__name__=='__main__':
  112. iflen(sys.argv)<2:
  113. print'tag[6,4]convertrawdatatotrain.dataandtag.test.data'
  114. else:
  115. tag=sys.argv[1]
  116. 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. #Bigram
  9. 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-f3-c4.0template6.train.data6.model>6.train.rst
  3. ./crf_test-m6.model6.test.data>6.test.rst
  4. ./crfeval.py6.test.rst
  5. #./crf_learn-aMIRA-f3templatetrain.datamodel
  6. #./crf_test-mmodeltest.data
  7. #rm-fmodel



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-f3-c4.0template4.train.data4.model>4.train.rst
  3. ./crf_test-m4.model4.test.data>4.test.rst
  4. ./crfeval.py4.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. #Bigram
  15. B

 

 

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

 

 

[python] view plain copy
 
  1. #!/usr/bin/python
  2. #-*-coding:utf-8-*-
  3. importsys
  4. if__name__=="__main__":
  5. try:
  6. file=open(sys.argv[1],"r")
  7. except:
  8. print"resultfileisnotspecified,oropenfailed!"
  9. sys.exit()
  10. wc_of_test=0
  11. wc_of_gold=0
  12. wc_of_correct=0
  13. flag=True
  14. forlinfile:
  15. ifl=='/n':continue
  16. _,_,g,r=l.strip().split()
  17. ifr!=g:
  18. flag=False
  19. ifrin('E','S'):
  20. wc_of_test+=1
  21. ifflag:
  22. wc_of_correct+=1
  23. flag=True
  24. ifgin('E','S'):
  25. wc_of_gold+=1
  26. print"WordCountfromtestresult:",wc_of_test
  27. print"WordCountfromgoldendata:",wc_of_gold
  28. print"WordCountofcorrectsegs:",wc_of_correct
  29. #查全率
  30. P=wc_of_correct/float(wc_of_test)
  31. #查准率,召回率
  32. R=wc_of_correct/float(wc_of_gold)
  33. print"P=%f,R=%f,F-score=%f"%(P,R,(2*P*R)/(P+R))

 

你可能感兴趣的:(使用)