这几天正在写中文新词发现的程序,需要用到Peng(2004)中的CRF模型。因为Python语言写实验代码各种舒服,所以一直在用Python,但是发现我要的功能没有现成的库好用(CRF++虽然说可以,但是用起来很Trick),于是自己用Python手写了一个CRF的Tagger作为副产品,这个当然只包含了Tag的过程,L-BFGS训练什么的,这个方法到现在我还没有看懂就不会去写了。

其实最困难的部分就是怎样去读CRF的模型了,幸好CRF++的训练程序crf_learn支持一个-t命令,就是把模型文件再用一个文本模式输出,这个就比原生的CRF++模型好读多了。原生的CRF++模型非常复杂,里面还包含了一个双数组Trie树来存储特征。

可以读取模型之后,Tagger的过程就非常简单了,这边因为不需要计算alpha, beta或者每个节点的概率分布,所以只要一个前向的viterbi就可以了,跟HMM差不多。读模型的代码可以在crf_model.py里面找到,tagger的代码可以在crf_tagger.py里面找到,也非常简单。

接下去说说怎么得到模型吧。训练时使用CRF++自带的crf_learn程序训练,需要用到它的-t参数额外输出一个文本格式的模型文件。先把CRF++的模板文件和训练文件准备好使用

./crf_learn -t seg_template seg.4 ctb_seg.model

训练文件。其中template是CRF的模板文件,seg.4就是训练的文件,ctb_seg.model就是最后得到的模板文件。在训练完成后不仅仅会得到ctb_seg.model(这个是二进制的模型文件)还会得到ctb_seg.model.txt,这个才是我们要的文本形式的模型文件。在得到文本形式的模型文件之后,就可以用model_conv.py去把它转换成为pickle文件,加快模型读取的速度。

python3 model_conv.py ctb_seg.model.txt ctb_seg.pickle

OK,到此模型的训练和转换就完成了!可以试试一个代码中带的简单的分词程序,使用常用的6标注+6模板的CRF分词方法,训练用的是CTB7语料。

python3 crf_seg.py ctb_seg.pickle msr_test.utf8

msr_test.utf8是bakeoff-2005的MSA的测试语料,用之前最好先看看相关的版权说明。另外,不要对这个程序的速度抱有任何的幻想,如果真的想用可以使用CRF++自带的Python接口。顺带说一下,咱作为严重的版本控,代码都是用Python3写的。相关代码和模型文件已经放到了Github上,可以在下面找到

https://github.com/ling0322/pycrf