这是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