天天看点

CRF++实现中文分词简介过程问题与解决

简介

本文主要是我学习使用CRF++进行中文分词的一个记录,因此并没有CRF的背景知识与CRF++安装的相关内容。本文主要是按照中文分词入门之字标注法4中的相关内容进行。

过程

本文中所使用backoff2005数据集中的原始数据格式与CRF++所要求的格式不符,因此首先需要对数据集格式进行修改。

msr_training数据格式转换

在原始的msr_training.utf8的文件中的数据格式为如下所示

“ 人们 常 说 生活 是 一 部 教科书 , 而 血 与 火 的 战争 更 是 不可多得 的 教科书 , 她 确实 是 名副其实 的 ‘ 我 的 大学 ’ 。

“ 心 静 渐 知 春 似 海 , 花 深 每 觉 影 生 香 。

而在CRF++中则要求为每个字一行,按列的形式写,句子之间空一行,后面的列则为标记。在本文中,B为单词开头,M为单词中间,E为单词结尾,S为一个字为一个词。形式如下

“ S

人 B

们 E

常 S

说 S

生 B

通过下述程序即可完成此步,从而得到符合CRF++格式要求的msr_training.tagging4crf.utf8

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 + "\tS\n")   
            else:
                output_data.write(word[0] + "\tB\n")
                for w in word[1:len(word)-1]:
                    output_data.write(w + "\tM\n")
                output_data.write(word[len(word)-1] + "\tE\n")
        output_data.write("\n")
    input_data.close()
    output_data.close()

if __name__ == '__main__':
    if len(sys.argv) != 3:
        print("pls use: python make_crf_train_data.py input output")
        sys.exit()
    input_file = sys.argv[1]
    output_file = sys.argv[2]
    character_tagging(input_file, output_file)
# python make_crf_train.py ../icwb2-data/training/msr_training.utf8 msr_training.tagging4crf.utf8
           

得到model

有了符合要求的训练数据,还需要template去生成特征函数,从而得到model。本文中所使用的template为CRF++本身自带的example目录下seg例子中的template

# 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

然后通过一下命令即可得到相应model

crf_learn -f 3 -c 4.0 template msr_training.tagging4crf.utf8 crf_model

msr_test数据格式转换

在使用上述得到的model之前,还需要对msr_test同样进行数据格数的变化,与msr_train不同,此处msr_test中每个字的标记只需为B

扬 B

帆 B

远 B

东 B

做 B

转换的代码如下,转换后得到msr_test4crf.utf8

import codecs  
import sys  
  
def character_split(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():
        for word in line.strip():
            word = word.strip()
            if word:
                output_data.write(word + "\tB\n")
        output_data.write("\n")
    input_data.close()
    output_data.close()

if __name__ == '__main__':
    if len(sys.argv) != 3:
        print("pls use: python make_crf_test_data.py input output")
        sys.exit()
    input_file = sys.argv[1]
    output_file = sys.argv[2]
    character_split(input_file, output_file)

# python make_crf_test.py ../icwb2-data/testing/msr_test.utf8 msr_test4crf.utf8
           

测试

然后执行下述命令即可对test数据完成标注,得到标注结果msr_test4crf.tag.utf8

crf_test -m crf_model msr_test4crf.utf8 > msr_test4crf.tag.utf8

并且可以通过以下代码使用标注后的结果对test数据进行分词。

import codecs
import sys

def character_2_word(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():
        if line == "\n":
            output_data.write("\n")
        else:
            char_tag_pair = line.strip().split('\t')
            char = char_tag_pair[0]
            tag = char_tag_pair[2]
            if tag == 'B':
                output_data.write(' ' + char)
            elif tag == 'M':
                output_data.write(char)
            elif tag == 'E':
                output_data.write(char + ' ')
            else: # tag == 'S'
                output_data.write(' ' + char + ' ')
    input_data.close()
    output_data.close()

if __name__ == '__main__':
    if len(sys.argv) != 3:
        print("pls use: python crf_data_2_word.py input output")
        sys.exit()
    input_file = sys.argv[1]
    output_file = sys.argv[2]
    character_2_word(input_file, output_file)

# python crf_data_2_word.py msr_test4crf.tag.utf8 msr_test4crf.tag2word.utf8
           

结果如下所示

扬帆 远东 做 与 中国 合作 的 先行

希腊 的 经济 结构 较 特殊 。

最后可以使用backoff2005自带的测试工具对结果进行评价

./icwb2-data/scripts/score ./icwb2-data/gold/msr_training_words.utf8 ./icwb2-data/gold/msr_test_gold.utf8 msr_test4crf.tag2word.utf8 > msr_crf_segment.score

最后得到结果综合如下

=== SUMMARY:

=== TOTAL INSERTIONS: 1410

=== TOTAL DELETIONS: 1276

=== TOTAL SUBSTITUTIONS: 2432

=== TOTAL NCHANGE: 5118

=== TOTAL TRUE WORD COUNT: 106873

=== TOTAL TEST WORD COUNT: 107007

=== TOTAL TRUE WORDS RECALL: 0.965

=== TOTAL TEST WORDS PRECISION: 0.964

=== F MEASURE: 0.965

=== OOV Rate: 0.026

=== OOV Recall Rate: 0.650

=== IV Recall Rate: 0.974

### msr_test4crf.tag2word.utf8 1410 1276 2432 5118 106873 107007 0.965 0.964 0.965 0.026 0.650 0.974

接下来则是一次执行即可将测试数据转换为分词结果的代码,此代码要求在此之前已完成model

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 + "\to\tB"))
        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)
                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: # tag == 'S'
                    output_data.write(' ' + char + ' ')
        output_data.write('\n')
    input_data.close()
    output_data.close()

if __name__ == '__main__':
    if len(sys.argv) != 4:
        print("pls use: python crf_segmenter.py model input output")
        sys.exit()
    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)

# python crf_segmenter.py crf_model ../icwb2-data/testing/msr_test.utf8 msr_test.seg.utf8
           

问题与解决

在这整个过程中都还算进行的比较顺利,可能在使用测试工具的命令时会出现没有权限的错误,此时只需对socre文件使用chmod改变权限