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文件
- #!/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>
 - # -*- coding: utf-8 -*-
 - import sys
 - #home_dir = "D:/source/NLP/people_daily//"
 - home_dir = "/home/lhb/workspace/CRF_data/"
 - def splitWord(words):
 - uni = words.decode('utf-8')
 - li = list()
 - for u in uni:
 - li.append(u.encode('utf-8'))
 - return li
 - #4 tag
 - #S/B/E/M
 - def get4Tag(li):
 - length = len(li)
 - #print length
 - if length == 1:
 - return ['S']
 - elif length == 2:
 - return ['B','E']
 - elif length > 2:
 - li = list()
 - li.append('B')
 - for i in range(0,length-2):
 - li.append('M')
 - li.append('E')
 - return li
 - #6 tag
 - #S/B/E/M/M1/M2
 - def get6Tag(li):
 - length = len(li)
 - #print length
 - if length == 1:
 - return ['S']
 - elif length == 2:
 - return ['B','E']
 - elif length == 3:
 - return ['B','M','E']
 - elif length == 4:
 - return ['B','M1','M','E']
 - elif length == 5:
 - return ['B','M1','M2','M','E']
 - elif length > 5:
 - li = list()
 - li.append('B')
 - li.append('M1')
 - li.append('M2')
 - for i in range(0,length-4):
 - li.append('M')
 - li.append('E')
 - return li
 - def saveDataFile(trainobj,testobj,isTest,word,handle,tag):
 - if isTest:
 - saveTrainFile(testobj,word,handle,tag)
 - else:
 - saveTrainFile(trainobj,word,handle,tag)
 - def saveTrainFile(fiobj,word,handle,tag):
 - if len(word) > 0:
 - wordli = splitWord(word)
 - if tag == '4':
 - tagli = get4Tag(wordli)
 - if tag == '6':
 - tagli = get6Tag(wordli)
 - for i in range(0,len(wordli)):
 - w = wordli[i]
 - h = handle
 - t = tagli[i]
 - fiobj.write(w + '/t' + h + '/t' + t + '/n')
 - else:
 - #print 'New line'
 - fiobj.write('/n')
 - #B,M,M1,M2,M3,E,S
 - def convertTag(tag):
 - fiobj = open( home_dir + 'people-daily.txt','r')
 - trainobj = open( home_dir + tag + '.train.data','w' )
 - testobj = open( home_dir + tag + '.test.data','w')
 - arr = fiobj.readlines()
 - i = 0
 - for a in arr:
 - i += 1
 - a = a.strip('/r/n/t ')
 - words = a.split(' ')
 - test = False
 - if i % 10 == 0:
 - test = True
 - for word in words:
 - word = word.strip('/t ')
 - if len(word) > 0:
 - i1 = word.find('[')
 - if i1 >= 0:
 - word = word[i1+1:]
 - i2 = word.find(']')
 - if i2 > 0:
 - word = word[:i2]
 - word_hand = word.split('/')
 - w,h = word_hand
 - #print w,h
 - if h == 'nr': #ren min
 - #print 'NR',w
 - if w.find('·') >= 0:
 - tmpArr = w.split('·')
 - for tmp in tmpArr:
 - saveDataFile(trainobj,testobj,test,tmp,h,tag)
 - continue
 - if h != 'm':
 - saveDataFile(trainobj,testobj,test,w,h,tag)
 - if h == 'w':
 - saveDataFile(trainobj,testobj,test,"","",tag) #split
 - trainobj.flush()
 - testobj.flush()
 - if __name__ == '__main__':
 - if len(sys.argv) < 2:
 - print 'tag[6,4] convert raw data to train.data and tag.test.data'
 - else:
 - tag = sys.argv[1]
 - convertTag(tag)
 
