您的位置:首页 > 其它

CRF的简单应用--【中文分词】

2018-02-08 23:28 435 查看
之前简单研究了一下CRF的东西,但是原理还是知之甚少…但是大概想尝试一下它的实际作用,查案资料的过程中发现了所谓的中文分词的方法,尝试一下。

首先需要训练的语料,这个越大越好,但是为了处理方便只用了80M的语料啊,下载可以从这里

http://pan.baidu.com/s/1bnhXX6Z

当然这个是我们的一般的语料,想要成为CRF++的训练数据还需要进行加工一番。写一个小程序解决一下,然后我们需要讨论一下训练数据中的二三列了!我们首先看一下example中的样例

Confidence NN B
in IN O
the DT B
pound NN I
is VBZ O
widely RB O
expected VBN O
to TO O
take VB O
another DT B
sharp JJ I
dive NN I
if IN O
trade NN B
figures NNS I


查看的资料中说第三列起到主要作用,第二列主要是词性,第三列我们是自由发挥的,我们这样设计:

第二列我们标注是标点符号或者是文字,如果是标点符号标注为S,是文字则位W

第三列为分词的模式,采用4-tag标注法,(B(Begin,词首), E(End,词尾), M(Middle,词中), S(Single,单字词))

举个栗子(随便举例,不一定准确啊,只是为了说明设计方法)

我 W S
说 W S
: S S
“ S S
我 W S
真 W S
是 W S
个 W S
大 W B
垃 W M
圾 W E
” S S


然后那么多材料写个程序处理一下

注意啊!!!我们这里不需要全部的内容,只要几十个txt就行了,只是实验而已,要是文本太大了model学习就十万年…

#_*_ coding:utf-8 _*_
import jieba
import os
import codecs
goods_path = r'自己的目录哟\SogouC.reduced\Reduced\233'
write_path = r'自己的目录哟\train.txt'
test_txt = r'自己的目录哟\SogouC.reduced\Reduced\C000008\1107.txt'
'''处理1行内容'''
def handle_one_line(f,txt_dir,oneline):
sign = ['“','”','。',',','!','—','@','#','¥','%','&','*','(',')','+','-','·','`','~',';',':','‘','’','<','>','《','》','?','/','[',']','{','}','|','\\',' ','、']
for i in range(len(sign)):
sign[i] = sign[i].decode('gbk')
seg_list = jieba.cut(oneline, cut_all=False)
oneline = ' '.join(seg_list)
#print oneline
oneline = oneline.split(' ')
#print oneline
for content in oneline:
if len(content)==0:
continue
elif len(content)==1:
if content in sign:
second = 'S'
else:
second = 'W'
f.write(content[0]+' '+second+' S\n')
else:
f.write(content[0]+' W B\n')
for i in range(1,len(content)-1):
f.write(content[i]+' W M\n')
f.write(content[len(content)-1]+' W E\n')

'''处理1个txt'''
def hadnle_one_txt(f,txt_dir):
print "Prepare hand ",txt_dir
temp_f = open(txt_dir,'r')
content = temp_f.readlines()
for oneline in content:
oneline = oneline.replace(' ','').replace('\n','').replace(' ','')
try:
oneline=oneline.decode('gbk')
except:
print txt_dir,'is a UTF-8 file!'
handle_one_line(f,txt_dir,oneline)
print "Finish ",txt_dir
temp_f.close()

'''总函数,提取处理所有txt'''
def handle(file_dir):
global write_path
f = codecs.open(write_path,'a','utf-8')
for root, dirs, files in os.walk(file_dir):
if len(files)==0:
continue
#print(root) #当前目录路径
#print(dirs) #当前路径下所有子目录
#print(files) #当前路径下所有非目录子文件
for txt_name in files:
read_path = root+'\\'+txt_name
hadnle_one_txt(f,read_path)
f.close()
if __name__ == '__main__':
#handle(goods_path)
if os.path.exists(write_path):
os.remove(write_path)
#hadnle_one_txt(test_txt)#测试使用来着
handle(goods_path)


