欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  科技

CRF的简单应用

程序员文章站 2022-07-09 23:11:44
之前简单研究了一下CRF的东西,但是原理还是知之甚少…但是大概想尝试一下它的实际作用,查案资料的过程中发现了所谓的中文分词的方法,尝试一下。 当然这个是我们的一般的语料,想要成...

之前简单研究了一下CRF的东西,但是原理还是知之甚少…但是大概想尝试一下它的实际作用,查案资料的过程中发现了所谓的中文分词的方法,尝试一下。
当然这个是我们的一般的语料,想要成为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()
CRF的简单应用

测试结果看着有点高啊…讲道理是不应该的,但是毕竟这是一次小测试吧,而且错误的数量还是很多的…数据集也比较小,后面再进行研究吧。