下载下来并解压,然后用脚本整理数据,注意home_dir改为语料的目录:
python ./peopledata.py 6
90%数据作为训练数据,10%的数据作为测试数据,生成的文件如:
6.test.data
6.train.data
模板文件的写法如下
template:
- # Unigram
 - U00:%x[-1,0]
 - U01:%x[0,0]
 - U02:%x[1,0]
 - U03:%x[-1,0]/%x[0,0]
 - U04:%x[0,0]/%x[1,0]
 - U05:%x[-1,0]/%x[1,0]
 - # Bigram
 - B
 
%x[row,column]代表的是行和列,[-1,0]表示前1个字的第1列,[0,0]当前字的第1列,[1,0]后1个字的第1列
4 执行和结果查看 
6exec.sh文件
- #!/bin/sh
 - ./crf_learn -f 3 -c 4.0 template 6.train.data 6.model > 6.train.rst
 - ./crf_test -m 6.model 6.test.data > 6.test.rst
 - ./crfeval.py 6.test.rst
 - #./crf_learn -a MIRA -f 3 template train.data model
 - #./crf_test -m model test.data
 - #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文件为
- #!/bin/sh
 - ./crf_learn -f 3 -c 4.0 template 4.train.data 4.model > 4.train.rst
 - ./crf_test -m 4.model 4.test.data > 4.test.rst
 - ./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要好,可以尝试以下。
- # Unigram
 - U00:%x[-1,1]
 - U01:%x[0,1]
 - U02:%x[1,1]
 - U03:%x[-1,1]/%x[0,1]
 - U04:%x[0,1]/%x[1,1]
 - U05:%x[-1,1]/%x[1,1]
 - U06:%x[-1,0]
 - U07:%x[0,0]
 - U08:%x[1,0]
 - U09:%x[-1,0]/%x[0,0]
 - U010:%x[0,0]/%x[1,0]
 - U011:%x[-1,0]/%x[1,0]
 - # Bigram
 - B
 
有某位同学问我要crfeval.py文件,特放出如下:
- #!/usr/bin/python
 - # -*- coding: utf-8 -*-
 - import sys
 - if __name__=="__main__":
 - try:
 - file = open(sys.argv[1], "r")
 - except:
 - print "result file is not specified, or open failed!"
 - sys.exit()
 - wc_of_test = 0
 - wc_of_gold = 0
 - wc_of_correct = 0
 - flag = True
 - for l in file:
 - if l=='/n': continue
 - _, _, g, r = l.strip().split()
 - if r != g:
 - flag = False
 - if r in ('E', 'S'):
 - wc_of_test += 1
 - if flag:
 - wc_of_correct +=1
 - flag = True
 - if g in ('E', 'S'):
 - wc_of_gold += 1
 - print "WordCount from test result:", wc_of_test
 - print "WordCount from golden data:", wc_of_gold
 - print "WordCount of correct segs :", wc_of_correct
 - #查全率
 - P = wc_of_correct/float(wc_of_test)
 - #查准率,召回率
 - R = wc_of_correct/float(wc_of_gold)
 - print "P = %f, R = %f, F-score = %f" % (P, R, (2*P*R)/(P+R))
 
CRF++中文分词使用指南的更多相关文章
- 【中文分词】条件随机场CRF
		
之前介绍的MMEM存在着label bias问题,因此Lafferty et al. [1] 提出了CRF (Conditional Random Field). BTW:比较有意思的是,这篇文章的二 ...
 - crf++实现中文分词简单例子 (Windows crf++0.58 python3)
		
学习自然语言处理的同学都知道,条件随机场(crf)是个好东西.虽然它的原理确实理解起来有点困难,但是对于我们今天用到的这个crf工具crf++,用起来却是挺简单方便的. 今天只是简单试个水,参考别人的 ...
 - Hanlp分词之CRF中文词法分析详解
		
这是另一套基于CRF的词法分析系统,类似感知机词法分析器,提供了完善的训练与分析接口. CRF的效果比感知机稍好一些,然而训练速度较慢,也不支持在线学习. 默认模型训练自OpenCorpus/pku9 ...
 - 97.5%准确率的深度学习中文分词(字嵌入+Bi-LSTM+CRF)
		
