【实战】运用CRF++进行实体识别实战

本文运用CRF++0.58进行文本实体识别,用人民日报已经标注的语料进行处理,并将数据分成测试样本和训练样本,再使用训练样本数据用CRF++0.58及相应的自定义模板进行训练模型,之后用训练后的模型来用测试样本进行评估模型性能,最后用模型对句子进行实体识别进行测试。因此本文分为如下几个部分:

第一部分:Python依赖库CRFPP安装

第二部分:人名日报预料库处理

第三部分:CRF++工具模板的使用详解

第四部分:CRF++工具训练模型使用

第五部分:测试模型效果及实战应用

第一部分:Python依赖库CRFPP安装(Win10)

本部分,请参考《【基础库】CRF++0.58在Ubuntu16.04和win10环境搭建

第二部分:人名日报预料库处理

人民日报语料文件:people-daily.txt

人民日报语料格式:

使用Python脚本,生成两个文件(train.txt和test.txt),其中文件格式如下(截图参考):

脚本代码:G:\chuangzhi_source_third\nlp-algorithms-git\nlp-algorithms\nlp-base\crf-algorithm\corpusHandler.py

第三部分:CRF++工具模板的使用详解

CRF++模板构建分为两类,一类是Unigram标注,一类是Bigram标注。Unigram和Bigram模板分别生成CRF的状态特征函数S(yi, x, i)和转移特征函数tk(yi-1, yi, x, i)。其中yi是标签,x是观测序列,i是当前节点位置。Bigram 下面只需要加一个B就ok了,其它还是用Unigram模板生成特征。

主要介绍Unigram模板

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]

特征模板格式:%x[row,col]。x可取U或B,对应两种类型。方括号里的编号用于标定特征来源,row表示相对当前位置的行,0即是当前行;col对应训练文件中的列。这里只使用第1列(编号0),即文字。Unigram template:第一个字符是U,这是用于描述unigram feature的模板。每一行%x[#,#]生成一个CRFs中的点(state)函数: f(s, o), 其中s为t时刻的标签(output),o为t时刻的上下文。这是CRF++例子中给出的模板,一共有9个模板,先看第一个模板,表示当前词和其前面的第二个词组成的特征,U02表示当前词。

CRF++遍历每一个位置时,会查看此位置前一个位置(-1,0)和前两个位置(-2,0)和后一个位置(1,0)和后两个位置(2,0),这是U00,U01,U02,U03,U04所规定的。至于后面几个特征函数,比如U05,它把前面的几个位置合起来看。以“小明今天穿了一件红色上衣”为例,符合CRF++处理格式的这句话应该变成如下形式(标签体系的确定?):

小 B
明 I
今 B
天 I
穿 S
了 S
一 B
件 I
红 B
色 I
上 B
衣 I

假设我们有三个标记tag,B(表示一个词的开头那个字),I(表示一个词的结尾那个字),S(表示单个字的词)。先看第一个模板U00:%x[-2,0],第一个模板产生的特征如下:如果当前词是‘今’,那-2位置对应的字就是‘小’,
每个特征对应的字如下:

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]=====>今/天

根据第一个模板U00:%x[-2,0]能得到的转移特征函数如下:

func1=if(output=B and feature=’U00:小‘ ) return 1 else return 0

func2=if(output=I and feature=’U00:小’) return 1 else return 0
func3=if(output=S and feature=’U00:小) return 1 else return 0

其中output=B 指的是当前词(字)的预测标记,也就是’今‘的预测标记,每个模板会把所有可能的标记输出都列一遍,然后通过训练确定每种标记的权重,合理的标记在训练样本中出现的次数多,对应的权重就高,不合理的标记在训练样本中出现的少,对应的权重就少。

得到三个特征函数之后当前这个字’今‘的特征函数利用第一个模板就全了。然后扫描下一个字‘天‘,以’天‘字作为当前字预测这个字的标记tag,同样会得到三个特征函数:

func4=if(output=B and feature=’U00:明’) return 1 else return 0
func5=if(output=I and feature=’U00:明’) return 1 else return 0
func6=if(output=S and feature=’U00:明’) return 1 else return 0

后面U01~U09都会按此方式继续扫描生成特征函数。

func = if(output = B,and feature = “U05:小/明/今”) return 1 else return 0

即当前位置输出标签为B,并且当前位置为今,前一个位置是明,前两个位置是小,则输出1。不管以何种方式扫描或者生成模板,最终会生成N*T*M个特征函数,N代表分词中词的个数,T代表分词标注的tag标签(B,I,S等),M代表模板个数。

Bigram类型

与Unigram不同的是,Bigram类型模板生成的函数会多一个参数:上个节点的标签 。

生成函数类似于:

