条件随机场(4)——CRF++分词训练和预测

这是CRF的最后一篇总结。这篇总结是训练分词模型和预测。CRF、最大熵、隐马尔科夫模型貌似都可以做分词、词性标注、命名实体识别,以前只是在hanLP里面用这些工具,实际上是不明所以的。现在要开始弄明白其中原理,并自己训练模型了。好吧,又啰嗦了。

先说一说CRF++的训练和预测吧,在《条件随机场(3)——学习和预测》中提到CRF++训练接口为crf_learn,预测接口为crf_test,直接调用就可以了,官网上有调用命令。
crf_learn需要的训练数据是有标注的,如果是分词,就对一个词中的每一个字进行标注,标注值为每个字在词中的位置,4tag标注的话,标注集合为Y={S,B,M,E},S表示单字为词,B表示词的首字,M表示词的中间字,E表示词的结尾字。同时,同一个词字与标注之间用’\t’分隔,字与字之间通过’\n’分隔。句子与句子之间通过一行空白行分隔。
crf_learn接受的数据是这样的:

迈      B
向      E
充      B
满      E
希      B
望      E
的      S
新      S
世      B
纪      E
—       B
—       E
一      B
九      M
九      M
八      M
年      E
新      B
年      E
讲      B
话      E
(      S
附      S
图      B
片      E
1      S
张      S
)      S

中      B
共      M
中      M

crf_test接收的预测语句也不是通常的句子,之前直接将一整句话预测,预测出来的结果是

今天天气很好,出去逛逛。    S

以为是模型训练问题,后来发现,是crf_test对预测数据要求的格式不同,这句话被crf_test会当做一个字来处理的。crf_test接收的预测数据是这个样子的

共      B
同      B
创      B
造      B
美      B
好      B
的      B
新      B
世      B
纪      B
—       B
—       B
二      B
○       B
○       B
一      B
年      B
新      B
年      B
贺      B
词      B

(      B
二      B
○       B
○       B
○       B
年      B
十      B
二      B
月      B
三      B

本笨妞采用了pku的语料来训练。pku的语料是这种格式的:

迈向  充满  希望  的  新  世纪  ——  一九九八年  新年  讲话  (  附  图片  1  张  )  
中共中央  总书记  、  国家  主席  江  泽民  
(  一九九七年  十二月  三十一日  )  
12月  31日  ,  中共中央  总书记  、  国家  主席  江  泽民  发表  1998年  新年  讲话  《  迈向  充满  希望  的  新  世纪  》  。  (  新华社  记者  兰  红光  摄  )  
同胞  们  、  朋友  们  、  女士  们  、  先生  们  :  
在  1998年  来临  之际  ,  我  十分  高兴  地  通过  中央  人民  广播  电台  、  中国  国际  广播  电台  和  中央  电视台  ,  向  全国  各族  人民  ,  向  香港  特别  行政区  同胞  、  澳门  和  台湾  同胞  、  海外  侨胞  ,  向  世界  各国  的  朋友  们  ,  致以  诚挚  的  问候  和  良好  的  祝愿  !

预测语料是这样的:

共同创造美好的新世纪——二○○一年新年贺词
(二○○○年十二月三十一日)(附图片1张)
女士们,先生们,同志们,朋友们:
2001年新年钟声即将敲响。人类社会前进的航船就要驶入21世纪的新航程。中国人民进入了向现代化建设第三步战略目标迈进的新征程。
在这个激动人心的时刻,我很高兴通过中国国际广播电台、中央人民广播电台和中央电视台,向全国各族人民,向香港特别行政区同胞、澳门特别行政区同胞和台湾同胞、海外侨胞,向世界各国的朋友们,致以新世纪>第一个新年的祝贺!

采用通用的4tag标注法,下面是从“我爱自然语言”的博客copy来的代码,用于对训练语料和预测语料进行标注,代码如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: 52nlpcn@gmail.com
# Copyright 2014 @ YuZhen Technology
#
# 4 tags for character tagging: B(Begin), E(End), M(Middle), S(Single)

import codecs
import sys

def character_tagging(input_file, output_file):
    input_data = codecs.open(input_file, 'r', 'utf-8')
    output_data = codecs.open(output_file, 'w', 'utf-8')
    for line in input_data.readlines():
        word_list = line.strip().split()
        for word in word_list:
            if len(word) == 1:
                output_data.write(word + "\tS\n")
            else:
                output_data.write(word[0] + "\tB\n")
                for w in word[1:len(word)-1]:
                    output_data.write(w + "\tM\n")
                output_data.write(word[len(word)-1] + "\tE\n")
        output_data.write("\n")
    input_data.close()
    output_data.close()

if __name__ == '__main__':
    if len(sys.argv) != 3:
        print "pls use: python make_crf_train_data.py input output"
        sys.exit()
    input_file = sys.argv[1]
    output_file = sys.argv[2]
    character_tagging(input_file, output_file)

同样,预测语料标注代码如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: 52nlpcn@gmail.com
# Copyright 2014 @ YuZhen Technology
#
# 4 tags for character tagging: B(Begin), E(End), M(Middle), S(Single)

import codecs
import sys

def character_split(input_file, output_file):
    input_data = codecs.open(input_file, 'r', 'utf-8')
    output_data = codecs.open(output_file, 'w', 'utf-8')
    for line in input_data.readlines():
        for word in line.strip():
            word = word.strip()
            if word:
                output_data.write(word + "\tB\n")
        output_data.write("\n")
    input_data.close()
    output_data.close()

if __name__ == '__main__':
    if len(sys.argv) != 3:
        print "pls use: python make_crf_test_data.py input output"
        sys.exit()
    input_file = sys.argv[1]
    output_file = sys.argv[2]
    character_split(input_file, output_file)

数据处理完之后,用以下命令训练

crf_learn -f 3 -c 4.0 example/seg/template data/tagging/pku_train_tagging.utf8 model/crf_model_pku -t

crf_learn可自定义的参数还有很多,具体参见官网。以上命令中,template是特征模板,偷懒直接将example里面的分词模板拿来用了。
模板是这样的:

# 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

特征模板的解释这里最权威。Unigram和Bigram是特征模板的类型。U00:%x[-2,0]中,U表示类型为Unigram,00表示特征的id,%x[-2,0]表示x(在这里为字)的位置,-2表示x的行偏移,0表示x的列偏移。
用pku语料为例

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]            ==>充/满

