由于最近想实现CRF,学完了理论后就开始怎么想怎么实现,想参照CRF++的开源实现,但首先要解决的怎么理解特征模板,所以写了此文,主要参考了2篇文章,在此感谢。
http://www.52nlp.cn/%E4%B8%AD%E6%96%87%E5%88%86%E8%AF%8D%E5%85%A5%E9%97%A8%E4%B9%8B%E5%AD%97%E6%A0%87%E6%B3%A8%E6%B3%954
http://www.hankcs.com/nlp/the-crf-model-format-description.html
对于训练数据,首先需要多列,但不能不一致,既在一个文件里有的行是两列,有的行是三列;其次第一列代表的是需要标注的“字或词”,最后一列是输出位"标记tag",如果有额外的特征,例如词性什么的,可以加到中间列里,所以训练集或者测试集的文件最少要有两列。
例如:下面所有例子都是以下列的分词数据举例
CRF++的特征模板通常长下面这个样子:
“%x[行位置,列位置]”代表了相对于当前指向的token的行偏移和列的绝对位置。
先来解释以下Unigram特征模板是什么意思。
举例:U00:%x[-2,0]
行-2代表是当前行的之前的第2个字符(观测),
0代表第2列,如果遍历到的行如下图:
图3.10.1
将产生以下的特征函数:(注:下列伪码的x代表观测,y代表状态,也就是贴的标签)
If(y==’B’&&x==’人’) return 1 else return 0;
If(y==’E’&&x==’人’) return 1 else return 0;
If(y==’M’&&x==’人’) return 1 else return 0;
If(y==’S’&&x==’人’) return 1 else return 0;
算一算将产生4*65536(4是状态个数,65536是不同字符的个数),如果状态数和观测数一样这就对应于HMM的发射矩阵。
这什么意思呢?学过HMM的知道,这类似于发射矩阵,统计状态下的观测分布。
有人可能要问?U01:%x[-1,0],U02:%x[0,0]这2个会重复统计吧?确实是啊,目前我还不清楚为什么这样,我猜想这里要表达的是,当前状态下的观测分布不仅仅受当前观测字符的影响,还受前第2个U02:%x[0,0],前第1个U01:%x[-1,0],后1个U03:%x[1,0]等的影响。
至于下面这种特征模板:
我想是统计当前状态下,窗口为3周围观测的分布,
以图3.10.1举例产生下面的特征函数:
if(x1==’民’&&x2==’网’&x3==’1’&y==’B’) return 1 else return 0;
if(x1==’民’&&x2==’网’&x3==’1’&y==’M’) return 1 else return 0;
if(x1==’民’&&x2==’网’&x3==’1’&y==’E’) return 1 else return 0;
if(x1==’民’&&x2==’网’&x3==’1’&y==’S’) return 1 else return 0;
这样想想确实CRF比HMM一个发射矩阵表达了更多的信息。
令人疑惑的是Bigram并没有直接定义模板函数,只写了一个B(如上图),我在HanLp作者的博客中找到这样的解释。如果只写一个B的话,默认生成f(s', s),其中s'为t – 1时刻的标签.也就是说,Bigram类型与Unigram大致机同,只是还要考虑到t – 1时刻的标签.这意味着前一个output token和current token将组合成bigram features。
这样的话唯一一个bigram特征模板的所有特征函数类似于HMM中的转移矩阵。
还是以图3.10.1举例,将产生下列的特征函数:下面y1是前一个状态,y2是后一个状态。
If(y1==’B’&&y2==’B’) return 1 else return 0; 当然这个特征函数应该是不合理的,当前字符是Begin的状态,下一个只能是Middle或者End。我们暂时不考虑是否算法实现考虑了这个特征函数,因为我也没具体实现。
If(y1==’B’&&y2==’B’) return 1 else return 0;
If(y1==’B’&&y2==’M’) return 1 else return 0;
If(y1==’B’&&y2==’E’) return 1 else return 0;
If(y1==’B’&&y2==’S’) return 1 else return 0;
If(y1==’M’&&y2==’B’) return 1 else return 0;
If(y1==’M’&&y2==’M’) return 1 else return 0;
If(y1==’M’&&y2==’E’) return 1 else return 0;
If(y1==’M’&&y2==’S’) return 1 else return 0;
If(y1==’E’&&y2==’B’) return 1 else return 0;
If(y1==’E’&&y2==’M’) return 1 else return 0;
If(y1==’E’&&y2==’E’) return 1 else return 0;
If(y1==’E’&&y2==’S’) return 1 else return 0;
If(y1==’S’&&y2==’B’) return 1 else return 0;
If(y1==’S’&&y2==’M’) return 1 else return 0;
If(y1==’S’&&y2==’E’) return 1 else return 0;
If(y1==’S’&&y2==’S’) return 1 else return 0;
暂时学到这儿,等我实现了这个算法将补充细节或者修正。