XF-Event-Extraction学习笔记

讯飞事件抽取挑战赛

感觉这个pipeline方案核心在于流程和数据处理方面,所以可借鉴的点在于流程和各种trick上

image.png

流程大概就是下面这样:

image.png

也就是说先去提取触发词提取(TriggerExtractor),然后训练论元提取(Role1ExtractorRole2Extractor),最后是属性分类(AttributionClassifier),训练顺序也就是这个了。

1. 触发词提取(TriggerExtractor)

输入数据处理部分
  • 数据集标注的trigger:

    1. 输入的文本按字位置转为[0, 0]初始化,如:[[0, 0], ..., [0, 0], ..., [0, 0]]
    2. 对于输入的trigger文本,根据其在原文中的位置,对start和end位置的label进行改变,如:[[0, 0], ..., [0, 0], [1, 0], [0, 1], [0, 0], ..., [0, 0]]
  • 远程监督标注的trigger

    1. 按字位置构建distant_trigger_label,如:[0, 0, ..., 0]
    2. 遍历远程监督的trigger文本,找到start和end的index,将distant_trigger_label对应index位置的值变为1

至此,构建完成feature:

image.png
模型结构
  1. 输入的文本过bert得到句子表示
  2. 如果选择用远程监督的特征,则通过self.distant_trigger_embedding(distant_trigger)获取特征后(其实就是0或1的意思),通过torch.cat([seq_out, distant_trigger_feature], dim=-1)与bert的输出拼在一起
  3. 接一层dnn
  4. 输出每个字位置的二分类,这里用的sigmoid,其实就是想-> [0, 0] or [0, 1] (end_idx) or [1, 0] (start_idx)

所以说整体还是按照序列标注的思路来完成的trigger抽取任务,这里的远程监督的信息也是直接并入到每个字位置,从而对最终的输出产生影响。

image.png
# 特征,用一个embedding层,这样可以在训练中优化对特征的表示
if use_distant_trigger:
    embedding_dim = kwargs.pop('embedding_dims', 256)
    self.distant_trigger_embedding = nn.Embedding(num_embeddings=2, embedding_dim=embedding_dim)
    out_dims += embedding_dim

mid_linear_dims = kwargs.pop('mid_linear_dims', 128)

# 一个dnn
self.mid_linear = nn.Sequential(
    nn.Linear(out_dims, mid_linear_dims),
    nn.ReLU(),
    nn.Dropout(dropout_prob)
)

self.classifier = nn.Linear(mid_linear_dims, 2)

self.activation = nn.Sigmoid()

self.criterion = nn.BCELoss()

init_blocks = [self.mid_linear, self.classifier]

if use_distant_trigger:
    init_blocks += [self.distant_trigger_embedding]

self._init_weights(init_blocks, initializer_range=self.bert_config.initializer_range)

定义下forward

# 获取bert输出的句子表示
bert_outputs = self.bert_module(
    input_ids=token_ids,
    attention_mask=attention_masks,
    token_type_ids=token_type_ids
)
seq_out = bert_outputs[0]

# 特征embedding
if self.use_distant_trigger:
    assert distant_trigger is not None, \
        'When using distant trigger features, distant trigger should be implemented'

    distant_trigger_feature = self.distant_trigger_embedding(distant_trigger)
    # distant_trigger_feature shape: torch.Size([4, 256, 256])
    seq_out = torch.cat([seq_out, distant_trigger_feature], dim=-1)
    # seq_out.shape torch.Size([4, 256, 1024])

# 对(可能)结合特征的表示过一层dnn
seq_out = self.mid_linear(seq_out)
# seq_out : torch.Size([4, 256, 128])

# 这里用的是Sigmoid
# logits shape: torch.Size([4, 256, 2])
logits = self.activation(self.classifier(seq_out))

out = (logits,)

if labels is not None:
    loss = self.criterion(logits, labels.float())
    out = (loss,) + out

2. 论元提取(Role1ExtractorRole2Extractor)

这里根据项目,将subject与object并为一组、将time与loc并为一组分别处理。

输入部分
image.png
模型结构
  1. 文本输入bert获取句子表示

  2. 根据trigger的索引,获取其bert表示(start-idx和end-idx,因此是768*2=1536)

  3. 将句子的表示与上文trigger的表示根据conditional_layer_norm组合,获取处理后的表示

  4. 如果选择trigger距离的特征,则通过self.trigger_distance_embedding(trigger_distance)获取特征,这里nn.Embedding(num_embeddings=512, embedding_dim=256),我认为num_embeddings选择512是因为bert支持最长为512,因此类似于位置embedding直接支持最大的长度(待测试:如果这个长度小于句子最大长度?测试后:会报错)

  5. concat上面第3的表示和第4的特征

  6. 对于subject和object,通过sigmoid激活并logits = torch.cat([obj_logits, sub_logits], dim=-1),其实这里逻辑与上面相似:

    • subject
      • [0 0 1 0]->start index
      • [0 0 0 1]->end index
    • object
      • [1 0 0 0]->start index
      • [0 1 0 0]->end index

