【Natural Language Processing】基于CRF++的中文分词
2017-07-11 20:37
337 查看
一、任务简介
中文分词是中文信息处理领域中的最重要的任务,它对于智能信息处理技术具有重要的意义,当前的各种汉语分词技术都可以取得不错的结果。
本任务做的是繁体中文分词,将训练语料的30%作为验证集,70%作为训练集,按通常的 P/R/F 三个指标进行评测,最后用全部训练数据进行训练,用给出的测试数据进行测试,并将结果提交。本次任务使用条件随机场模型(CRF)进行实验。
2 实验环境
三、数据处理
因为本次任务所给原数据文件的编码是utf-16, CRF++能处理该类型的格式,所以要先把数据格式转成CRF++能处理的utf-8编码文件,否则出现异常不会生成模型,我们采用4-tag( B(Begin,词首), E(End,词尾), M(Middle,词中), S(Single,单字词))标记集,处理文本。本次任务把原训练数据划分成3:7,其中30%作为测试集,70%作为训练集,在划分数据集上采用了两种方法:
①选取前70%作为训练集,剩下作为验证集;
②随机选取70%作为训练集,余下为测试集。
对应代码:process.py
四、使用模板
五、训练和测试
经过前面的数据处理部分得到的验证阶段的训练集,就可以利用crf的训练工具crf_learn来训练模型,然后用得到的模型对验证集进行分词,最后进行评测。
同样地,使用该方法对任务给出的全部训练集训练数据,然后使用训练得到的模型对测试集进行分词。
对应代码:make_train.py; make_test.py;evaluate.py
①make_train.py
六、结果与分析
从验证集的结果来看,无论是从准确率或者是召回率上分词的效果还不错;同时可以发现按照不同的数据划分方式,结果表现出一定的差别,随机划分的方式结果要好于按照顺序划分数据集的方式,经过分析,应该是由于附近的句子存在相近或者相同的可能性更大,,那么测试集和验证集的分布更为相似,最后会导致结果偏高,但在实际中,训练集和测试集的分布可能相差比较大,所以我们在这个实验中,我们应该选择按顺序划分数据集的方法更为合理,同时结果也以按顺序划分为准。
由于之前没有做过中文分词方面的工作,所以本次大作业我自己尝试写了一个一阶隐马尔科夫模型进行分词,但是效果并不好,主要是比较简单,如果尝试二阶或者三阶隐马尔科夫模型效果可能会有一定的提升,后面我查询许多信息发现都是用CRF来做的,并且效果不错,并且有CRF++这个现成的工具可用,所以最后我选择了CRF。
中文分词是中文信息处理领域中的最重要的任务,它对于智能信息处理技术具有重要的意义,当前的各种汉语分词技术都可以取得不错的结果。
本任务做的是繁体中文分词,将训练语料的30%作为验证集,70%作为训练集,按通常的 P/R/F 三个指标进行评测,最后用全部训练数据进行训练,用给出的测试数据进行测试,并将结果提交。本次任务使用条件随机场模型(CRF)进行实验。
2 实验环境
名称 | 详细信息 |
操作系统 | Macos x 10.12.5 64位 |
CPU | 2.2GHz*4 |
内存 | 16G |
Python | 2.7 |
CRF++ | 0.58 |
三、数据处理
因为本次任务所给原数据文件的编码是utf-16, CRF++能处理该类型的格式,所以要先把数据格式转成CRF++能处理的utf-8编码文件,否则出现异常不会生成模型,我们采用4-tag( B(Begin,词首), E(End,词尾), M(Middle,词中), S(Single,单字词))标记集,处理文本。本次任务把原训练数据划分成3:7,其中30%作为测试集,70%作为训练集,在划分数据集上采用了两种方法:
①选取前70%作为训练集,剩下作为验证集;
②随机选取70%作为训练集,余下为测试集。
对应代码:process.py
# -*- coding: UTF-8 -*- import codecs import random import numpy as np import sys reload(sys) sys.setdefaultencoding('utf8') # 训练集7,3分,用于训练和测试 def divide(input_file, train, dev): input_data = codecs.open(input_file, 'r', 'utf-16') fw_train = codecs.open(train, 'w', 'utf-16') fw_dev = codecs.open(dev, 'w', 'utf-16') data = input_data.readlines() train_len = int(len(data) * 0.7 ) for i in range(train_len): fw_train.write(data[i]) for i in range(train_len,len(data)): fw_dev.write(data[i]) input_data.close() fw_train.close() fw_dev.close() # 随机将训练集7,3分,用于训练和测试 def train_dev_split(input_file, train, dev): input_data = codecs.open(input_file, 'r', 'utf-16') data = input_data.readlines() print len(data) fw_train = codecs.open(train, 'w', 'utf-16') fw_dev = codecs.open(dev, 'w', 'utf-16') shuffle_indices = np.random.permutation(np.arange(len(data))) train_lst = shuffle_indices[0:0.7*len(data)] test_lst = shuffle_indices[0.7*len(data):] for i in train_lst: if len(data[i].strip())==0: continue fw_train.write(data[i]) for j in test_lst: if len(data[j].strip()) == 0: continue fw_dev.write(data[j]) input_data.close() fw_train.close() fw_dev.close() def trans_dev(devseg, dev): fr = codecs.open(devseg, 'r', 'utf-16') fw_dev = codecs.open(dev, 'w', 'utf-16') for line in fr.readlines(): line = line.replace(' ', '') fw_dev.write(line) fr.close() fw_dev.close() if __name__ == '__main__': train_dev_split('../CRF_Seg/data/Train_utf16.seg', '../CRF_Seg/data/train.seg', '../CRF_Seg/data/dev.seg') trans_dev('../CRF_Seg/data/dev.seg', '../CRF_Seg/data/devs.seg')
四、使用模板
# 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的训练工具crf_learn来训练模型,然后用得到的模型对验证集进行分词,最后进行评测。
同样地,使用该方法对任务给出的全部训练集训练数据,然后使用训练得到的模型对测试集进行分词。
对应代码:make_train.py; make_test.py;evaluate.py
①make_train.py
#-*-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-16') 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__': # input_file = '../CRF_Seg/data/train.seg' # output_file = '../CRF_Seg/data/train.data' # character_tagging(input_file, output_file) input_file = '../CRF_Seg/data/Train_utf16.seg' output_file = '../CRF_Seg/data/All_train.data' character_tagging(input_file, output_file)②make_test.py
#-*-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-16') output_data = codecs.open(output_file, 'w', 'utf-16') for line in input_data.readlines(): tagger.clear() for word in line.strip(): word = word.strip() if word: tagger.add((word + "\to\tB").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('\n') input_data.close() output_data.close() if __name__ == '__main__': crf_model = '/Users/lhy/Documents/CS_work/python/CRF_Seg/data/crf_model' input_file = '/Users/lhy/Documents/CS_work/python/CRF_Seg/data/devs.seg' output_file = '/Users/lhy/Documents/CS_work/python/CRF_Seg/data/devCRF.seg' tagger = CRFPP.Tagger("-m " + crf_model) crf_segmenter(input_file, output_file, tagger) # crf_model = '/Users/lhy/Documents/CS_work/python/CRF_Seg/data/crf_model' # input_file = '/Users/lhy/Documents/CS_work/python/CRF_Seg/data/Test_utf16.seg' # output_file = '/Users/lhy/Documents/CS_work/python/CRF_Seg/data/test.seg' # tagger = CRFPP.Tagger("-m " + crf_model) # crf_segmenter(input_file, output_file, tagger)③evaluate.py
# -*- coding: utf-8 -*- import codecs import sys def loadResults(FileName) : ret = [] lines = codecs.open(FileName, encoding='utf-16-le') for iters, line in enumerate(lines) : line = line.strip() chr_t= line.split(' ') ret.append(chr_t) return ret def evaluate(predictedFile, goldenFile) : result_pre = loadResults(predictedFile) result_gld = loadResults(goldenFile) assert len(result_pre) == len(result_gld) # print([len(result_pre), len(result_gld)]) TPs = 0. TPFPs = 0. TPFNs = 0. for i in range(len(result_pre)) : pre_i, gld_i = result_pre[i], result_gld[i] TPs += len([word for word in gld_i if word in pre_i]) TPFPs += len(pre_i) TPFNs += len(gld_i) harmonic_mean = lambda x, y : 2 / (1 / x + 1 / y) precisions = 100.*TPs / TPFPs if not TPs == 0 else 0 recalls= 100.*TPs / TPFNs if not TPs == 0 else 0 f1_ss= harmonic_mean(precisions, recalls) if not precisions* recalls == 0 else 0 print('\tprecision: %.2f recall: %.2f f1_score: %.2f'%(precisions, recalls, f1_ss)) if __name__ == '__main__': pre='../CRF_Seg/data/dev.seg' gold='../CRF_Seg/data/devCRF.seg' evaluate(pre,gold)
六、结果与分析
| P(准确率) | R(召回率) | F值 |
按顺序7:3划分 | 96.58% | 96.53% | 96.56% |
随机划分数据集 | 97.18% | 97.27% | 97.22% |
由于之前没有做过中文分词方面的工作,所以本次大作业我自己尝试写了一个一阶隐马尔科夫模型进行分词,但是效果并不好,主要是比较简单,如果尝试二阶或者三阶隐马尔科夫模型效果可能会有一定的提升,后面我查询许多信息发现都是用CRF来做的,并且效果不错,并且有CRF++这个现成的工具可用,所以最后我选择了CRF。
相关文章推荐
- 基于CRF++0.54搭建中文分词系统
- 基于CRF的中文分词(ZT)
- 基于CRF的中文分词
- 基于Tire树和最大概率法的中文分词功能的Java实现
- 基于字标注的中文分词方法
- 基于HTTP协议的开源中文分词系统:HTTPCWS 1.0.0
- 《Natural Language Processing with Python》6.2节的一些错误
- 基于HTTP协议的开源中文分词系统:HTTPCWS 1.0.0 发布
- 基于MMSeg算法的中文分词类库
- 基于python的中文分词的实现及应用(转载)
- Introduction to Chinese natural language processing
- Tools for Natural Language Processing(转)
- .Net下的中文分词IKAnalyzerNet(基于Lucene.Net)
- 中文分词—基于Lucene的分词器—支持中英文混合词
- 搜索系统中基于字典的逆向中文分词
- 基于HTTP协议的开源中文分词系统:HTTPCWS 1.0.0 发布
- Lucene中文分词实现方法:基于StopWord分割分词
- 基于HTTP协议的开源中文分词系统:HTTPCWS 1.0.0 发布
- CRF中文分词开源版发布啦
- NLP:Natural Language Processing