而这些x的标注为

迈      B
向      E
充      B
满      E
希      B

根据特征模板和标注数据可以做出的特征是这样的:

func1 = if (output = S and feature="U00:迈") return 1 else return 0
func2 = if (output = B and feature="U00:迈") return 1 else return 0
func3 = if (output = M and feature="U00:迈") return 1  else return 0
func4 = if (output = E and feature="U00:迈") return 1  else return 0
...

对于这5个字来说,能生成(4*10)个特征函数(L*N),4是Y的取值个数,我们是4tag标注,所以L是4,这5个字通过特征模板,扩展成了上面的10个特征,N是10。对于整个语料来说,L还是4,N则是整个语料根据特征模板能扩展出来的特征个数。
在上一篇总结训练过程中提到,当特征与实际的标注匹配上时,会影响该特征对应的权重的梯度,以此来更新各特征对应的权重。
上面只是官方给出的特征模板,很多同道中人都直接用这个模板(我也直接用了),但是实际上模板是可以自己根据实际情况自定义的哦。

根据命令训练完之后,得到模型crf_model_pku,然后拿模型来预测句子,命令如下:

crf_test -m model/crf_model_pku data/tagging/pku_test_tagging.utf8 >> data/result/pku_test_result

结果是这样的

扬      B       B
帆      B       E
远      B       B
东      B       E
做      B       S
与      B       S
中      B       B
国      B       E
合      B       B
作      B       E
的      B       S
先      B       B
行      B       E

希      B       B
腊      B       E
的      B       S
经      B       B
济      B       E
结      B       B
构      B       E
较      B       S
特      B       B
殊      B       E
。      B       S

海      B       B
运      B       E
业      B       S
雄      B       B
踞      B       E
全      B       B

用make_crf_train_data.py相反的过程,就可以将结果通过第三列标注将词分开了。

至此,CRF的学习基本完成了。接下来就是BiLSTM-CRF了,这个算法貌似才是现在segment、NER、词性标注的主流算法。不过以手上的pku、msr的数据跑LSTM,也不知道效果如何哦。先不管了,研究之后再说。

另,我手上的中文语料全部存在这里的:https://pan.baidu.com/s/1jIu2j9W 密码:z2b6

相关文章
相关标签/搜索