CRF应用以及CRF++

主要内容

  • 问题描述
  • 模型训练
    • 样本格式
    • 模板文件
    • 训练参数
  • 总结

之前我们简单介绍了CRF的背景知识、基本原理、应用场景。接下来我们主要介绍通过CRF来解决实际问题的工具CRF++。CRF++是工业应用比较广泛的条件随机场的开源工具。安装包下载地址:CRF++安装包,官方使用教程:CRF++教程。本文主要通过序列标注任务中的实体识别(Named Entity Recognition,NER,命名实体识别),进行介绍。

一、问题描述

原始query:播放周杰伦的告白气球

序列标注结果:播/O   放/O   周/B-artist   杰/I-artist   伦/E-artist   的/O   告/B-song   白/I-song   气/I-song   球/E-song

其中,artist表示歌手,song表示歌曲,是我们需要从句子中识别的实体。O表示没有实体意义的字,B表示实体的第一个字,E表示实体的最后一个字,I表示实体的中间部分。这里是用基于BIEO的方式进行标注,其中,两个字构成的实体只需要BE进行标注,单个字构成的实体只需要B进行标注。也可以用基于BIESO的方式进行标注,其中,单个字构成的实体通过S标注。

原始query,输入至模型中,输出的序列标注结果,从而得到最终的实体 {"artist":"周杰伦", "song":"告白气球"}。

二、模型训练

1、样本格式

采用CRF进行序列标注,准备训练数据就是准备训练序列对(X,Y),准备测试数据就是准备测试序列对(X,Y),其中X=\left \{ x_{1},\cdots ,x_{D} \right \}表示有D个特征。训练样本的格式如下图。第一列特征,表示原始query的每个字,也可以是其他形式的token,比如词等等。第二列特征,这里的例子采用的是query的每个字(token)可能是实体的概率,假设这里的概率是根据实际样本分布得到的(具有一定的置信度),将概率hash成枚举型[1,2,3,4,5](例如概率大于0.8取值5,概率小于0.2取值1),这样做的好处是该特征具有更好的容错性以及泛化性;当然也这里可以选取其他的特征,具体根据业务需求设定。第三列表示query的序列标注结果。这里的例子只用了两个特征(第一二列),当然也可以用更多的特征,每个特征一列,还是那句话,具体根据业务需求设定。所有列之间通过制表符"\t"(Tab键)分隔,两条不同query之间间隔一个空行。测试样本的格式和训练样本严格一致。

来    1    O
一    1    O
首    1    O
王    5    B-artist
力    5    I-artist
宏    5    E-artist
的    1    O
歌    1    O
曲    1    O
心    5    B-song
跳    5    E-song

播    1    O
放    1    O
七    5    B-song
里    5    I-song
香    5    E-song

2、模板文件

有了训练样本和测试样本之后,接下来准备模板文件,如下图:

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

U10:%x[0,0]/%x[0,1]

U11:%x[-1,1]/%x[0,1]
U12:%x[0,1]/%x[1,1]

# Bigram
B

其中,"%x[row,col]",row表示相对于当前token的行的位置,col表示列的位置(表示特征)。例如上文中的query:来一首王力宏的歌曲心跳,假如"王"作为当前token,模板"U00:%x[-2,0]"中"0"表示采用第1列特征,"-2"表示当前token的前2行,那么模板"U00:%x[-2,0]"对应的是"一"。模板"U09:%x[0,0]/%x[1,0]/%x[2,0]"对应的是"王/力/宏"。模板"U10:%x[0,0]/%x[0,1]"对应的是"王/5"。模板"U11:%x[-1,1]/%x[0,1]"对应的是"1/5"。

模板文件中一般包含两种模板。

第一种是Unigram template:第一个字符是U,这是用于描述unigram feature的模板。每个"%x[row,col]"生成一个CRF中的点函数(State Function): f(s, o),其中st时刻的标签(预测标记),ot时刻的上下文。还是以"王"作为当前字,根据第一个模板"U00:%x[-2,0]"能得到特征函数如下: 

func1=if(output=B-artist and feature='U00:一' ) return 1 else return 0
func1=if(output=I-artist and feature='U00:一' ) return 1 else return 0
func1=if(output=E-artist and feature='U00:一' ) return 1 else return 0
func1=if(output=B-song and feature='U00:一' ) return 1 else return 0
func1=if(output=I-song and feature='U00:一' ) return 1 else return 0
func1=if(output=E-song and feature='U00:一' ) return 1 else return 0
func1=if(output=O and feature='U00:一' ) return 1 else return 0

其中"output=B-artist"指的是当前token的预测标记,也就是"王"的预测标记,每个模板会把所有可能的预测标记(BIEO)都计算一遍,然后通过训练确定每种标记的权重,合理的标记在训练样本中出现的次数多,对应的权重就高,不合理的标记在训练样本中出现的少,对应的权重就少。利用模板生成特征函数就是把所有可能的特征函数都列出来,由模型通过训练决定每个特征函数的重要程度。 Unigram template中,每个query根据模板生成的特征函数共有K\times M,其中K表示预测标记的类别个数(上例中有7个,"B-song、B-artist、O、..."),M表示特征模板的个数。