本文转载自:http://www.17bigdata.com/97-5%E5%87%86%E7%A1%AE%E7%8E%87%E7%9A%84%E6%B7%B1%E5%BA%A6%E5%AD%A6%E ...
 - 用条件随机场CRF进行字标注中文分词(Python实现)
		
http://www.tuicool.com/articles/zq2yyi http://blog.csdn.net/u010189459/article/details/38546115 主题 ...
 - 基于CRF的中文分词
		
http://biancheng.dnbcw.info/java/341268.html CRF简介 Conditional Random Field:条件随机场,一种机器学习技术(模型) CRF由J ...
 - CRF++进行中文分词实例
		
工具包:https://taku910.github.io/crfpp/#tips 语料:http://sighan.cs.uchicago.edu/bakeoff2005/ 安装: 1)下载linu ...
 - PyTorch 高级实战教程:基于 BI-LSTM CRF 实现命名实体识别和中文分词
		
前言:译者实测 PyTorch 代码非常简洁易懂,只需要将中文分词的数据集预处理成作者提到的格式,即可很快的就迁移了这个代码到中文分词中,相关的代码后续将会分享. 具体的数据格式,这种方式并不适合处理 ...
 - 【中文分词】最大熵马尔可夫模型MEMM
		
Xue & Shen '2003 [2]用两种序列标注模型--MEMM (Maximum Entropy Markov Model)与CRF (Conditional Random Field ...
 
随机推荐
- 小米miui5系统的webview在处理动画事件transitionEnd事件时,竟然要用transitionend才行
			
一般的安卓系统用的是webkitTransitionEnd, 而小米的系统我用了webkitTransitionEnd事件无法执行,只能用transitionend才会被执行,怪
 - LC.exe exited with code -1
			
昨天从win8.1升级到win10之后, 一切还算顺利, 就是升级时间比较长. 但是快下班的时候 遇到一个问题, 是之前在win8.1上没遇到的, 首先代码win8.1 vs2013 上跑的时候一切正 ...
 - 算法系列7《CVN》
			
计算CVN时使用二个64位的验证密钥,KeyA和KeyB. 1) 计算CVN 的数据源包括: 主账号(PAN).卡失效期和服务代码,从左至右顺序编排. 4123456789012345+8701+11 ...
 - SQLite判断某表是否存在
			
SQLite判断表是否存在:其实很简单,只要查看sqlite_master表中是否存在这条数据就可以知道了.SELECT count(*) FROM sqlite_master WHERE type= ...
 - AsyncTask和Handler两种异步方式的实现和区别比较
			
1 AsyncTask实现的原理,和适用的优缺点 AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的程度(可以 ...
 - mvc4 http错误403.14 forbidden
			
1. 检查服务器上是否安装了“HTTP重定向”功能和“静态内容压缩”功能(在添加/删除程序或增加角色处安装). 2. 应用程序池要被配置为“集成” 3. 把.net 4.0安装在iis上 4. 确保自 ...
 - MVC4.0 解决Controllers与Areas中控制器不能同名问题
			
在使用MVC4.0的时候,难免会遇到在根目录下的Controllers中添加的控制器名称可能会跟在Areas中的某个区域下的控制器名称一样.这个时候访问Areas下面的Controller/Actio ...
 - ubuntu 屏幕截图
			
无意间发现这个组合 Fn+Shift+t,按下之后,鼠标变成一个十字, 选择一个范围,okay,可以截图了
 - 邻接矩阵实现Dijkstra算法以及BFS与DFS算法
			
//============================================================================ // Name : MatrixUDG.c ...
 - 关于在 loadView 中改变状态栏的可视性
			
这种问题不知道大家是否遇见过,在此用两句话(时间紧迫,还得加班)分享下今天犯的错误 我把状态栏的的可视性的改变写在了loadView 里面,然后就出现了调用了两次 loadView 和 viewDid ...