实战 _ 瑞金医院 MMC 知识图谱大赛初赛

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