第二种是Bigram template:第一个字符是B,这是用于描述Bigram feature的模板。每个"%x[row,col]"生成一个CRF中的边函数(Edge Function):f(s', s, o),其中s't-1时刻的标签(预测标记),也就是说,Bigram类型与Unigram类型大致相同,只是还要考虑到t-1时刻的预测标记。Bigram template中,每个query根据模板生成的特征函数共有K\times K\times M,其中K表示预测标记的类别个数(上例中有7个,"B-song、B-artist、O、..."),M表示特征模板的个数。由于Bigram template生成的特征模板个数过于庞大,在训练和预测阶段会带来比较大的内存和计算开销,同时Unigram template可以模拟Bigram template的二元特征,例如"U05:%x[-1,0]/%x[0,0]",所以通常只用Unigram template。

说完两种特征模板,我们回过头来看一下CRF的条件概率函数,里边的特征函数包含两种:转移特征函数、状态特征函数。个人理解:Unigram template每个"%x[row,col]"代表状态特征函数,Bigram template每个"%x[row,col]"代表转移特征函数。但是一般情况下我们只用Unigram template,那么转移特征函数如何表示呢?就是我们通过Unigram template中类似"U05:%x[-1,0]/%x[0,0]"的这种组合来实现。

3、训练参数

如果我们已经准备完成训练样本以及模板文件,那么我们就可以开始训练了,模型结果保存至模型文件中,如下:

crf_learn = "./CRF++-0.58/crf_learn"    #训练函数
template_file = "./crf_template_file"   #模板文件
train_file = "./crf_train_data"         #训练样本
model_file = "./model/crf"              #模型文件

${crf_learn} ${template_file} ${train_file} ${model_file}

这里有四个主要的参数,来调整模型的训练:

-a, –algorithm=(CRF-L1|CRF-L2|MIAR) #训练算法,默认CRF-L2
-c, –cost=FLOAT                     #代价参数,默认1.0
-f, –freq=INT                       #特征至少出现的频次,默认1
-p, –thread=INT                     #CPU线程数,默认1

-a 训练算法选择上,通常基于L2正则化的准确率略高于基于L1正则化的准确率,但是基于L2正则化会产生大量非零参数,导致模型过大,所以根据实际业务需求选择CRF-L1还是CRF-L2。

-c 代价参数的选择上,越大则模型越拟合训练样本,用于调整模型对训练样本的拟合程度,或者说用于平衡过拟合与欠拟合。

-f  频次控制的选择上,用于控制训练样本中特征出现的频次至少满足N个,当使用CRF++进行大规模数据训练时,只出现一次的特征可能会有成百上千万个,通过这个参数能够有效控制特征规模。

-p CPU线程数的选择上,越多训练速度越快。

其他的可选参数:

-m, –maxiter=INT        #设置LBFGS的最大迭代次数,默认10k
-n,                     #输出n-best结果,INT
-e, –eta=FLOAT          #设置终止标准,默认0.0001
-C, –convert            #将文本模式转为二进制模式
-t, –textmodel          #为调试建立文本模型文件
-H, –shrinking-size=INT #设置迭代变量次数,默认20
-v, –version            #显示版本号并退出
-h, –help               #显示帮助并退出

以下举个例子:

crf_learn = "./CRF++-0.58/crf_learn"    #训练函数
template_file = "./crf_template_file"   #模板文件
train_file = "./crf_train_data"         #训练样本
model_file = "./model/crf"              #模型文件
train_log = "./train.log"               #训练日志

${crf_learn} -a CRF-L1 -f 2 -p 8 -m 8000 ${template_file} ${train_file} -t ${model_file} > ${train_log}

最终模型结果如下所示(数据是楼主随意造的,明白意思即可):每一行表示对应模板的结果,每一列表示当前token可能属于各个类别的概率。预测过程,就是加载这个文件,然后类似查表操作得到对应的概率,最后用维特比算法求得概率最大的路径。

[u_features]:200000
U_feature/label     B-artist    I-artist    E-artist    B-song    I-song    E-song    O    
0 U00:_B-2          -0.12345    0.000000    0.000000    -0.123    0.000000  0.0000    0.9
5 U01:_B-1          -0.12345    0.000000    0.000000    -0.123    0.000000  0.0000    0.9
10 U02:我           -0.12345    0.000000    0.000000    -0.123    0.000000  0.0000    0.9
15 U03:想           -0.12345    0.000000    0.000000    -0.123    0.000000  0.0000    0.9
20 U04:听           -0.12345    0.000000    0.000000    -0.123    0.000000  0.0000    0.9
25 U05:_B-1/我      -0.12345    0.000000    0.000000    -0.123    0.000000  0.0000    0.9
30 U06:我/想        -0.12345    0.000000    0.000000    -0.123    0.000000  0.0000    0.9
35 U07:_B-2/_B-1/我 -0.12345    0.000000    0.000000    -0.123    0.000000  0.0000    0.9
40 U08:_B-1/我/想   -0.12345    0.000000    0.000000    -0.123    0.000000  0.0000    0.9
45 U09:我/想/听     -0.12345    0.000000    0.000000    -0.123    0.000000  0.0000    0.9

三、总结

1、token的粒度:可以是字粒度,也可以是词粒度,也可以其它形式。英文建议至少词粒度,而不是character粒度。例如,如果解决pattern明确的NER问题,可以用字粒度、词粒度,可以用于解决槽位的欠召、也可以用于解决槽位的过招(需要一定的技巧)。

2、特征的选择:需要根据具体的业务进行特征的选择。如果有个多个特征,可以增加"U10:%x[0,0]/%x[0,1]"这种类型的模板,能够把特征之间的关联对最终的分类结果的影响找出来。也可以尝试不同特征的上下文关系。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值