模型结构如下,我认为有一个地方箭头画错了

image.png
# seq_out.shape:  torch.Size([4, 256, 768])
# pooled_out.shape:  torch.Size([4, 768])
seq_out, pooled_out = bert_outputs[0], bert_outputs[1]

# trigger_label_feature.shape:  torch.Size([4, 2, 768])
# 这里是将对应位置的向量拿出来作为特征
# 其实这里面有个小问题,在比赛的数据中,每个句子只有一个trigger,如果是多个trigger这里需要修改
trigger_label_feature = self._batch_gather(seq_out, trigger_index)

# trigger_label_feature.shape:  torch.Size([4, 1536])
trigger_label_feature = trigger_label_feature.view([trigger_label_feature.size()[0], -1])

# seq_out.shape:  torch.Size([4, 256, 768])
seq_out = self.conditional_layer_norm(seq_out, trigger_label_feature)

if self.use_trigger_distance:
    assert trigger_distance is not None, \
        'When using trigger distance features, trigger distance should be implemented'
    # trigger_distance_feature.shape:  torch.Size([4, 256, 256])
    trigger_distance_feature = self.trigger_distance_embedding(trigger_distance)

    # seq_out.shape:  torch.Size([4, 256, 1024])
    seq_out = torch.cat([seq_out, trigger_distance_feature], dim=-1)

    # seq_out.shape:  torch.Size([4, 256, 1024])
    seq_out = self.layer_norm(seq_out)

    # seq_out = self.dropout_layer(seq_out)
# seq_out.shape:  torch.Size([4, 256, 128])
seq_out = self.mid_linear(seq_out)
print("5. seq_out.shape: ", seq_out.shape)

obj_logits = self.activation(self.obj_classifier(seq_out))
sub_logits = self.activation(self.sub_classifier(seq_out))

# torch.Size([4, 256, 4])
logits = torch.cat([obj_logits, sub_logits], dim=-1)
print("logits.shape: ", logits.shape)
out = (logits,)

if labels is not None:
    masks = torch.unsqueeze(attention_masks, -1)

    labels = labels.float()
    obj_loss = self.criterion(obj_logits * masks, labels[:, :, :2])
    sub_loss = self.criterion(sub_logits * masks, labels[:, :, 2:])

    loss = obj_loss + sub_loss

    out = (loss,) + out

return out

对于Role2Extractor,作者提供了CRF的方式:

image.png

3. 属性分类(AttributionClassifier)

这个代码好像有个bug,待排查

image.png
trigger_index:  tensor([[119, 120]], device='cuda:6')
labels:  tensor([[0, 0]], device='cuda:6')

其中,label的构成为:labels = [tense2id[raw_label[0]], polarity2id[raw_label[1]]],这里

  • tense: "map": { "过去": 0, "将来": 1, "其他": 2, "现在": 3 }
  • polarity2id: "map": { "肯定": 0, "可能": 1, "否定": 2 }

pooling_mask的构成为:

# 左右各取 20 的窗口作为 trigger 触发的语境
pooling_masks_range = range(max(1, trigger_loc[0] - window_size),
                            min(min(1 + len(raw_text), max_seq_len - 1), trigger_loc[1] + window_size))

pooling_masks = [0] * max_seq_len
for i in pooling_masks_range:
    pooling_masks[i] = 1
for i in range(trigger_loc[0], trigger_loc[1] + 1):
    pooling_masks[i] = 0

假设trigger_loc[119, 120],那么得到pooling_mask为:tensor([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,...,0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 0., 0., 1., 1., 1., 1., 1., 1., 1., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0...

因此

  1. 通过bert获取句子表示
  2. 通过self._batch_gather(seq_out, trigger_index)获取trigger的embedding向量作为trigger_label_feature
  3. (接1)通过viewtranspose后得到shape为(bs, hidden, seq_len)的句子表示向量
  4. (接3)然后对该向量用nn.AdaptiveMaxPool1d进行池化得到pooled_out
  5. 通过torch.cat([pooled_out, trigger_label_feature], dim=-1)获取logits
  6. 分别获得polarity_logitstense_logits,分别做softmax
  7. 分别计算loss后将两个loss相加
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容