受限前缀注意机制的事件关系识别
✨✨ 欢迎大家来访Srlua的博文(づ ̄3 ̄)づ╭❤~✨✨🌟🌟 欢迎各位亲爱的读者,感谢你们抽出宝贵的时间来阅读我的文章。
我是Srlua小谢,在这里我会分享我的知识和经验。🎥
希望在这里,我们能一起探索IT世界的奥妙,提升我们的技能。🔮
记得先点赞👍后阅读哦~ 👏👏
📘📚 所属专栏:传知代码论文复现
欢迎访问我的主页:Srlua小谢 获取更多信息和资源。✨✨🌙🌙
目录
1. 论文概述
2. 论文方法
2.1 生成模板创建
2.2 使用CPATT的提示调优
2.3 推理
3. 实验
3.1 配置环境
3.2 训练
3.3 推理
核心代码
本文所有资源均可在该地址处获取。
1. 论文概述
事件关系识别(Event Relation Identification,ERI)旨在挖掘事件句子中表达的事件间依赖关系。此任务的主要挑战在于识别那些没有明确指示关系的上下文词汇的隐含线索。当训练样本不足时,主流技术难以有效捕捉事件之间的微妙关系,因为神经网络的参数不能得到充分拟合。尽管有越来越多的方法尝试使用提示学习(prompt learning)来缓解这一问题,但现有方法在提示和提示调优过程中缺乏优化,导致共现干扰和提示歧义。为此,本文提出了一种受限前缀注意机制(Constrained Prefix ATTention,CPATT),并将其整合到传统的提示调优过程中。通过这种方式,将上下文语义特征整合到动态提示中,以减轻共现干扰。此外,CPATT通过将类别之间的互斥性引入损失函数来监督提示的指导效果。基于两个广泛使用的数据集的实验结果表明,在句内和句间事件关系识别任务中均优于所有最先进的基线,包括GPT-3.5-turbo。
本文在原来代码的基础上添加了Bart模型“Bart-base”,不用访问外网就可以。
2. 论文方法
通过将ERI任务转换为自然语言生成任务,使用生成性预训练语言模型(PLM)来解决该任务。提示学习框架的架构如图所示,包含三个主要模块:模板创建模块(2.1)、提示调优模块(2.2)和关系推理模块(2.3)。基于此框架,我们在提示调优模块中整合了受限前缀注意机制(CPATT)。
如图所示,CPATT机制将类别独有的偏置分配为不同前缀的相对位置,并在生成过程中增强正确前缀的作用。
2.1 生成模板创建
为了显式获得关系线索,生成模板应将关系特定的词汇放置在事件之间。对于图3中的例子,事件关系三元组(点燃,火灾,因果关系)被转换为一句话“点燃是火灾的原因”,这明显指示了点燃和火灾之间的因果关系。形式化地定义模板和相应的填空规则如下:
?+ ??,?? = (??)是(??)的原因
?− ??,?? = (??)与(??)无关
其中,??和??分别表示两个输入事件。注意,我们使用上标“+”和“-”来区分因果相关和不相关的事件。
2.2 使用CPATT的提示调优
在此模块中,使用生成性预训练语言模型BART-base通过合成样本(???, ???)进行微调。BART-base符合Transformer结构,利用自注意机制增强上下文意识,并应用交叉注意机制连接编码器和解码器层。为了捕捉上下文语义特征,设计了一种前缀偏置的全连接注意机制,该机制将类别敏感的注意偏置作为相对位置表示融入编码器的自注意机制中。此外,还考虑了前缀对生成文本的指导效果,并采用对比损失函数来提高前缀的正确指导性。
方法的关键在于融合上下文特征以生成动态提示。将???输入到编码器层,以获得隐藏表示???。在BART-base中,注意头的数量为12,隐藏层维度为768。
特别地,用前缀偏置的全连接注意机制升级了自注意机制。注意机制遵循传统的查询-键-值注意机制。对于每个输入词?,使用??,??,??产生其查询、键和值。在计算前缀与事件上下文之间的相似性时,引入了可训练的注意偏置??,将上下文特征融合到前缀表示中。
2.3 推理
在推理阶段,引入模板解析算法来累积揭示最终结果的关系特定分数。
具体地,给定一个候选事件对(??, ??)及其对应的句子(??, ??),我们首先根据公式(8)构建???。其次,生成两句关系特定的句子?+ ??,?? 和?− ??,?? ,分别表示因果关系和无关关系的目标序列。最后一步,将它们输入训练好的BART,并通过选择不同关系的最大分数来确定是否存在因果关系。
作者的方法可以无缝适应多种事件关系识别任务,只需设计简单而有效的模板。通过引入类别敏感的前缀及其偏置,可以有效处理多事件关系识别任务。最终,我们可以将每个特定事件关系?替换到公式(20)中,并将具有最大分数的视为最终结果。
3. 实验
3.1 配置环境
requirments
attrdict==2.0.1
PyYAML==6.0pi
transformers==4.29.0
numpy
pandas
scikit_learn
torch
tqdm
3.2 训练
train.py
3.3 推理
inference.py
核心代码
def predict_relation(input_TXT, event1, event2): # 预测一个句子中两个事件的关系
input_TXT = [input_TXT]*2
input_ids = tokenizer(input_TXT, return_tensors='pt')['input_ids']
model.to(device)
relation_dict = {0: 'Cause-Effect', 1: 'NONE'}
temp_list = []
temp_list.append(event1+"is the cause of "+event2)
temp_list.append(event1+"has no relation to "+event2)
output_ids = tokenizer(temp_list, return_tensors='pt',
padding=True, truncation=True)['input_ids']
# 加一个unused字符
output_ids[:, 0] = 2
output_length_list = [0]*2
base_length = ((tokenizer(temp_list[0], return_tensors='pt', padding=True, truncation=True)[
'input_ids']).shape)[1]-2
output_length_list[0:1] = [base_length]*2
score = [1]*2
with torch.no_grad():
outputs = model(input_ids=input_ids.to(device), decoder_input_ids=output_ids[:, :output_ids.shape[1] - 2].to(device))
output = outputs[0]
# print(tokenizer.decode(output_ids[1, :output_ids.shape[1] - 2]))
for i in range(output_ids.shape[1] - 3):
# output = model(input_ids=input_ids.to(device), decoder_input_ids=output_ids.to(device))[0]
logits = output[:, i, :]
logits = logits.softmax(dim=1)
logits = logits.to('cpu').numpy()
# for j in range(0, 2):
# if int(output_ids[j][i + 1]) not in [16, 34, 5, 117, 1303, 9355, 9, 7]:
# weight = 1
# else:
# weight = 1.5
# if i < output_length_list[j]:
# score[j] = score[j] * (logits[j][int(output_ids[j][i + 1])] ** weight)
for j in range(0, 2):
if i < output_length_list[j]:
score[j] = score[j] * logits[j][int(output_ids[j][i + 1])]
# print(temp_list[0],score[0])
# print(temp_list[1],score[1])
# print(relation_dict[(score.index(max(score)))])
return relation_dict[(score.index(max(score)))]