!!!这是一篇未完成的文章,不慎点击发布,请注意!!!
之前的一篇文章写了基于CRF的命名实体识别模型,并上传了代码,现在添加BiLSTM部分。关于BiLSTM的介绍,可以看我的另一篇文章,这里主要是训练过程以及其中的一些问题。
声明:训练部分的代码及数据来源于网上,对原作者的成果表示感谢。遗失了原文链接,找到后会添加。
1.为什么是BiLSTM与CRF的结合
在利用CRF搭建模型的时候已经指出,这种方法的一大不足之处就是,需要人工手动设置特征模板,据此算法才可以提取出特征。由此可知,特征模板设置的好坏就会直接影响到实体识别的效果。为了解决这个问题,人们提出了利用BiLSTM进行特征的提取。
实际上,如果在BiLSTM模型后添加一个softmax
层,即将BiLSTM的输出控制到你想要的输出维度(在NER任务中,假设输入文本的长度为
L
L
L,实体种类为
n
n
n,那么输出维度就是
n
×
L
n×L
n×L),这个最终的输出矩阵就看以看做是得分矩阵,我们只需要逐个找出输入单字的得分最大的标签即可。图示如下:
可以看出,利用BiLSTM构建的模型可以取得一定的效果。然而,根据最大得分选择标签并不总是得到正确的结果,尽管BiLSTM可以考虑上下文的信息,但它不能检查输出结果的逻辑,即根据BiLSTM搭建的NER模型可能会得到B_Per I_Loc B_Loc I_Per O
等结果。如果可以对结果进行一定的筛选,比如:O
后面不能接I
开头的标签、不会单独出现I
标签等,那么结果的准确率将会得到提升。
要完成这样的功能,需要怎么做呢?我们知道,CRF模型中计算了转移概率,而上述所说的那些逻辑错误的例子在对语料进行标注时是不会出现的,即它们的转移概率会非常小。如果把BiLSTM的输出作为CRF的输入,那么就可以将发生逻辑错误的部分“扭转”过来,从而提高准确率。
2.模型构建
这部分,将展示如何利用tensorflow
框架来搭建一个上述NER模型并对其进行训练。
2.1.数据部分
2.1.1.数据描述
在./data
文件夹中保存了三个文件,分别是example.dev
、example.test
、example.trian
,分别对应开发集、测试集与训练集。其中的数据格式如下:
海 O
钓 O
比 O
赛 O
地 O
点 O
在 O
厦 B-LOC
门 I-LOC
与 O
金 B-LOC
门 I-LOC
之 O
间 O
的 O
海 O
域 O
。 O
可以看出,每个字占一行,后面是一个空格,然后是它的实体标签。在原始数据中,标签的方式是B-I-O
。一个句子结束后,是一个空行,然后按照同样的格式接第二个句子。
2.1.2.数据导入
读入原始数据,用到了loader.py
包中的load_sentences
函数,该函数的输出为一个三层的嵌套列表,原始数据中每一行组成一个列表,每个句子组成第二层列表,所有的句子组成第三层列表。
2.1.3.标签更新
一般来说,实体标签分的越细,则NER的结果越好。因此,用B-E-S-I-O
的标签替换掉B-I-O
标签。loader.py
中还有一个update_tag_scheme
函数,用于将导入的数据的标签进行转换。该函数只是对原始数据的标签进行了转换操作,并没有输出。
2.1.4.词向量
任何神经网络均只能接受数值型的输入,所以自然语言处理的首要任务就是将字转化为数值。一种合适的转化方法叫做“词嵌入”(word embedding),或者叫“词向量”。通过这种方式,可以把所有的词转化为特定长度的向量,而且它可以保证具有类似意义的词之间的余弦距离很小,反之则距离很大。
在本次训练中,保存好的词向量文件为wiki_100.utf8
。用文本编辑器打开后可以发现,文件的正文部分就是每个字(字符)及其对应的词向量,这里维度是100。当语料库更新时,应该重新生成该映射文件,以免有新的汉字加入。关于如何训练得到词向量,超出本文的范围,不做过多说明。
2.1.5.数据分批
接下来就是将数据按照batch_size的大小进行分批。这里定义了一个类BatchManager
,通过调用该类,我们可以实现数据的分批