引言
这是第三篇文章,和第二篇本质没有顺序之分,上一篇是讯飞2020年事件提取比赛第一名-主客体提取。
1. 跑通代码
只需要改动task_type,如下:
1 | args = TrainArgs().get_parser() |
2. 模型结构
3. 模型结构
1 | AttributionClassifier( |
4. 整体流程
根据挑战可知事件属性抽取分成两部分:
- 极性分为:肯定、否定、可能;
- 时态分为:过去、现在、将来、其他。
这俩分别对应tense_classifier
和polarity_classifier
。
1. 输入句子,拿到bert output
2、做动态池化
在属性分类优化
ppt那页,作者认为:
1 | 能决定事件属性的词大多存在触发词左右,故舍弃CLS中的全局特征,采用trigger左右两端动态池化特征作为全局特征; |
具体做法即以触发词所在位置为准,设置window_size=20,只考虑这个范围内的bert output。
示例:
1 | # 假设爱是trigger word。 |
3、获取window_size内最大的feature
1 |
|
4、和trigger feature进行concat
1 | logits = torch.cat([pooled_out, trigger_label_feature], dim=-1) |
5、整体forward代码
1 | bert_outputs = self.bert_module( |
5、值得注意的点
核心在于作者的这句话:
1 | 能决定事件属性的词大多存在触发词左右,故舍弃CLS中的全局特征,采用trigger左右两端动态池化特征作为全局特征; |
比如我们可以这么做,直接拿[CLS]位置,然后输入两个linear(一个极性,一个时态),分别拿到各自loss,就ok了。
但是作者这里没有拿[CLS]来代表整个句子,因为作者认为能决定事件属性的词大多存在触发词左右
。所以他这里采用了以trigger左右window_size=20来缩减范围。
从个人角度来讲,他这种方式第一能加快速度,第二是可能会有更好的泛化效果。
因为打比赛这种东西,恨不得啥奇淫巧技都上。但是这种思想可以借鉴。
备忘
关于AdaptiveMaxPool1d
的用法
1 | a = torch.arange(24, dtype=torch.float32).view(2,3,4) |