1.介绍
CRF++是一个简单的,定制的,开源实现条件随机场的工具。条件随机场用来分词或标注序列数据。CRF++被设计成一个通用的工具,而且可以被应用到各种各样的自然语言处理任务中,比如命名实体识别,信息提取和文本分块。
2.特点
- 可以重新定义特征集
- 用c++编写
- 基于LBFGS的快速训练,LBFGS是拟牛顿算法,用于解决大规模的数值优化问题。
- 在训练和测试的时候使用更少的内存
- 编码/解码在实际的时间(翻译的感觉怪怪的)
- 能够产生n-best输出
- 能够产生single-best MIRA训练
- 为所有的可能输出边缘概率
- 作为一个开源的软件提供
3.使用
训练和测试文件格式
为了使CRF++使用正常,训练文件和测试文件都要有特定的格式。通常来说,训练和测试文件都要包括多个tokens,此外,一个token包括多个列。(列数是固定的)。tokens的定义依赖于你的任务,但是在大多数情况下,他们简单的与对应于你的words。每个tokens用一行表示,这一行的列被空格或tab键分开。一串token组成一个句子。为了标识句子与句子之间,我们通常放一个空行。
你可以按照你的需求的设置列数,但是,所有tokens的列数一定要是固定的。此外,在列之间有一些“语义”,比如,第一列是‘word’,第二列是‘POS tag’,第三列是‘sub-category of POS’等等。
最后一列代表着真正的正确的标注,这一列将会被CRF训练。
这里是一个这样文件的例子:(data for CoNLL shared task)
每一个token都有三列:
- 单词自己(比如:reckons)
- 与单词有关的词性(比如:VBZ)
- 分块(答案)标记(以IOB2格式表示)
下图所示的格式是不合理的,因为第二列和第三列的列数是2(他们没有词性列),列数是要被固定的。
4.准备特征模板
因为CRF++是被设计成一个通用的工具,所以你需要提前明确你的特征模板。这个文件描述了哪些特征在训练和测试时被使用。
- Template basic and macro
在模板文件中的每一行都是一个template。在每一个template中,将使用特殊的macro
%x[row,col] 在输入文件中来标识一个token。row表示从当前的token中相对的位置,col表示列的绝对的位置。
下面展示一个替换的例子
- 模板类型
注意有两种模板类型。这两种类型可以通过模板的第一个字母来区分。
- unigram template:第一个字母,‘U’
这个模板描述一元特征。当你给一个template“U01:%x[0,1]”,CRF++自动的形成一系列特征函数(func1,func2,...,funN),B比如:
由模板生成的特征函数的个数等于L*N,L是输出类的数目,而N是从给定模板扩展的唯一字符的数量。
2.Bigram template:第一个字母,‘B’
这个模板用来描述二元特征。通过这个模板,自动生成当前输出token和之前的输出token(bigram)的组合。注意,这种类型的模板一共生成L*L*N种不一样的特征函数。L也是输出类型的数目,N是通过模板形成的不同特征的数目。当输出的类型很多时(理解为标注集的类型很多时),这种模板将产生一大堆特征,这会导致在训练和测试时的效率都降低。
3.unigram feature和bigram feature的区别?
单词unigram/bigram有点让人困惑,因为unigram-features也能够写成word-level的bigram形式,比如:%U[-1,0]%U[0,0].这里的unigram 和 bigram的意思是 uni/bigrams 输出标签。
unigram:|output tag| X |all possible strings expanded with a macro|
bigram:|output tag| X
|output tag| X |all possible strings expanded with a macro|
- 区分相对位置的标识符
当tokens的相对位置需要被区分时,你也需要设置一个标识符。
在下面的例子中,macro“%x[-2,1]”和“%x[1,1]”都会被“DT”代替。但是他们显示的时不同的‘DT’
为了将两者区分开来,在模板中使用不一样的标识符(U01:或者U02):
在这个例子中,这两个模板被看作是不一样的,因为他们拓展成不一样的特征,“U01:DT”或“U02:DT”.你可以使用你想要的任意标识符,但是用数字来管理他们是有效的,因为这与他们的特征id是对应的
- 例子
这是CoNLL 2000 shared task 和 Base-NP chunking task 模板特征例子。二元模板只使用了一个B。这意味着只有它之前的output token和当前的token的组合被用作为二元特征。以#开始的行为当作注释
5.训练(encoding)
使用crf_learn命令:
这里的template_file和train_file要事先准备好。crf_learn产生训练好的model_file。
crf_learn输出下列的信息。
iter:迭代的次数
terr:与标签相关的错误率(# of error tags/#of all tag)
serr:与句子相关的错误率(# of error sentences/#of all sentences)
obj:当前对象的值,当这个值趋向于一个固定值是,crf++停止迭代
diff:与之前对象值得相对差
这里有四个主要得参数来控制训练条件
- -a CRF-L2 or CRF-L1
改变正则化算法。默认是L2。总的来说,虽然L1中得非零特征数量明显小于L2中得非零特征数量,但是L2表现得比L1好一些
- -c float:
在这个选项下,你可以改变crfs的hyper-parameter。C值越大,CRF可能会过拟合给定的训练语料。这个参数调整过拟合和你和不足之间的平衡。结果会被这个参数严重的影响。你可以通过使用held-out数据或者更一般的模型选择方法(如交叉验证)来找到最佳值。
- -f num
这个参数设置特征的cut-off阈值。CRF++使用在训练数据中出现超过num次的特征。默认值是1,当你使用大量数据时,出现一次的特征可能会有几百万个,这时这个选项就是有用的了。
- -p num
如果你的电脑有多个cpu,你可以使用多线程来是的训练速度更快。num是线程的数目
下面是例子:
6.测试(decoding)
使用测试的命令
这里的model_file是crf_learn产生的。在测试中,你不需要指明模板文件,因为这个模型文件具有相同的模板文件信息。test_file是你想赋予一系列标注的测试文件。这个文件需要和训练文件一样的格式。
下面是crf_test产生的输出:
最后一列是估计的标签,如果第三列是正确的标签,那么你可以评估它的准确性通过看第三列和第四列的不同。
- verbose level
-v设置详细级别。默认是0,通过提高这个水平,可以从crf++获取额外的信息
1.级别1
你可以获得每个标注的边际概率(每种输出标签的置信度度量)和输出的条件概率(整体输出的置信度度量)
第一行的#0.478113表示输出的条件概率,‘B/0.992465’表示标注的边际概率。
2.级别2
你也可以看到其它可能的边际概率
- N-best outputs
通过-n 选项,你可以获得通过按CRF的条件概率排序的N-best结果,通过n-best输出模式,CRF++给出了额外的一行,类似于“#N prob”,N表示从0开始的输出等级,以及prob表示输出的条件概率。
请注意,如果CRF ++无法再找到候选项,它有时会不会枚举N的最佳结果,当你给crf++一个简短的句子时就会出现这种情况
CRF ++使用前向维特比和后向A *搜索的组合,这个组合产生了n-best结果确切列表。
下面是例子: