引言
这是第二篇文章,因为主客体提取需要依赖触发词识别。上一篇是讯飞2020年事件提取比赛第一名-触发词提取。
1. 跑通代码
1 | args = TrainArgs().get_parser() |
2. 模型结构
3. 模型结构
1 | Role1Extractor( |
4. 训练中需要注意的点
1. label构造
具体代码这里。因为这个任务作者只提取主客体,所以每一个label的长度为4。
前两个为客体的开始和结束,后两个为主体的开始和结束。
所在的index对应句子的位置。
具体代码这里。
比如:
1 | label = [ |
看到这里,基本就明白主体思路和触发词提取是一样的。
2.相对位置编码
这个是指引入了一个新的feature,这个feature是以trigger位置来算前面和后面的位置编码。比如trigger为(32,33),那(32,33)的位置编码为0,左右两边递增。
比如:
1 | distince_feature = [3,2,1,0,0,1,2,3] |
我对这一步的做法能带来多大的提升保留质疑。
所以此处就不细讲了。以后有机会试试效果。
3. conditional layer norm
这个作者的思想来自苏剑林的CondiationalLayerNorm,但是我没找到它的源码。
** 作用:**
作者这里采用的是利用Conditional Layer Normalization来将外部条件和bert output做了一次注意力。
** 具体流程:**
1、通过trigger index获取对应的bert output(看batch_gather函数),这里假设叫做trigger feature。
2、接着将trigger feature和bert output通过conditional layer norm进行融合。
** conditional layer norm流程:**
1、对bert output做layer norm,这一步没什么可说的。
2、将trigger feature经过weight linear和bias linear,这个做法其实和正常的layer norm指定elementwise_affine做法是类似的,正常的layer norm做归一化没有训练参数。
3、和bert_output进行相乘。这个地方可以理解成trigger和其他词做了一个注意力。
** 举例说明 **
在本次任务里,触发词
的长度都为2
,所以self.weight_dense
和self.bias_dense
都是normalized_shape * 2
。batch_gather后拿到的触发词shape为(32,2,768)
,这步叫做trigger feature。然后进行reshape,变成了(32, 1, 1536)
,经过self.weight_dense
和self.bias_dense
变换后变成了(32, 1, 768)
,随后和bert_output进行相乘,即和每个字做了一个注意力。
作者代码如下:
1 |
|
作者说添加了这个layer后效果有小幅度提升,以后可以试试。
如果说有很大创新的吧,算不上,我觉得把bert output不做layer norm最终效果也大差不差。
** 备注: **
这里有个问题,如果触发词的长度是变长的话,怎么用呢?估计引入一个mask,算出来。
以后试试。
4. 多feature layer norm
这地方作者代码是在使用了trigger相对位置编码后和bert output进行concat到一起时用到的,如下所示。
1 |
|
这里的做法觉得是平时没注意到的一个点~
平时俩向量直接concat到一起完事。这里还进行了一个layer norm。算是一个挺细心的点。至于能不能带来效果提升,此处就不特别注意啦。
5. 计算loss
1 | if labels is not None: |
这里没什么特别需要注意的地方了,这里和trigger的做法类似,只是这里分成了两个loss,一个是subject loss,一个是object loss。
6. 解码
这部分没细看了,猜测和trigger解码应该也是类似的。
7. 备注
关于conditional layer norm
1 | # -*- coding: utf8 -*- |