GPLinker:基于GlobalPointer的事件联合抽取
By 苏剑林 | 2022-02-21 | 76468位读者 |大约两年前,笔者在百度的“2020语言与智能技术竞赛”中首次接触到了事件抽取任务,并在文章《bert4keras在手,baseline我有:百度LIC2020》中分享了一个转化为BERT+CRF做NER的简单baseline。不过,当时的baseline更像是一个用来凑数的半成品,算不上一个完整的事件抽取模型。而这两年来,关系抽取的模型层见迭出,SOTA一个接一个,但事件抽取似乎没有多亮眼的设计。
最近笔者重新尝试了事件抽取任务,在之前的关系抽取模型GPLinker的基础上,结合完全子图搜索,设计一个比较简单但相对完备的事件联合抽取模型,依然称之为GPLinker,在此请大家点评一番。
任务简介 #
事件抽取是一个比较综合的任务。一个标准的事件抽取样本如下:
每个事件会有一个事件类型以及相应的触发词,并且配有不同角色的论元。事件类型和论元角色是在约定的有限集合(schema)中选择,而触发词和论元一般情况下都是输入句子的片段,少数情况下也是可枚举的分类对象(百度的DuEE-fin出现过)。原则上来说,事件抽取模型的设计取决于评价指标,在LIC2020中,我们之所以可以将事件抽取转化为一个NER问题,是因为当时的评测指标只考察(事件类型, 论元角色, 论元)构成的三元组,因此我们可以将(事件类型, 论元角色)组合成一个大类,然后就跟NER对应上了。
当然,这只是针对该指标的一种取巧方式。对于真实的事件抽取场景,我们自然是希望把标准格式的事件抽取出来,也就是设计一个尽量完备的模型。下面将介绍我们用于事件抽取的GPLinker模型,它基本达到了简单而完备的要求。
统一论元 #
我们多次提到了“完备”这个词,它是什么含义呢?具体来说,我们希望最终设计出来的模型,至少在理论上能适用于尽可能多情形的事件抽取任务。传统的事件抽取模型一般分为“触发词检测”、“事件/触发词类型识别”、“事件论元检测”和“论元角色识别”四个子任务,也就是说要先检测触发词然后基于触发词做进一步的处理,所以如果训练集没有标注出触发词,它就没法做了,这说明传统思路是不够完备的。
为了统一有无触发词的场景,我们这里就触发词也当作事件的一个论元角色,这样有无触发词就是增减一个论元而已,所以主要还是在于论元识别和事件划分。对于论元识别,我们还是将(事件类型, 论元角色)组合成一个大类,然后转化为NER问题,但要注意的是,不同的实体是可能嵌套的,所以之前基于CRF的NER其实是不够用的,这里用能识别嵌套实体的GlobalPointer来完成。
前面还提到,像DuEE-fin这样的任务还出现了分类式的论元类型,其论元不是输入文本的一个片段,而是有限集合中选1个,考虑到这种论元类型不多,所以这里也将其转化为抽取式论元。以DuEE-fin为例,其“公司上市”事件类型的“环节”论元,四个候选值为筹备上市、暂停上市、正式上市、终止上市,那么我们不将“环节”视为论元类型,而分别将筹备上市、暂停上市、正式上市、终止上市视为四种论元类型,而对应的实体则为触发词,这样我们要抽取的论元不是分类式的(公司上市, 环节, XX上市),而是抽取式的(公司上市, XX上市, 触发词),最后我们在模型后处理阶段再将它们转回来就好。
完全子图 #
至于事件划分,一个很自然的想法就是直接把所有具有同一事件类型的论元聚合起来作为一个事件,但这也是不够完备的,因为同一个输入可能有多个同一类型的事件。如果加上触发词呢?还是不够,多个同一类型的事件还可能公用同一个触发词,比如DuEE有一个样本是“主要成员程杰、王绍伟被法院一审判处有期徒刑22年和20年。”,分别有两个事件“程杰判处有期徒刑22年”和“王绍伟判处有期徒刑20年”,触发词都是“有期徒刑”,事件类型都是“入狱”。
所以,我们需要设计一个额外的模块来做事件划分。我们认为,同一事件的各个论元是有联系的,这种联系可以用无向图来描述。也就是说,我们将每个论元看成是图上的一个节点,而同一事件任意两个论元的节点可以连上一条边而成为相邻节点,而如果两个论元从未出现在同一事件中,那么对应的节点则没有边(不相邻)。这样一来,同一事件的任意两个节点都是相邻的,这我们称为“完全图(Complete Graph)”,也称为“团(Clique)”,事件划分转化为图上的完全子图搜索。
那么如何构建这个无向图呢?我们沿用TPLinker的做法,如果两个论元实体有关系,那么它们的(首, 首)和(尾, 尾)都能匹配上,我们可以像关系抽取的GPLinker一样,用GlobalPointer来预测它们的匹配关系。特别地,由于我们只需要构建一个无向图,所以我们可以mask掉下三角部分,所有的边都只用上三角部分描述。
搜索算法 #
假定我们已经有了描述论元关系的有向图,那么我们要怎么搜索出所有的完全子图呢?看上去跟图分割有点相似,但不完全一样,因为在我们的场景下,节点是可以重复使用的,这意味着同一个实体同时是多个不同事件的论元。比如上图中的8个节点可以搜索出两个完全子图,其中节点$D$同时出现在两个子图中,这代表我们可以划分出两个事件,它们有共同的论元$D$。
经过分析,笔者构思了如下的递归搜索算法:
1、枚举图上的所有节点对,如果所有节点对都相邻,那么该图本身就是完全图,直接返回;如果有不相邻的节点对,那么执行步骤2;
2、对于每一对不相邻的节点,分别找出与之相邻的所有节点集(包含自身)构成子图,然后对每个子图集分别执行步骤1。
还是以上图为例,我们可以找出$B$、$E$是一对不相邻节点,那么我们可以分别找出其相邻集为$\{A,B,C,D\}$和$\{D,E,F,G,H\}$,然后继续找$\{A,B,C,D\}$和$\{D,E,F,G,H\}$的不相邻节点对,发现找不到,所以$\{A,B,C,D\}$和$\{D,E,F,G,H\}$都是完全子图。注意这个不依赖于不相邻节点对的顺序,因为对于“所有”不相邻节点对我们都要进行同样的操作,比如我们又找到$A$、$F$是一对不相邻节点,那我们同样要找其相邻集$\{A,B,C,D\}$和$\{D,E,F,G,H\}$并递归执行。所以,在整个过程中,我们可能会得到很多重复结果,但不会漏了某个结果,也不会搜识别顺序影响,最后再去重即可。
此外,每次搜索的时候,我们只需要对同一事件类型的节点进行搜索,而多数情况下同一事件类型的论元只有个位数,所以上述算法看似复杂,实际运行速度还是很快的。
实验结果 #
现在我们的事件抽取设计已经介绍完了。总的来说,我们需要一个嵌套实体识别模型来识别论元,然后分别需要一个“首-首”匹配和“尾-尾”匹配模型来构建论元之间的关系,这些模块跟关系抽取的GPLinker模型出奇一致,并且也都可以用GlobalPointer来完成,所以我们依然将它命名为“GPLinker”。代码统一整理在:
Github地址:https://github.com/bojone/GPLinker
笔者简单在DuEE和DuEE-fin上做了实验,并提交到了千言排行榜,结果分别如下:
\begin{array}{c|ccc}
\hline
& \text{precision} & \text{recall} & \text{f1} \\
\hline
\text{DuEE} & 82.65 & 80.31 & 81.47 \\
\hline
\text{DuEE-fin} & 50.35 & 65.19 & 56.82 \\
\hline
\end{array}
单看这两个成绩,在排行榜上排第5,不算出彩,距离榜1还有点远。不过这篇文章也不是专门去打榜,所以也没有进一步针对比赛数据来优化,目前这个成绩,也算是可圈可点了。至于没有对比其他事件抽取模型,是因为笔者对这一领域实在不熟,简单看了几篇关系抽取的论文,感觉里边的模型都异常复杂,提不起兴趣去复现。
最后,代码用的GlobalPointer,全部都是Efficient GlobalPointer,笔者也比较过使用标准版的GlobalPointer,发现它前期收敛更快但最终效果会更差。这也再次肯定了Efficient GlobalPointer的有效性。
模型反思 #
GPLinker做事件抽取,追求的是简单和完备,实际情况下可能并不是最有效的方案。一个很明显的问题是,它对数据的依赖比较明显,在小样本的情况下效果可能欠佳。这本质上也是GPLinker足够完备导致的,因为完备意味着自由度相当大(可以容纳足够多的场景),模型没有多少先验信息,所以学习难度也会增加。
但是,GPLinker确实有着简单高效的特点,而且理论上也不存在Exposure Bias问题,所以实际情况下,如果GPLinker效果欠佳,那么一种可行的方案是先用其他能把效果做上去的方案(很可能效果好但效率差)做一版模型,然后蒸馏到GPLinker上去,这样就可以兼顾效果与效率了。
文章小结 #
本文介绍了将GPLinker用于事件抽取的思路,先用嵌套实体抽取的方式来抽取论元,然后将事件的划分转化为完全子图搜索问题,整个模型相对简洁和完备,并且理论上不存在Exposure Bias问题。
转载到请包括本文地址:https://kexue.fm/archives/8926
更详细的转载事宜请参考:《科学空间FAQ》
如果您还有什么疑惑或建议,欢迎在下方评论区继续讨论。
如果您觉得本文还不错,欢迎分享/打赏本文。打赏并非要从中获得收益,而是希望知道科学空间获得了多少读者的真心关注。当然,如果你无视它,也不会影响你的阅读。再次表示欢迎和感谢!
如果您需要引用本文,请参考:
苏剑林. (Feb. 21, 2022). 《GPLinker:基于GlobalPointer的事件联合抽取 》[Blog post]. Retrieved from https://kexue.fm/archives/8926
@online{kexuefm-8926,
title={GPLinker:基于GlobalPointer的事件联合抽取},
author={苏剑林},
year={2022},
month={Feb},
url={\url{https://kexue.fm/archives/8926}},
}
September 26th, 2022
[...]GPLinker:基于GlobalPointer的事件联合抽取[...]
May 30th, 2023
[...]GPLinker:基于GlobalPointer的事件联合抽取[...]