然后我们的template基本上可以沿用之前的默认的,但是我们想要分词,肯定主要还是用到第一例和第二列的关系,这里我也不太会设计太复杂的,主要利用了第一列的内容…第二列就不太知道咋玩了(这个可以日后研究一下人家是怎么构造板子的)

# Unigram
U00:%x[-3,0]
U01:%x[-2,0]
U02:%x[-1,0]
U03:%x[0,0]
U04:%x[1,0]
U05:%x[2,0]
U06:%x[3,0]

U07:%x[-1,0]/%x[0,0]
U08:%x[0,0]/%x[1,0]

U09:%x[-2,0]/%x[-1,0]/%x[0,0]
U10:%x[-1,0]/%x[0,0]/%x[1,0]
U11:%x[0,0]/%x[1,0]/%x[2,0]

U12:%x[-3,0]/x[-2,0]/%x[-1,0]/%x[0,0]
U13:%x[-2,0]/x[-1,0]/%x[0,0]/%x[1,0]
U14:%x[-1,0]/x[0,0]/%x[1,0]/%x[2,0]
U15:%x[0,0]/x[1,0]/%x[2,0]/%x[3,0]

# Bigram
B


然后我们需要构造测试数据,简单构造一下,测试语句为

人群瞬间寂静下来,人们抬头看着,那架可能烧死了几十人的穿梭机轰鸣着从停泊区升起,拖着白色的尾迹直上高空,然后转向东方。人们似乎不敢相信眼前发生的事。只过了十几秒钟,又一架穿梭机从停泊区起飞,这次距离他们更近,轰鸣、火光和热浪让人群由僵滞陷人极度的狂乱中。接下来,第三架,第四架……停泊区的穿梭机相继强行发射,团团烈焰中,焦黑的人体拖着烟火在空中横飞,停泊区变成了火葬场!

老李躺在正中的一张床上,看上去很平静,云天明想到他们还没有告别过,心里越来越沉重。两个法律公证人在里面完成了公证程序,老李在公证书上签了字。公证人出来后,又有一个人进去为他讲解最后的操作程序。这人身着白大褂,不知是不是医生。他首先指着床前的一个大屏幕,问老李是否能看清上面显示的字,老李说可以后,他又让老一李试试是否能用右手移动床边的鼠标点击屏幕上的按钮,并特别说明,如果不方使.还有别的方式,老李试了试也可以。这时云天明想到.老一李曾告诉过他,自己从没用过电脑、取钱只能到银行排队,那么这是他有生第一次用鼠标了。穿白大褂的人接着告诉老李,屏幕上将显示一个问题,并重复显示五次,问题下面从0到5有六个按钮,每一次如果老李做肯定的回答,就按照提示按动一个按钮,提示的数字是1到5中随机的一个——之所以这样做,而没有用“是”或“否”按钮,是为了防止病人在无意识状态下反复按动同一个按钮;如果否定,则都是按0,这种情况下安乐程序将立刻中止。一名护士进去,把一个针头插到老李左臂上,针头通过一个软管与一台笔记本电脑大小的自动注射机相连。先前那名指导者掏出一个东西,打开层层密封,是一支小玻璃管,里面有淡黄色的液体,他小心地把那个玻璃管装到注射机上,然后和护士一起走出来。安乐室里只剩老李一人了。安乐程序正式开始,屏幕显示问题,同时由一个柔美的女声读出来:你要结束自己的生命吗?是,请按3键;否,请按0键。


处理数据的代码如下

