zoukankan      html  css  js  c++  java
  • 用条件随机场CRF进行字标注中文分词(Python实现)

    http://www.tuicool.com/articles/zq2yyi

            本文运用字标注法进行中文分词,使用4-tag对语料进行字标注,观察分词效果。模型方面选用开源的条件随机场工具包“ CRF++: Yet Another CRF toolkit ”进行分词。

            本文使用的中文语料资源是SIGHAN提供的 backoff 2005 语料,目前封闭测试最好的结果是4-tag+CFR标注分词,在北大语料库上可以在准确率,召回率以及F值上达到92%以上的效果,在微软语料库上可以到达96%以上的效果。

    第一部分 条件随机场模型工具包安装说明

            在Linux或者Mac OS系统下,下载C++源代码安装包(这里用的是  CRF++-0.58.tar.gz  )之后,按照如下步骤进行安装:

            1.进入到代码主目录后,正常按照“configure & make & (sudo) make install就可以完成C++库的安装。

            2.再进入到子目录python下,安装python包:python setup.py build & (sudo) python setup.py install,这个python库是通过强大的 SWIG 生成的。

            3.安装完毕之后,可以在python解释器下测试,是否能成功import CRFPP,如果ok,则准备工作就绪。

            注意:在安装过程中或者是后面的运行过程中(具体什么时候我忘记了),如果报出下面的错误:

            ImportError: libcrfpp.so.0: cannot open shared object file: No such file or directory 

            错误的原因是未能引入libcrfpp.so.0库,查找库文件存在,于是建立链接: 

            32位系统  ln -s /usr/local/lib/libcrfpp.so.* /usr/lib/ 

            64位系统  ln -s /usr/local/lib/libcrfpp.so.* /usr/lib64/ 

            问题解决。

    第二部分 模型相关知识介绍

            在CRF++ example里有个seg目录,这个seg目录对应的是一个日文分词的样例,正好可以套用到我们的中文分词中来。在安装包目录下,执行cd example和cd seg命令后,切换到seg目录后,发现有4个文件:

           

    exec.sh(执行脚本)

            template(特征模板)

            test.data(测试集)

            train.data(训练集)


           有了这4个文件,我们可以做得事情就比较简单,只要按测试集,训练集的格式准备数据就可以了,特征模板和执行脚本可以套用,不过这里简单解读一下这几个CRF++文件。首先来看训练集:

    毎 k   B
    日 k   I
    新 k   I
    聞 k   I
    社 k   I
    特 k   B
    別 k   I
    顧 k   B
    問 k   I                                                 
    4 n   B

            这里第一列是待分词的日文字,第二列暂且认为其是词性标记,第三列是字标注中的2-tag(B, I)标记,这个很重要,对于我们需要准备的训练集,主要是把这一列的标记做好,不过需要注意的是,其断句是靠空行来完成的。

            再来看测试集的格式:

    よ h   I
    っ h   I
    て h   I
    私 k   B
    た h   B
    ち h   I
    の h   B                                                     
    世 k   B
    代 k   I
    が h   B

            同样也有3列,第一列是日文字,第二列第三列与上面是相似的,不过在测试集里第三列主要是占位作用。事实上,CRF++对于训练集和测试集文件格式的要求是比较灵活的,首先需要多列,但不能不一致,既在一个文件里有的行是两列,有的行是三列;其次第一列代表的是需要标注的“字或词”,最后一列是输出位”标记tag”,如果有额外的特征,例如词性什么的,可以加到中间列里,所以训练集或者测试集的文件最少要有两列。

            接下里我们再来详细的分析一下特征模板文件:

    # Unigram
    U00:%x[-2,0]
    U01:%x[-1,0]
    U02:%x[0,0]
    U03:%x[1,0]
    U04:%x[2,0]
    U05:%x[-2,0]/%x[-1,0]/%x[0,0]
    U06:%x[-1,0]/%x[0,0]/%x[1,0]
    U07:%x[0,0]/%x[1,0]/%x[2,0]
    U08:%x[-1,0]/%x[0,0]                                          
    U09:%x[0,0]/%x[1,0]
    
    # Bigram
    B

            关于CRF++中特征模板的说明和举例,请大家参考官方文档上的“ Preparing feature templates

    ”这一节,而以下部分的说明拿上述日文分词数据举例。在特征模板文件中,每一行(如U00:%x[-2,0])代表一个特征,而宏“%x[行位置,列位置]”则代表了相对于当前指向的token的行偏移和列的绝对位置,以上述训练集为例,如果当前扫描到“新 k I”这一行,

    毎 k   B
    日 k   I
    新 k   I   <== 扫描到这一行,代表当前位置
    聞 k   I
    社 k   I
    特 k   B
    別 k   I
    顧 k   B
    問 k   I
    4 n   B

            那么依据特征模板文件抽取的特征如下:

    # Unigram
    U00:%x[-2,0] ==> 毎
    U01:%x[-1,0] ==> 日
    U02:%x[0,0]  ==> 新
    U03:%x[1,0]  ==> 聞
    U04:%x[2,0]  ==> 社
    U05:%x[-2,0]/%x[-1,0]/%x[0,0] ==> 每/日/新
    U06:%x[-1,0]/%x[0,0]/%x[1,0]  ==> 日/新/聞
    U07:%x[0,0]/%x[1,0]/%x[2,0]   ==> 新/聞/社
    U08:%x[-1,0]/%x[0,0]          ==> 日/新
    U09:%x[0,0]/%x[1,0]           ==> 新/聞
    
    # Bigram
    B

            CRF++里将特征分成两种类型,一种是Unigram的,“U”起头,另外一种是Bigram的,“B”起头。对于Unigram的特征,假如一个特征模板是”U01:%x[-1,0]“, CRF++会自动的生成一组特征函数(func1 … funcN) 集合:

    func1 = if (output = B and feature="U01:日") return 1 else return 0
    func2 = if (output = I and feature="U01:日") return 1 else return 0
    ....
    funcXX = if (output = B and feature="U01:問") return 1  else return 0
    funcXY = if (output = I and feature="U01:問") return 1  else return 0

    生成的特征函数的数目 = (L * N),其中L是输出的类型的个数,这里是B,I这两个tag,N是通过模板扩展出来的所有单个字符串(特征)的个数,这里指的是在扫描所有训练集的过程中找到的日文字(特征)。

    而Bigram特征主要是当前的token和前面一个位置token的自动组合生成的bigram特征集合。最后需要注意的是U01和U02这些标志位,与特征token组合到一起主要是区分“U01:問”和“U02:問”这类特征,虽然抽取的日文”字”特征是一样的,但是在CRF++中这是有区别的特征。

    最后我们再来看一下执行脚本:

    #!/bin/sh
    ../../crf_learn -f 3 -c 4.0 template train.data model
    ../../crf_test -m model test.data
    
    ../../crf_learn -a MIRA -f 3 template train.data model
    ../../crf_test -m model test.data
    rm -f model

            执行脚本告诉了我们如何训练一个CRF模型,以及如何利用这个模型来进行测试,执行这个脚本之后,对于输入的测试集,输出结果多了一列:

    よ h   I   B
    っ h   I   I
    て h   I   B
    私 k   B   B
    た h   B   B
    ち h   I   I
    の h   B   B                                      
    世 k   B   B
    代 k   I   I
    が h   B   B

            而这一列才是模型预测的改字的标记tag,也正是我们所需要的结果。到此为止,关于日文分词样例的介绍已经完毕,读者应该可以猜测到接下来我们会如何做中文分词吧?

    第三部分 中文分词实践

            1.将backoff2005里的训练数据转化为CRF++所需的训练数据格式,语料选取的是以北京大学《人民日报》的语料,采用4-tag( B(Begin,词首), E(End,词尾), M(Middle,词中), S(Single,单字词))标记集,处理utf-8编码文本。

            原始训练集./icwb2-data/training/pku_training.utf8的形式是人工分好词的中文句子形式。根据如下的脚本 make_train.py,将这个训练语料转换为CRF++训练用的语料格式(2列,4-tag):

    #!/usr/bin/env python
    #-*-coding:utf-8-*-
    
    #4-tags for character tagging: B(Begin),E(End),M(Middle),S(Single)
    
    import codecs
    import sys
    
    def character_tagging(input_file, output_file):
    	input_data = codecs.open(input_file, 'r', 'utf-8')
    	output_data = codecs.open(output_file, 'w', 'utf-8')
    	for line in input_data.readlines():
    		word_list = line.strip().split()
    		for word in word_list:
    			if len(word) == 1:
    				output_data.write(word + "	S
    ")
    			else:
    				output_data.write(word[0] + "	B
    ")
    				for w in word[1:len(word)-1]:
    					output_data.write(w + "	M
    ")
    				output_data.write(word[len(word)-1] + "	E
    ")
    		output_data.write("
    ")
    	input_data.close()
    	output_data.close()
    
    if __name__ == '__main__':
    	if len(sys.argv) != 3:
    		print ("Usage: python " + argv[0] + " input output")
    		sys.exit(-1)
    	input_file = sys.argv[1]
    	output_file = sys.argv[2]
    	character_tagging(input_file, output_file)

            结果(假定文件名为pku_training_tag.utf8,后文会用到)样例如下:

    “ S
    人 B
    们 E
    常 S
    说 S
    生 B
    活 E
    是 S                                      
    一 S
    部 S
    ...

            

            2.有了这份训练语料,就可以利用crf的训练工具crf_learn来训练模型了,执行如下命令即可:crf_learn -f 3 -c 4.0 template pku_training_tag.utf8 crf_model

            训练的时间稍微有些长,在我的2G内存的机器上跑了将近567轮,大约21个小时,最终训练的crf_model约37M。有了模型,现在我们需要做得还是准备一份CRF++用的测试语料,然后利用CRF++的测试工具crf_test进行字标注。原始的测试语料是icwb2-data/testing/pku_test.utf8 ,下面的脚本会把测试语料转化为模型需要的格式(详见第二部分的介绍),并且把切分好的文件展现出来。脚本如下:

    #!/usr/bin/env python
    #-*-coding:utf-8-*-
    
    #CRF Segmenter based character tagging:
    # 4-tags for character tagging: B(Begin), E(End), M(Middle), S(Single)
    
    import codecs
    import sys
    
    import CRFPP
    
    def crf_segmenter(input_file, output_file, tagger):
    	input_data = codecs.open(input_file, 'r', 'utf-8')
    	output_data = codecs.open(output_file, 'w', 'utf-8')
    	for line in input_data.readlines():
    		tagger.clear()
    		for word in line.strip():
    			word = word.strip()
    			if word:
    				tagger.add((word + "	o	B").encode('utf-8'))
    		tagger.parse()
    		size = tagger.size()
    		xsize = tagger.xsize()
    		for i in range(0, size):
    			for j in range(0, xsize):
    				char = tagger.x(i, j).decode('utf-8')
    				tag = tagger.y2(i)
    				if tag == 'B':
    					output_data.write(' ' + char)
    				elif tag == 'M':
    					output_data.write(char)
    				elif tag == 'E':
    					output_data.write(char + ' ')
    				else:
    					output_data.write(' ' + char + ' ')
    		output_data.write('
    ')
    	input_data.close()
    	output_data.close()
    
    if __name__ == '__main__':
    	if len(sys.argv) != 4:
    		print "Usage: python " + sys.argv[0] + " model input output"
    		sys.exit(-1)
    	crf_model = sys.argv[1]
    	input_file = sys.argv[2]
    	output_file = sys.argv[3]
    	tagger = CRFPP.Tagger("-m " + crf_model)
    	crf_segmenter(input_file, output_file, tagger)

    第四部分 分词结果

            有了CRF字标注分词结果,我们就可以利用backoff2005的测试脚本来测一下这次分词的效果了:

    ./icwb2-data/scripts/score ./icwb2-data/gold/pku_training_words.utf8 ./icwb2-data/gold/pku_test_gold.utf8 pku_result.utf8 > pku_result.score

            注:

    pku_result.utf8为最终得到的分词结果文件

            结果如下:

            从上图可以看出,CRF分词的准确率为93.7%,召回率为92.3%。

    参考文章: http://www.52nlp.cn/中文分词入门之字标注法4

  • 相关阅读:
    python,生产环境安装
    neo4j 图数据库
    RNN系列
    机器学习关于AUC的理解整理
    fensorflow 安装报错 DEPENDENCY ERROR
    dubbo Failed to check the status of the service com.user.service.UserService. No provider available for the service
    使用hbase遇到的问题
    MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk
    gradle 安装
    jenkins 安装遇到的坑
  • 原文地址:https://www.cnblogs.com/DjangoBlog/p/6814398.html
Copyright © 2011-2022 走看看