1. 说明
《瑞金医院MMC人工智能辅助构建知识图谱大赛》是一个天池的自然语言处理相关的比赛,初赛是命名实体识别(Named Entity Recognition,简称NER)。具体说,就是从医学文档里标注出药名,疾病,病因,临床表现,检查方法等十二种实体的类别和位置。这是一个有监督学习,它的训练集是标注好的医学文档。
还是延续以往比赛的思路,找一个类似的简单项目,在其上修修改改,于是找到了 "参考1" 中的例程,它是一个在中文文本中标注地名,人名,组织名的程序,使用工具是tensorflow,算法是BiLSTM-CRF。
花了不到两天的时间,混进了复赛,虽说是在别人的代码上修修改改,但也不失为一个NLP相关的深度学习入门,顺便熟悉一下如何使用tensorflow。我做的工作很简单:参考代码2000多行,修改了不到200行,主要就是把那套代码对这个项目做一个适配,没啥可说的,本文主要梳理了深度学习如何应用于自然语言处理,算法原理,以及Tensorflow的一些用法。
2. 深度学习能解决自然语言处理中的哪些问题
前一阵被BERT刷屏了,它是谷歌AI团队新发布新模型,传说在机器阅读理解顶级水平测试中狂破11项纪录,全面超越人类,测试主要来自QLUE和SquAD,它们包括:
MNLI:判断两个句子间是继承,反驳,关系。
QQP:两个问句的类似程度。
QNLI:问答系统,区分问题的正确答案和同一段中的其它描述。
SST-2:电影评论的感情色彩标注。
CoLA:判断语法是否正确。
STS-B:语义相似度打分(1-5级)
MRPC:两句语义是否等价。
RTE:识别继承关系,和MNLI差不多,但数据集比较小。
SquAD:阅读理解,在段落中找答案。
看起来,只要有标注好的训练集数据,上述的各种关系都可以被预测,但是具体这些文字,又怎么代入模型呢?往下看…
3. 命名实体识别的BIO标注集
BIO标注将每个元素标注为“B-X”、“I-X”或者“O”。其中,“B-X”表示此元素所在的片段属于X类型并且此元素在此片段的开头,“I-X”表示此元素所在的片段属于X类型并且此元素在此片段的中间位置,“O”表示不属于任何类型。形如:
本 O 课 O 题 O 组 O 前 O 期 O 研 O 究 O 及 O 相 O 关 O 研 O 究 O 结 O 果 O 已 O 经 O 明 O 确 O 1 B-Anatomy 1 I-Anatomy β I-Anatomy - I-Anatomy H I-Anatomy S I-Anatomy D I-Anatomy 1 I-Anatomy 作 O 为 O 公 O 认 O 的 O 肥 B-Disease 胖 I-Disease 相 O 关 O 基 O 因 O
本例中借鉴的工具使用了BIO标注集,因此,把赛题数据置换成了BIO格式,以便代入,可以看到,具体方法是以字为单位的标注(以词为单位,可能在分词的过程中引入一些误差,我也没尝试)。转换之后更容易看出,它是一个词序列的关系问题。
4. 命名实体识别的具体方法
BiLSTM-CRF是近两年业界比较流行的解决命名实体识别的方法,本题使用的也是这个方法,本题的解法可以分为三个步骤:第一,将文字向量化(Word embedding);第二,计算上下文之间的关系(Bi-LSTM);第三,句子级的序列标注(CRF)。
我借鉴的代码位置在https://github.com/Determined22/zh-NER-TF, 功能是标注出文字中的人名,地名,组织名。模型相关的代码在 model.py,我觉得特别核心的100行不到,建议一边看代码一边看以下说明,不妨在其中打印一些信息,追踪一下它的运行流程。
(1) Word Embedding
Word Embedding词嵌入向量是NLP里一个重要的概念,我们利用它将一个词转换成固定长度的向量表示,从而便于进行数学处理。比如一篇文章里有1000个词,如果做one hot提取特取,则需要新增1000个特征,而像got与get会被识别为不同的词,如果使用Word Embedding,则可能将一些同义词提取为一个特征,从而实现降维和抽象的效果,具体实现方法是用上下文预测当前词,也可以理解为在神经网络上实现word2vec,详见参考二。
总之,可以把该层更解为对把词置换为词向量,本例中映射前支持的中文字符有3000多个,降维的目标是300维,而每一维中的特征值是从3000映射到300后的概率分布,即可能性(有点像PCA降维)。从代码中可以看到,用tensorflow降维就几行代码,确实很方便(见代码中的model.py:lookup_layer_op)。
(2) Bi-LSTM
LSTM(Long Short-Term Memory)长短期记忆网络,是RNN(时间递归神经网络)的一种,适合于处理和预测时间序列中间隔和延迟相对较长的重要事件。 Bi-LSTM即双向LSTM,本例中使用TensorFlow提供的LSTMCell类建立了正向和反向各一个LSTM,用bidirectional_dynamic_rnn() 结合成一个双向RNN(见代码中的model.py: biLSTM_layer_op)
这一步,计算的是上下文之间的关系,谁更可能出现在前面,谁更可能出现在谁后面,还没真正标注。
(3) CRF
CRF是条件随机场,是在给定随机变量X的条件下,随机变量Y的马尔可夫随机场,这里使用的线性链条件随机场,在条件概率模型P(Y|X)中,Y是输出变量,表示标记序列(状态序列),X是输入变量,表示需要标注的观测序列,用最大似然估计计算条件概率模型P ^(Y|X),预测时,对于给定的输入序列,求出条件概率P ^(y|x)最大的输出序列y ^(可能性最大的标注序列),详见代码中的tf.contrib.crf.crf_log_likelihood()。
要了解CRF的原理,先要了解马尔可夫链,以及很多基础知识(虽然很有趣,但需要花很多时间),但在程序里调用它很容易,几行代码就够了,一开始了解它怎么用就够了。
5. TensorFlow
C++相对于 C,Python相对于Java,TensorFlow相对于之前的程序架构,它们不只是语法不同,更多的是程序的组织方式,和思考方法不同。
Tensorflow是基于图(Graph)的计算系统。而图的节点则是由操作(Operation)来构成的,而图的各个节点之间则是由张量(Tensor)作为边来连接在一起的。所以Tensorflow的计算过程就是一个Tensor流图。Tensorflow的图则是必须在一个Session中来计算。
这样讲还是很抽象,简单地说,假设在程序中定义了三个函数(Operation): a,b,c,普通程序会顺序地调用a,b,c,而Tensorflow是指定a,b,c之间的依赖关系(Tensor),比如c依赖b的结果,b又依赖a的结果,运行时,使用Session.run(),只要告诉它,我最终需要c,程序自己找到它依赖的b,以及a来运行,通过数据流向把握整个过程,有点像Luigi的工作模式。具体请见"参考7"。
6. 参考
(1) DL4NLP —— 序列标注:BiLSTM-CRF模型做基于字的中文命名实体识别
https://www.cnblogs.com/Determined22/p/7238342.html
(2) 浅谈词嵌入(word embedding)
https://blog.csdn.net/puredreammer/article/details/78330821
(3) 神经网络中embedding层作用
https://www.cnblogs.com/bonelee/p/7904495.html
(4) tensorflow学习笔记--embedding_lookup()用法
https://blog.csdn.net/u013041398/article/details/60955847
(5) tensorflow笔记3:CRF函数:tf.contrib.crf.crf_log_likelihood()
https://www.cnblogs.com/lovychen/p/8490397.html
(6) [学习笔记] TensorFlow 入门之基本使用
https://www.cnblogs.com/flyu6/p/5555161.html
(7) Tensorflow学习笔记2:About Session, Graph, Operation and Tensor
https://www.cnblogs.com/lienhua34/p/5998853.html