#_*_ coding:utf-8 _*_
import jieba
import os
import codecs
write_path_for_test = r'C:\Users\Administrator\Desktop\ACM比赛及其补题\test.txt'
'''处理1行内容'''
def handle_one_line(f,txt_dir,oneline):
sign = ['“','”','。',',','!','—','@','#','¥','%','&','*','(',')','+','-','·','`','~',';',':','‘','’','<','>','《','》','?','/','[',']','{','}','|','\\',' ','、']
for i in range(len(sign)):
sign[i] = sign[i].decode('gbk')
seg_list = jieba.cut(oneline, cut_all=False)
oneline = ' '.join(seg_list)
#print oneline
oneline = oneline.split(' ')
#print oneline
for content in oneline:
if len(content)==0:
continue
elif len(content)==1:
if content in sign:
second = 'S'
else:
second = 'W'
f.write(content[0]+' '+second+' S\n')
else:
f.write(content[0]+' W B\n')
for i in range(1,len(content)-1):
f.write(content[i]+' W M\n')
f.write(content[len(content)-1]+' W E\n')

if __name__ == '__main__':
s1 = '人群瞬间寂静下来,人们抬头看着,那架可能烧死了几十人的穿梭机轰鸣着从停泊区升起,拖着白色的尾迹直上高空,然后转向东方。人们似乎不敢相信眼前发生的事。只过了十几秒钟,又一架穿梭机从停泊区起飞,这次距离他们更近,轰鸣、火光和热浪让人群由僵滞陷人极度的狂乱中。接下来,第三架,第四架……停泊区的穿梭机相继强行发射,团团烈焰中,焦黑的人体拖着烟火在空中横飞,停泊区变成了火葬场!'
s2 = '老李躺在正中的一张床上,看上去很平静,云天明想到他们还没有告别过,心里越来越沉重。两个法律公证人在里面完成了公证程序,老李在公证书上签了字。公证人出来后,又有一个人进去为他讲解最后的操作程序。这人身着白大褂,不知是不是医生。他首先指着床前的一个大屏幕,问老李是否能看清上面显示的字,老李说可以后,他又让老一李试试是否能用右手移动床边的鼠标点击屏幕上的按钮,并特别说明,如果不方使.还有别的方式,老李试了试也可以。这时云天明想到.老一李曾告诉过他,自己从没用过电脑、取钱只能到银行排队,那么这是他有生第一次用鼠标了。穿白大褂的人接着告诉老李,屏幕上将显示一个问题,并重复显示五次,问题下面从0到5有六个按钮,每一次如果老李做肯定的回答,就按照提示按动一个按钮,提示的数字是1到5中随机的一个——之所以这样做,而没有用“是”或“否”按钮,是为了防止病人在无意识状态下反复按动同一个按钮;如果否定,则都是按0,这种情况下安乐程序将立刻中止。一名护士进去,把一个针头插到老李左臂上,针头通过一个软管与一台笔记本电脑大小的自动注射机相连。先前那名指导者掏出一个东西,打开层层密封,是一支小玻璃管,里面有淡黄色的液体,他小心地把那个玻璃管装到注射机上,然后和护士一起走出来。安乐室里只剩老李一人了。安乐程序正式开始,屏幕显示问题,同时由一个柔美的女声读出来:你要结束自己的生命吗?是,请按3键;否,请按0键。'
if os.path.exists(write_path_for_test):
os.remove(write_path_for_test)
f = codecs.open(write_path_for_test,'a','utf-8')
handle_one_line(f,write_path_for_test,s1)
handle_one_line(f,write_path_for_test,s2)
f.close()


之后执行

crf_learn -c 10.0 template train.txt model
crf_test  -m model test.txt >>result.txt


最后写个程序验证一下正确率…

#_*_ coding:utf-8 _*_
import jieba
import os
import codecs
check_file = r'自己的目录哟\result.txt'
f = codecs.open(check_file,'r','utf-8')
total = 0
right = 0
wrong = 0
for lines in f.readlines():
temp = lines.replace('\r\n','').split(' ')
if len(temp)<4:
continue
total+=1
if temp[2]==temp[3]:
right+=1
else :
wrong+=1
print '='*50
print 'Total number is ',total
print 'Right number is ',right
print 'Wrong number is ',wrong
print 'Accuracy is ',str((right*1.0/total)*100)[:6]+'%'
print '='*50

f.close()




测试结果看着有点高啊…讲道理是不应该的,但是毕竟这是一次小测试吧,而且错误的数量还是很多的…数据集也比较小,后面再进行研究吧。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: