通过追加-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
http://www.zhihu.com/question/20279019
博主,您好。CRF中和HMM中也存在状态转移矩阵Mi(x)。我想知道,CRF++除了log likelihood,可以获得这个转移矩阵么?
好文
博主您好,我想问问CRF++最后产生model和model.txt文件,使用model时会自动调用model.txt里面的参数和特征函数吗,还是model.txt只是给我们直观了解model用的?也就是model.txt可以删掉吗?
你好!请问hanlp是不是不能加载.txt的模型?训练出来的.txt模型需转成.txt.bin才能用?
博主您好!请问我在用crf++0.58训练时数据时,当训练数据很大时,crf_learn.exe崩溃停止工作,情况跟下面链接网友遇到的一样,http://bbs.csdn.net/topics/391905441?page=1,请问博主如何解决,求教了,非常感谢!
博主好!为什么我用CRF++-0.58版本,训练输出的模型不是按id排序呢?而如果非id排序,则不能用于您的hanlp!看您源码是默认认为是排序了的!
看到有人问了,不好意思,忽略掉我的问题吧!
您的源码在哪呢?
源码在NLP的开源项目,CRFModel相关
我看了下确实是默认排序的,训练是 加上 -f参数, 结果就是按照id排序的
我今天也遇到了同样的问题,我看代码里写先从bin 读模型文件,然道还是要以txt的方式去读取。
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; 个人理解,博主省略了
您好 为什么我这儿生成的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
版本不同,自己按id排序就行了
博主,打扰一下! 我想向您确认一件事情,就是您在1.2.7中的CRF分词这一块,如果用户enableCustomDictionary(trued)的话,才会去执行 猜词性 。反之的话,如果不做修改都是 null 是吧?
enableCustomDictionary(trued)的话,选择词频最高的词性,com.hankcs.hanlp.seg.Segment#enablePartOfSpeechTagging后用HMM标注词性
好的,感谢!
博主博主–我又双叒叕来了: 关于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 对模型有特殊要求。
hanlp只接受文本形式的模型
谢谢! 重新用model.txt 加载的,已经ok, thx!
已经解决了 ,按照您上面的方式重新训练了一个model.txt , 可以正常使用!感谢
对,分词模型直接替换配置中的CRFSegmentModelPath,其他模型调用com.hankcs.hanlp.model.crf.CRFModel#load(java.lang.String)
您是怎么使用自己训练的模型呢?能不能指点一下?
博主您好!问一个入门的问题:就是您这里面crf模型文件model.txt , 是位与data/model/CRFSegment.txt.bin的吗?那我想用自己的CRF训练好的model,应该怎么使用crf++操作呢?
问一下博主,双精度trie树是受CRF++的启发吗?
[吃惊]
博主好坏,我以为你写的CRFModel是直接匹配CRF++的,原来你先做了一次转化。
的确,这是最精确的解释。
我说错了,应该就是直接等于特征维数。这里用CRF做分词,只使用了字符本身,所以xsize=1;最后一列是标签(SBME),所以总列数是2.
这么说也是对的
应该是训练语料列数-1,特征就多了
xsize=特征个数-1;
请问在HanLP的model中怎么没有CRFSegmentModel.txt呢?可不可以提供一份呢?我想通过通过CRFSegment的调用学习一下CRF的使用。
这是CRF++的原始模型文件,转了格式就删了。你可以自己训练一个。
博主你好,请问你在训练的时候-c参数指定的不是4.0吗,为什么模型文件里的cost-factor还是1
好问题,我要去源码里看看才能知道为什么。
xsize 是训练语料列数-1
感谢指点
可以告诉我怎么用条件随机场自己添加特征吗?
博主好,能给我几份CRF标注好的语料库吗,谢谢。82725411@qq.com
http://vdisk.weibo.com/s/aVfojIy2k8unB
多谢啊。