上篇使用HMM进行中文分词,HMM存在两个比较严格的假设条件:观测独立性和当前状态只与上一状态有关,相比于HMM,CRF不受这两个假设的限制,并且可以自定义特征。为了保持分词方法的连贯性,CRF的推理会在下篇给出,本文主要是使用CRF++分词,CRF虽然理解起来困难,但是CRF++使用起来却是很容易,只要按照规定的格式提供训练数据模板和特征模板就好了,因此本文会将重点会放在两个模板的理解上面。
语料和工具
- 语料采用SIGHAN Bakeoff 2005 微软亚洲研究院中文分词语料
- CRF++0.58
- python
安装
从官网上下载对应系统版本的CRF++ 0.58 并解压安装,安装方法和使用说明官网都有(win系统的好像是开袋即食,解压就可以用了)。安装好之后可以在example/chunking下执行crf_learn -a MIRA template train.data model 体验下。
数据模板
训练和测试数据只需要按照给定的格式提供即可。
训练数据模板:
|
|
每一列是一种类型特征,最后一列为序列标签 ,列与列之间使用制表符\t分割,句子与句子之间使用\n分开(句子间多个换行符有问题?待求证)。上图只有原始输入的字本身这一列特征,序列标签为BEMS。需要注意的是,对于所有的行,列的数量必须都保持一致,不能有些行多一列有些行少一些。
特征模板
特征模板如下:
|
|
特征模板分为两部分,Unigram feature 和 Bigram feature。
Unigram feature: 用来生成状态特征函数
- 标识符id, Unigram 特征根据第一个字符’U’识别,用于描述 unigram 的特征模板。U后面的数字00、01代表特征id,只是用于标记,不一定需要连续,只要不重复应该就ok了,当然,如果不考虑上下文相关,需要做bag-of-words的训练,可以不需要写id。
- 行列偏移
%x[row, col] x是观测序列,row代表相对的行偏移, col代表相对的列偏移。比如 %x[1, 0]代表后一个字对当前字的影响。%x[-2, 4] 代表前第二个字对第5列特征的影响(上图只有一列特征)。当然也可以进行特征组合,如U05:%x[-2,0]/%x[-1,0]/%x[0,0],代表当前字的前第二个字、前一个字和当前字的特征组合。还是以上图为例,假设当前行为”满”,那么对应的特征为:
特征模板 | 意义 | 代表特征 |
---|---|---|
U00:%x[-2,0] | -2行,0列 | 向 |
U01:%x[-1,0] | -1行,0列 | 充 |
U02:%x[0,0] | 0行,0列 | 满 |
U03:%x[1,0] | 1行,0列 | 希 |
U04:%x[2,0] | 2行,0列 | 望 |
U05:%x[-2,0]/%x[-1,0]/%x[0,0] | -2列,0行与-1列,0行和0列0行组合 | 向/充/满 |
U06:%x[-1,0]/%x[0,0]/%x[1,0] | -1列,0行与0列,0行和1列0行组合 | 充/满/希 |
U07:%x[0,0]/%x[1,0]/%x[2,0] | -1列,0行与0列,0行和1列0行组合 | 满/希/望 |
U08:%x[-1,0]/%x[0,0] | -1列,0行与0列,0行组合 | 充/满 |
U09:%x[0,0]/%x[1,0] | 0列,0行与1列,0行组合 | 满/希 |
unigram特征模板是对训练数据的每一行中的每列特征都生效的,所以会产生很多的特征,对于特征模板中的每一行,产生的特征数为 $L \times N$ ,其中 $L$ 是输出序列标签类别数, $N$ 是此行模板在训练集上展开的唯一样本数。这里比较困惑的就是模板如何在训练集上展开,以及唯一样本数是怎么统计的了,下面说说我的理解。以特征模板: U02:%x[0,0] 对上面训练模板中的部分样本“不依不饶,喋喋不休”为例,分词标签为BEMS四类,则会生成以下 $4 \times 6$ 个特征函数:
|
|
也就是该特征与标签组合的唯一样本数。
Bigram feature: 用来生成转移特征函数
Bigram 特征根据第一个字符’B’识别,用于描述 Bigram 的特征模板。 这个特征一般不用处理,只保留一个 $B$ 就好了。也可以参照unigram的格式生成模板,只不过这样模型会变得很复杂。Bigram在生成转移特征函数的时候会把上一时刻标签 $y_{i-1}$ 也考虑进来,有点类似于:
func1 = if(pred_output=B and output=B and feature=U02:'不') return 1 else 0
因此产生的特征数为 $(L \times L \times N)$,其中,$L$ 是输出序列标签类别数, $N$ 是此行模板在训练集上展开的唯一样本数。
训练
按照以上格式整理好训练就可以直接调用CRF++来对训练语料进行分词。在训练数据目录下执行:
# crf_learn template_file train_file model_file
其中,template_file 和 train_file 分别为事先准备好的特征模板文件和训练数据文件,model_file 是训练生成的模型文件,用于测试。
测试
训练好之后,按照训练数据的格式整理好测试数据,然后执行:
# crf_test model_file test_file > test_result_file
效果评估
评估方法以下三种:
1、自己写个脚本评估效果,评估方式参考:中文分词器分词效果的评测方法
2、使用Bakeoff 2005 icwb2-data数据集中自带的评分脚本:icwb2-data/scripts/score
3、使用conlleval.perl评测,该方法可以直接对crf_test生成的结果进行评估,无需做进一步处理。
# perl conlleval.pl -r -d "\t"< pku_test_result.data
processed 172733 tokens with 172733 phrases; found: 172733 phrases; correct: 162436.
accuracy: 94.04%; precision: 94.04%; recall: 94.04%; FB1: 94.04
B: precision: 94.85%; recall: 96.24%; FB1: 95.54 57713
E: precision: 94.65%; recall: 96.04%; FB1: 95.34 57713
M: precision: 78.98%; recall: 86.42%; FB1: 82.53 12560
S: precision: 96.42%; recall: 90.85%; FB1: 93.55 44747
问题整理
在使用crf++分词过程中遇到一些问题,记录下:
1、训练数据格式错误:encoder.cpp(340) [feature_index.open(templfile, trainfile)] feature_index.cpp(174) [max_size == size] inconsistent ;出现这个错误是没有按照规定的格式来整理训练数据,特征之间用制表符分隔,句子之间换行符…
2、x86_64-conda_cos6-linux-gnu-gcc错误
安装python 接口, 执行python setup.py build 的时候出现错误:error: command ‘x86_64-conda_cos6-linux-gnu-gcc’ failed with exit status 1 。
解决方法:需要安装gxx_linux-64,执行命令:conda install gxx_linux-64。
3、在python环境中导入CRFPP出现错误:ImportError: libcrfpp.so.0: cannot open shared object file: No such file or directory
解决方法见ubuntu16.04 CRF++安装及报错处理