func1 = if (prev_output = B and output = B and feature=B01:"北") return 1 else return 0

这样,每行模板则会生成 L*L*N 个特征函数。经过训练后,这些函数的权值反映了上一个节点的标签对当前节点的影响。

参考网址:https://www.cnblogs.com/gczr/p/10026567.html

第四部分:CRF++工具训练模型使用

CRF官网网址:https://taku910.github.io/crfpp/

下载地址(CRF++-0.58.zip):

https://drive.google.com/drive/folders/0B4y35FiV1wh7fngteFhHQUN2Y1B5eUJBNHZUemJYQV9VWlBUb3JlX0xBdWVZTWtSbVBneU0#list

解压缩放到如下位置(E:\CRF++-0.58):

注解:其中crf_learn.exe可执行文件,是用来训练模型的,其中crf_test.exe是用来测试模型的。

 

  • 使用crf_learn进行训练
    - crf_learn文件地址(进入CRF训练可执行路径位置):E:\CRF++-0.58

- crf_learn -f 4 -p 8 -c 3 G:\chuangzhi_source_third\nlp-algorithms-git\nlp-algorithms\nlp-base\data\template.txt G:\chuangzhi_source_third\nlp-algorithms-git\nlp-algorithms\nlp-base\data\train.txt G:\chuangzhi_source_third\nlp-algorithms-git\nlp-algorithms\nlp-base\data\model

注解1:上面脚本执行后生成的model模型

注解2上面参数理解:有四个基本的參数能够调整:

-a CRF-L2 or CRF-L1规范化算法选择。默认是CRF-L2。一般来说L2算法效果要比L1算法略微好一点,尽管L1算法中非零特征的数值要比L2中大幅度的小。

-c float 这个参数设置CRF的hyper-parameter。c的数值越大,CRF拟合训练数据的程度越高。这个参数能够调整过度拟合和不拟合之间的平衡度。这个参数能够通过交叉验证等方法寻找较优的参数。

-f NUM这个参数设置特征的cut-off threshold。CRF++使用训练数据中至少NUM次出现的特征。默认值为1。当使用CRF++到大规模数据时,仅仅出现一次的特征可能会有几百万,这个选项就会在这种情况下起到作用。

-p NUM 假设电脑有多个CPU,那么能够通过多线程提升训练速度。NUM是线程数量。

参考网址:https://www.cnblogs.com/lcchuguo/p/4015777.html

  • 使用crf_test进行测试

- crf_test文件地址(进入CRF训练可执行路径位置):E:\CRF++-0.58

- crf_test -m G:\chuangzhi_source_third\nlp-algorithms-git\nlp-algorithms\nlp-base\data\model G:\chuangzhi_source_third\nlp-algorithms-git\nlp-algorithms\nlp-base\data\test.txt > G:\chuangzhi_source_third\nlp-algorithms-git\nlp-algorithms\nlp-base\data\test.rst

第五部分:测试模型效果及实战应用

使用上面测试数据集和模型,使用crf_test.exe工具生成预测后数据test.rst,再使用Python脚本,测试实现效果,脚本位置:G:\chuangzhi_source_third\nlp-algorithms-git\nlp-algorithms\nlp-base\crf-algorithm\crf-plus-plus-example.py

运行的结果:

loc_P:0.8973555698885101, loc_R:0.8359656652360515, loc_F1:0.8655734790916767

使用上面的模型和随便一句话,与测试句子中的实体信息。

脚本位置:G:\chuangzhi_source_third\nlp-algorithms-git\nlp-algorithms\nlp-base\crf-algorithm\crf-plus-plus-test.py

脚本为:

# -*- coding: utf-8 -*-
def load_model(path):
import os, CRFPP
if os.path.exists(path):
return CRFPP.Tagger('-m {0} -v 3 -n2'.format(path))
return None

def locationNER(text):
tagger = load_model('../data/model')
for c in text:
tagger.add(c)
result = []
tagger.parse()
# print(tagger.xsize())
word = ''
for
i in range(0, tagger.size()):
for j in range(0, tagger.xsize()):
ch = tagger.x(i, j)
# print(ch)
tag = tagger.y2(i)
# print(tag)
if tag == 'B':
word = ch
elif tag == 'M':
word += ch
elif tag == 'E':
word += ch
result.append(word)
elif tag == 'S':
word = ch
result.append(word)
return result
text = '我中午要去北京饭店,下午去中山公园,晚上回亚运村。'
print(text, locationNER(text), sep='==> ')
text = '我去回龙观,不去南锣鼓巷。'
print(text, locationNER(text), sep='==> ')
text = '基于规则和条件随机场的中文命名实体识别方法研究 程志刚'
print(text, locationNER(text), sep='==> ')

预测的结果:

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: