中文NER任务实验小结:BERT-MRC的再优化
©作者 | 邱震宇
单位 | 华泰证券算法工程师
研究方向 | NLP方向
前言
熟悉我的读者,应该看过我之前写过的一篇关于中文 NER 任务实践的文章(邱震宇:中文 NER 任务实验小结报告——深入模型实现细节 [1],在那篇文章中,我介绍了一个做 NER 任务的新范式:将实体抽取任务转化为抽取式阅读理解任务。
其核心思想就是将待抽取的实体标签描述作为 query 与原始的文本进行拼接,然后基于 BERT 做对应 span 的抽取。通过这种方式能够让模型学习到实体标签本身的语义信息,引入先验知识,从而提升模型的能力。在我之后的实现中,也进一步验证了这个方法的有效性。但我尝试将这个方法应用到实际场景任务时,却遇到了很多限制。
本文涉及源码: https://github.com/qiufengyuyi/lear_ner_extraction
2.1 BERT-MRC的缺陷
2.2 LEAR模型架构
我尝试过 concat 拼接融合,最后发现效果并不如直接 add。在此架构下,add 操作的融合有更高的信息利用率。
之后,我们引入一个 dense layer,将最终的文本表达输出,其中激活函数使用了 tanh,相比 sigmoid,其值域更广,而且防止向量表示的绝对值过大:
在模型实现时,所有标签类别和所有 token 的计算可以在一次矩阵的计算中完成。
span抽取分类模块
虽然上面的公式看起来还是有点绕,但实现起来也不是很复杂,在 TensorFlow 的框架下,可以这么实现:
def classifier_layers(self,input_features,type="start"):
#input features
# batch_size,input_seq_len,num_labels,hidden_size
# input_shape = modeling.get_shape_list(input_features)
# batch_size = input_shape[0]
# seq_length = input_shape[1]
classifer_weight = tf.get_variable(
name="classifier_weight"+"_"+type,
shape=[self.num_labels, self.attention_size],
initializer=modeling.create_initializer(self.bert_config.initializer_range))
classifer_bias = tf.get_variable(name="classifier_bias"+"_"+type,shape=[self.attention_size],initializer=tf.constant_initializer(0))
output = tf.multiply(input_features,classifer_weight)
output += classifer_bias
#[batch_size, input_seq_len, num_labels]
output = tf.reduce_sum(output,axis=-1)
return output
其中 tf.multiply 是一个 element-wise 矩阵乘法,且支持 broadcasting 机制。
论文中提出了两种 decoding 策略,分别为最近距离策略和启发式策略。最近距离策略就是先定位所有的 start 位置,然后找距离 start 位置最近的 token 作为 end。而启发式策略则稍微复杂一些,若定位到一个 start1 位置,不会马上决定它作为一个 span 的起始位置,若后面的 token 如果也是一个候选的 start2,且其概率还要大于 start1,则会选择新的 start2 作为 start 候选。具体大家可以参考论文后面的附录,有详细的算法流程。
说实话,我感觉论文中附录的算法伪代码有些问题,对于 end 位置的判断应该不需要像 start 一样选择概率最高的。另外,我自己的训练数据预处理时,对于单字成实体的情况,我的 end 位置是置为 0 的,而论文和开源代码中的设置却不同,因此不好做直接对比。
我自己实现时,使用的是变种的最近距离策略。因为选择与 start 最近的 end 时,有可能会越过下一个新的 start 位置,这有可能是算法本身预测有问题,或者有实体嵌套的情况(目前还未考虑嵌套实体的 case),如图 3 所示。
▲ 图4 自定义的最近距离decoding策略
LEAR实现
if label_token_ids.shape.ndims == 3:
label_token_ids = label_token_ids[0,:,:]
label_token_type_ids = label_token_type_ids[0,:,:]
label_token_masks = label_token_masks[0,:,:]
label_token_length = label_token_length[0,:]
label_lexicons = label_lexicons[0,:,:,:]
2. 原始文本和标签文本共享 encoder 参数,这里要对 google原始的 BERT 的 modeling.py 进行修改,在模型定义的时候,添加 auto_reuse 配置,同时在调用 bert 的时候,传入 scope="bert":
with tf.variable_scope(scope, default_name="bert",reuse=tf.AUTO_REUSE):
3. 论文和官方开源的代码都没有对 attention 计算后的分数进行 scaling。我觉得可能的原因是融合模块中的 attention 操作只有一层, 且注意力的分数并没有直接用于词 softmax 的计算,而是融合到了原始文本的向量表示中,另外在最后输出的时候使用了 tanh 进行了激活,输出的值不会太大,因此即使不做 scale,也不会产生很严重的梯度消失问题,并影响后面的分类器的计算。我这边也验证了,加入了 scaling 之后,效果相差不多。
最后,我将 LEAR 在中文的 MSRA 的 NER 数据集上进行了验证,同时与之前已经实现过的 BERT-MRC 进行了对比。验证指标则是考虑了实体类型后的 micro-level 的 f1 分数,具体来说我会将实体类型字符串与实体内容拼接,做去重后,进行 exact match 匹配。最终,得到的结果大体如下:
因为很长时间没写文章了,所以写一次就尽量多包含点内容。为了奖励读到这边的同学,我再奉送一段额外的 NER 优化技巧。
读过我上一篇中文 NER 总结的同学应该记得我在那篇文章中提到尝试在 BERT 中引入词汇信息,当时尝试的办法很依赖分词的质量,且融合词汇信息的方式也比较简单。因此,这次我参考了最近比较火的一篇论文来做词汇增强:Simplify the Usage of Lexicon in Chinese NER [4]。
这篇论文解读我就不做了,大家可以参考这篇:JayJay:中文 NER 的正确打开方式: 词汇增强方法总结(从 Lattice LSTM 到 FLAT)[5]。这个方法的核心思想是先准备一份词向量和词表,然后对当前所有语料中的文本字符统计其分别属于 BMES 的信息,B 代表词的开头,M 代表词的中间,E 代表词的结束,S 代表单独成词。
▲ 图5 词汇增强方法
其使用了 trie 数来构建词典树,这也是海量字符串匹配场景中会使用的一种方法。我在实现的时候,没有用这种方式,为了快速得到效果,使用了一种比较基础的方式,就是在数据预处理的时候,将每个字符对应的 BMES 的词列表信息都预先存储起来,在模型输入的时候直接读取信息,这样做速度不慢,但是对内存的要求就比价高了,类似于空间换时间。
TIPS:我在构建词表的时候,为了提高速度,将出现频率小于 5 的词都过滤掉了。
可以看到增加了词汇增强信息后,LEAR 的效果有一定的提升。如果对这个方法感兴趣,可以尝试实现完整功能的增强,让增强信息也参与到模型学习中,最后应该会有很大的提升。
参考文献
特别鸣谢
感谢 TCCI 天桥脑科学研究院对于 PaperWeekly 的支持。TCCI 关注大脑探知、大脑功能和大脑健康。
更多阅读
#投 稿 通 道#
让你的文字被更多人看到
如何才能让更多的优质内容以更短路径到达读者群体,缩短读者寻找优质内容的成本呢?答案就是:你不认识的人。
总有一些你不认识的人,知道你想知道的东西。PaperWeekly 或许可以成为一座桥梁,促使不同背景、不同方向的学者和学术灵感相互碰撞,迸发出更多的可能性。
PaperWeekly 鼓励高校实验室或个人,在我们的平台上分享各类优质内容,可以是最新论文解读,也可以是学术热点剖析、科研心得或竞赛经验讲解等。我们的目的只有一个,让知识真正流动起来。
📝 稿件基本要求:
• 文章确系个人原创作品,未曾在公开渠道发表,如为其他平台已发表或待发表的文章,请明确标注
• 稿件建议以 markdown 格式撰写,文中配图以附件形式发送,要求图片清晰,无版权问题
• PaperWeekly 尊重原作者署名权,并将为每篇被采纳的原创首发稿件,提供业内具有竞争力稿酬,具体依据文章阅读量和文章质量阶梯制结算
📬 投稿通道:
• 投稿邮箱:hr@paperweekly.site
• 来稿请备注即时联系方式(微信),以便我们在稿件选用的第一时间联系作者
• 您也可以直接添加小编微信(pwbot02)快速投稿,备注:姓名-投稿
△长按添加PaperWeekly小编
🔍
现在,在「知乎」也能找到我们了
进入知乎首页搜索「PaperWeekly」
点击「关注」订阅我们的专栏吧