放牧代码和思想
专注自然语言处理、机器学习算法
    恕不接待索要源码语料者、索求技术方案者、以及不Google的懒人。

CRF++模型格式说明

目录

通过追加-t, –textmodel参数可以输出文本格式的CRF模型文件,通过该模型文本,可以加深对条件随机场的理解或为其他应用所利用。本文旨在介绍CRF++的文本模型格式,具体读取与解码将集成到HanLP中一并开源。

训练

语料

以BMES标注语料为例:

那	S
音	B
韵	E
如	S
轻	B
柔	E
的	S
夜	B
风	E
,	S

惊	S
溅	S
起	S
不	B
可	M
言	M
传	E
的	S
天	B
籁	E
。	S

注意字与标签之间的分隔符为制表符\t,否则会导致feature_index.cpp(86) [max_size == size] inconsistent column size错误。

特征模板

# 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

T**:%x[#,#]中的T表示模板类型,两个"#"分别表示相对的行偏移与列偏移。

一共有两种模板:

第一种是Unigram template:第一个字符是U,这是用于描述unigram feature的模板。每一行%x[#,#]生成一个CRFs中的点(state)函数: f(s, o), 其中s为t时刻的的标签(output),o为t时刻的上下文.如CRF++说明文件中的示例函数:

func1 = if (output = B and feature="U02:那") return 1 else return 0

它是由U02:%x[0,0]在输入文件的第一行生成的点函数.将输入文件的第一行"代入"到函数中,函数返回1,同时,如果输入文件的某一行在第1列也是“那”,并且它的output(第2列)同样也为B,那么这个函数在这一行也返回1。

第二种是Bigram template:第一个字符是B,每一行%x[#,#]生成一个CRFs中的边(Edge)函数:f(s', s, o), 其中s'为t – 1时刻的标签.也就是说,Bigram类型与Unigram大致机同,只是还要考虑到t – 1时刻的标签.如果只写一个B的话,默认生成f(s', s),这意味着前一个output token和current token将组合成bigram features。

命令行

使用下列命令可以得到一个model文件和一个model.txt文件,后者是本文的主要研究对象。

..\..\crf_learn  -f 3 -c 4.0 template pku_training.bmes.txt model -t

参数解释如下:

可选参数 

-f, –freq=INT使用属性的出现次数不少于INT(默认为1)

-m, –maxiter=INT设置INT为LBFGS的最大迭代次数 (默认10k)

-c, –cost=FLOAT      设置FLOAT为代价参数,过大会过度拟合 (默认1.0)

-e, –eta=FLOAT设置终止标准FLOAT(默认0.0001)

-C, –convert将文本模式转为二进制模式

-t, –textmodel为调试建立文本模型文件

-a, –algorithm=(CRF|MIRA)

选择训练算法,默认为CRF-L2

-p, –thread=INT线程数(默认1),利用多个CPU减少训练时间

-H, –shrinking-size=INT

设置INT为最适宜的跌代变量次数 (默认20)

-v, –version显示版本号并退出

-h, –help显示帮助并退出

输出

训练过程中会输出一些信息,其意义如下:

iter:迭代次数。当迭代次数达到maxiter时,迭代终止

terr:标记错误率

serr:句子错误率

obj:当前对象的值。当这个值收敛到一个确定值的时候,训练完成

diff:与上一个对象值之间的相对差。当此值低于eta时,训练完成

可见,如果希望训练快速结束,可以在命令行中适当减小maxiter值,增大eta值。

CRF模型格式

骨架

打开model.txt,其基本内容骨架如下:

version: 100
cost-factor: 1
maxid: 2159868
xsize: 1

B
E
M
S

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]
B

0 B
16 U00:-
20 U00:0
24 U00:1
28 U00:2
32 U00:3
36 U00:4
40 U00:5
44 U00:6
48 U00:7
52 U00:8
56 U00:9
60 U00:_B-1
64 U00:_B-2
……
17404 U01:厨
17408 U01:去
17412 U01:县
17416 U01:参
17420 U01:又
17424 U01:叉
17428 U01:及
17432 U01:友
17436 U01:双
17440 U01:反
17444 U01:发
17448 U01:叔
17452 U01:取
17456 U01:受
……
77800 U05:_B-1/一/个
107540 U05:一/方/面
107544 U05:一/无/所
107548 U05:一/日/三
107552 U05:一/日/为
107556 U05:一/日/之
……
566536 U06:万/吨/_B+1
……
2159864 U09:v/e

-8.5354017525999719
9.0491453814148901
7.0388286231971700
-7.2545558164093009
5.2799470769112835
-8.5333633546653758
-5.3549190735606933
5.2575182675282477
-5.4259109736696054

接下来我会把上述骨架分解说明——

文件头

version: 100
cost-factor: 1
maxid: 2159868
xsize: 1

说明了模型的版本,通过-c参数指定的cost-factor,特征函数的最大id,xsize是特征维数,也就是训练语料列数-1。

值得注意的是maxid比我们从文本中观察到的最大id大了4,这是为什么呢?且听下文分解。

标签

B
E
M
S

也就是最终的输出。

模板

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]
B

训练时用到的模板。

特征函数

0 B
16 U00:-
20 U00:0
24 U00:1
28 U00:2
32 U00:3
36 U00:4
40 U00:5
44 U00:6
48 U00:7
52 U00:8
56 U00:9
60 U00:_B-1
64 U00:_B-2
……
17404 U01:厨
17408 U01:去
17412 U01:县
17416 U01:参
17420 U01:又
17424 U01:叉
17428 U01:及
17432 U01:友
17436 U01:双
17440 U01:反
17444 U01:发
17448 U01:叔
17452 U01:取
17456 U01:受
……
77800 U05:_B-1/一/个
107540 U05:一/方/面
107544 U05:一/无/所
107548 U05:一/日/三
107552 U05:一/日/为
107556 U05:一/日/之
……
566536 U06:万/吨/_B+1
……
2159864 U09:v/e

按照[id] [参数o]的格式排列,你可能会奇怪,f(s, o)应该接受两个参数才对。其实s隐藏起来了,注意到id不是连续的,而是隔了四个,这表示这四个标签(s=b|m|e|s)和公共的参数o组合成了四个特征函数。特别的,0-15为BEMS转移到BEMS的转移函数,也就是f(s', s, o=null)。

值得注意的是,_B-1表示句子第一个单词前面的一个单词,_B+1表示末尾后面的一个单词,你可以在最大熵的模型中找到类似的逻辑处理,依次类推。

特征函数权值

后面的小数依id顺序对应每个特征函数的权值。

9.0491453814148901
7.0388286231971700
-7.2545558164093009
5.2799470769112835
-8.5333633546653758
-5.3549190735606933
5.2575182675282477
-5.4259109736696054

关于解码

严格来讲,解码并不属于本文的范围,但是不说说解码的话,对特征函数权值的理解就仅仅限于“浮点数”这一表面。所以简要地说说解码,比如说我们有一个句子“商品和服务”,对于每个字都按照上述模板生成一系列U特征函数的参数代入,得到一些类似010101的函数返回值,乘上这些函数的权值求和,就得到了各个标签的分数,由大到小代表输出这些标签的可能性。

至于B特征函数(这里特指简单的f(s', s)),在Viterbi后向解码的时候,前一个标签确定了后就可以代入当前的B特征函数,计算出每个输出标签的分数,再次求和排序即可。

Reference

CRF++工具包使用介绍.ppt

http://www.zhihu.com/question/20279019

转载须注明:码农场 » CRF++模型格式说明

分享到:更多 ()
  1. Pingback: HanLP开源 | HundredLee's Blog

  2. 博主你好,刚刚看了你这篇文章,我有一点不明白,请问,我用crf++自己训练的模型,放到这里边怎么不能用呢,我生成了一个model以及一个model.txt,难道是把model.txt放过来吗,还有就是博主请问你这个模型用了几列特征呢?就一列BMES吗,这样准确率会不会不太高,训练语料我是用了6个月的,博主呢,问题有点多,如果有时间,请指正

  3. 我知道了,前16个概率对应转移概率,特征第一行是0 B,第二行就是16开头了。我在训练的时候在template中把# Bigram B去掉了,导致输出的模型文件没有转移概率。

  4. 博主博主–我又双叒叕来了: 关于CRF模型的,我是先用CRF++ 按照BEMS 的标签训练好了模型,然后也可以成功使用CRF++的接口来分词,现在想用您的CRF来分词,主要识别地点新词, 结果替换模型之后, 报如下异常:
    Exception in thread “main” java.lang.OutOfMemoryError: Requested array size exceeds VM limit
    at com.hankcs.hanlp.model.crf.CRFModel.load(CRFModel.java:334), 但是模型只有几M的大小。

    博主能不能指点一下,是模型的问题,还是hanlp的CRF 对模型有特殊要求。

  5. 博主,打扰一下! 我想向您确认一件事情,就是您在1.2.7中的CRF分词这一块,如果用户enableCustomDictionary(trued)的话,才会去执行 猜词性 。反之的话,如果不做修改都是 null 是吧? 

  6. 您好 为什么我这儿生成的model里,下面特征函数的排列的ID号是乱的呢,长成这样:

    B

    300 B
    6264 U00:10
    426 U00:2
    5235 U00:30
    1992 U00:5
    7140 U00:A205门口
    3519 U00:B座
    8400 U00:D栋405
    60 U00:_B-1

  7. func1 = if (output = B and feature="U02:那") return 1 else return 0
    它是由U02:%x[0,0]在输入文件的第一行生成的点函数.将输入文件的第一行"代入"到函数中,函数返回1,同时,如果输入文件的某一行在第1列也是“那”,并且它的output(第2列)同样也为B,那么这个函数在这一行也返回1。
    博主 上面给的语料库里“那”不是对应的S么 怎么这里是B? 不太理解

    • 应该是生成output数量的func 。本例中还生成了func2= if (output = M and feature="U02:那") return 1 else return 0; func3= if (output = E and feature="U02:那") return 1 else return 0; func4= if (output = S and feature="U02:那") return 1 else return 0; 个人理解,博主省略了

  8. 我今天也遇到了同样的问题,我看代码里写先从bin 读模型文件,然道还是要以txt的方式去读取。

我的开源项目

HanLP自然语言处理包基于DoubleArrayTrie的Aho Corasick自动机