信息抽取(四)【NLP论文复现】Multi-head Selection和Deep Biaffine Attention在关系抽取中的实现和效果

2023-11-18


前言

最近在磕一人之力,刷爆三路榜单!信息抽取竞赛夺冠经验分享这篇文章,文章对关系抽取任务的解码方式做了简单的概述,在之前的文章中本人已经实现了指针标注网络,并对其进了优化(详情见改良后的层叠式指针网络,让我的模型F1提升近4%),因此这次把文中提到的多头选择和Biaffine关系矩阵构建的原论文拿出来研究了一下,并根据实际的关系抽取任务做了复现和改良。

本文主要涉及以下论文:

多头选择与关系抽取:
Joint entity recognition and relation extraction as a multi-head selection problem

Bert版多头选择:
BERT-Based Multi-Head Selection for Joint Entity-Relation Extraction

双仿射注意力机制:
Deep Biaffine Attention for Neural Dependency Parsing(基于深层双仿射注意力的神经网络依存解析)

新论文,采取Biaffine机制构造Span矩阵:
Named Entity Recognition as Dependency Parsing

本文只是记录自己的思考和实现,只对每篇论文核心部分做简单理解,可能有错误,感兴趣的同学建议直接看原文。


Multi-head Selection

一、Joint entity recognition and relation extraction as a multi-head selection problem

该网络结构将实体识别和关系抽取 joint 在一起,先通过在隐藏层上连接CRF,来抽取实体标签,并将实体标签信息embedding后与隐藏层一起传递给sigmoid_layer,来与其他实体特征进行交互抽取关系。
实体抽取部分比较好理解,对于多头选择部分,以下原文的几条核心公式:

理解如下:
当Token(j)为Subject的End 且 Token(i)为Object的End的概率分数为

非常简明的意思是:将Token(j)和Token(i)的Z(隐藏层➕label embedding)分别经过U,W线性变换后相加再加上偏置b,最后再进行一次整体线性变换V,得到的值经过sigmoid函数后即转换为对应的概率分数。
别急!后文会讨论多类别关系时,各个矩阵的维度关系。

二、BERT-Based Multi-Head Selection for Joint Entity-Relation Extraction

Bert + Multi-head Selection,除了引入了Bert作为编码层外,其他创新点如下:

  1. 考虑在训练时我们可以通过传入真实的实体标签的embedding给Multi-head Selection层,但在模型推理时,为了利用CRF的softmax在各个标签上产生的分值信息和考虑到推理时可能产生的错误标签结果,作者将softmax结果与各个标签的embedding进行加权后传给Multi-head Selection层。
  1. 引入句子级关系分类任务来指导特征学习,如图中的用CLS来获得稳定的维度特征。(关于这一改进我并没有进行尝试,还不是因为没有数据!因此持有怀疑态度,原本就将两个任务的解码压力放在一组encoder上,现在又增加了句子分类任务。这不会加大模型压力吗?文中给出的实验结果表明,单独增加Global predicate Prediction并没有带来明显的提升,而组合各种策略能带来的较高提升不一定是该方法的贡献。)
  1. Soft label embedding of the ith token hi is feed into two separate fully connected layers to get the subject representation hsi and object representation hoi. where f(·) means neural network, ri,j is the relation when the ith token is subject and the jth token is object, rj,i is the relation when the jth token is subject and the ith token is object.(论文原文,多头选择方法与上文相同,将构建好的token特征通过两个不同的全连接层后经过一个F网络输出两者的关系分值)

三、实现方法和模型代码展示

以三元关系抽取任务为例,我们多头选择该如何更好的理解和应用?功夫不负有心人我找大了夕大姐文章里一张图虽然和多头选择没多少关系,但能比较形象的展示Multi-head Selection的流程:

对于一个句子中的所有的token形成的SO组合,一共有N2种。假设我们的Token都已经经过了线性变化或者全连接层的洗礼,如图中对于每一个City,我们将其作为S,我们应该考虑其他所有token是否能和它形成SO关系,所以我们要计算每一个token和city经过V变换后的分数。

具体实现上也非常简单,我们可以构建一个NNHidden_size的token组合矩阵,经过一个P_num(关系类别总数)的Dense层后即可得到各个token之间在各个关系上的分值。

Subject = tf.keras.layers.Dense(hidden_size)(Encode)
Object = tf.keras.layers.Dense(hidden_size)(Encode)
'''
将原token的encode经过两个不同的全连接层后,得到Subject,Object两个token序列
对应公式中的 U*zj 和 W*zi
'''
Subject = tf.expand_dims(Subject,1)
#TensorShape([batch_size, 1, MAX_LEN, hidden_size])
Object = tf.expand_dims(Object,2)
#TensorShape([batch_size, MAX_LEN, 1, hidden_size])
Subject = tf.tile(Subject,multiples=(1,MAX_LEN,1,1))
#TensorShape([batch_size, MAX_LEN, MAX_LEN, hidden_size])
Object = tf.tile(Object,multiples=(1,1,MAX_LEN,1))
#TensorShape([batch_size, MAX_LEN, MAX_LEN, hidden_size])
concat_SO = tf.keras.layers.Concatenate(axis=-1)([Subject,Object])
#TensorShape([batch_size, MAX_LEN, MAX_LEN, 2*hidden_size])
output_logist = tf.keras.layers.Dense(P_num,activation='sigmoid')(concat_SO)
#TensorShape([batch_size, MAX_LEN, MAX_LEN, P_num])
'''
将组合后的 U*zj 与 W*zi 经过一个V全连接层,V.shape = (2*hidden_size,P_num)
对应公式中的 V*U*zj + V*W*zi = V(U*zj + W*zj + b)
'''

样本构建部分: 我们需要将一个标注好的[ MAX_LEN, MAX_LEN, P_num ]的矩阵作为Multi-Head Selection 结果的 Y值。

关于样本标注问题:

  1. 一个实体包含多个字符且可能存在实体嵌套的问题,如“我是歌手的主演是阿瓜”,需要抽取的关系为“我是歌手的主演是阿瓜”、“阿瓜是歌手”。

  2. 我们并不需要将“我是歌手”的四个token和“阿瓜”的两个token在P=主演的得分上全都标注为1,因为在实体抽取部分我们对实体的头和尾进行了识别,我们仅需要将 S“手”和O“瓜”所对应的P=主演的分值标注为1即可。本例中即 [3,9,indenx of 主演] = 1即可。如果阿瓜还是个歌手,则 [9,3,indenx of 职业] = 1即可,因为对于不同类型的嵌套的实体,可能存在尾字符相同的情况极少,因此我们标注尾字符而不是首字符。

以上就是Multi-head Selection Model部分的核心思路和代码。

完整模型代码如下:

  1. 用bert代替了原文的LSTM编码层。
  2. 这里将CRF替换为指针标注,并引入了实体的类别信息。
  3. 将实体的硬标签与实体的end_token拼接后传入Multi-head Selection层,这也是本人灵光一闪的部分,既然在Multi-head Selection层我们希望model能识别S的end_token和O的end_token, 我们就只给这两个token传入有效的实体标签信息,其余token类别都编码为0即可,实验正面这确实比你对所有的SOtoken都传入对应的实体类别embedding效果更好。
  4. 没有使用上文的软标签,软标签的具体实现可以通过自定义 layer 实现。
def build_model(pretrained_path,config,MAX_LEN,Cs_num,cs_em_size,R_num):
    ids = tf.keras.layers.Input((MAX_LEN,), dtype=tf.int32)
    att = tf.keras.layers.Input((MAX_LEN,), dtype=tf.int32)
    cs = tf.keras.layers.Input((MAX_LEN,), dtype=tf.int32)
    
    config.output_hidden_states = True
    bert_model = TFBertModel.from_pretrained(pretrained_path,config=config,from_pt=True)
    x, _, hidden_states = bert_model(ids,attention_mask=att)
    layer_1 = hidden_states[-1]
    
    start_logits = tf.keras.layers.Dense(Cs_num,activation = 'sigmoid')(layer_1)
    start_logits = tf.keras.layers.Lambda(lambda x: x**2,name='s_start')(start_logits)
    
    end_logits = tf.keras.layers.Dense(Cs_num,activation = 'sigmoid')(layer_1)
    end_logits = tf.keras.layers.Lambda(lambda x: x**2,name='s_end')(end_logits)
    
    cs_emb = tf.keras.layers.Embedding(Cs_num,cs_em_size)(cs)
    concat_cs = tf.keras.layers.Concatenate(axis=-1)([layer_1,cs_emb])
    
    f1 = tf.keras.layers.Dense(128)(concat_cs)
    f2 = tf.keras.layers.Dense(128)(concat_cs)
    
    f1 = tf.expand_dims(f1,1)
    f2 = tf.expand_dims(f2,2)

    f1 = tf.tile(f1,multiples=(1,MAX_LEN,1,1))
    f2 = tf.tile(f2,multiples=(1,1,MAX_LEN,1))
    
    concat_f = tf.keras.layers.Concatenate(axis=-1)([f1,f2])
    output_logist = tf.keras.layers.Dense(128,activation='relu')(concat_f)
    output_logist = tf.keras.layers.Dense(R_num,activation='sigmoid')(output_logist)
    output_logist = tf.keras.layers.Lambda(lambda x: x**4,name='relation')(output_logist)

    model = tf.keras.models.Model(inputs=[ids,att,cs], outputs=[start_logits,end_logits,output_logist])
    model_2 = tf.keras.models.Model(inputs=[ids,att], outputs=[start_logits,end_logits])
    model_3 = tf.keras.models.Model(inputs=[ids,att,cs], outputs=[output_logist])
    return model,model_2,model_3

模型示意图之丑陋手稿:看个乐哈~:

模型效果:
没有做其他任何的处理,在2019百度三元抽取数据集上F1就达到了 79.157,而且模型结构相比于层叠序列标注简单了许多,理解起来也更加到胃~


Deep Biaffine Attention

一、Deep Biaffine Attention for Neural Dependency Parsing

这篇文章主要通过Biaffine来应用于依存关系分析上,但这也正好和关系抽取共通,只是依存关系中的关系类别只有一种,而在关系抽取中存在多种关系分类。

文章使用了双仿射注意力机制,而不是使用传统基于MLP注意力机制的单仿射分类器,或双线性分类器;上文提到的Multi-Head Selection正是由多个线性分类器构成的关系分类器。而现在我们希望能通过构建一个Biaffine Attention矩阵直接计算各个token之间在某个关系分类上的attention。

(这里直接将关系依存中的head 和 dep 称为 关系抽取中的S和O

  1. 将BiLSTM编码的token hidden 经过两个MLP 得到 S 和 O的特征表示;
  2. 对于这一步文章中特地提到:“Applying smaller MLPs to the recurrent output states before the biaffine classifier has the advantage of stripping away information not relevant to the current decision. That is, every top recurrent state ri will need to carry enough information to identify word i’s head, find all its dependents, exclude all its non-dependents, assign itself the correct label, and assign all its dependents their correct labels, as well as transfer any relevant information to the recurrent states of words before and after it. Thus ri necessarily contains significantly more information than is needed to compute any individual score, and training on this superfluous information needlessly reduces parsing speed and increases the risk of overfitting. Reducing dimensionality and applying a nonlinearity addresses both of these problems.”
  3. LSTM层的输出状态需要携带足够的信息,如识别其头结点,找到其依赖项,排除非依赖项,分配自身及其所有依赖的依存标签,而且还需要把其它任何相关信息传递至前或后单元。对这些不必要的信息进行训练会降低训练速度,而且还有过拟合的风险。使用MLP对LSTM输出降维,并使用双仿射变换,可解决这一问题!
  4. 简单来说我们希望能通过将原本高维度富含丰富信息的 hidden state 通过MLP降为至只能容下关系依赖信息的低纬度的特征,一方面加速训练,另一方面可以抑制过拟合。
  1. 最终我通过构建一个U(Biaffine)矩阵来计算各个token之间依存的分值,并引入u矩阵来计算head的先验概率并产生偏置b。我们设token的长度为d,经过MLP压缩后的hidden_size为k,以下是我丑陋手稿来解释矩阵的乘法维度变化。

Biaffine实现代码:

class Biaffine(tf.keras.layers.Layer):
    def __init__(self, in_size, out_size, bias_x=False, bias_y=False):
        super(Biaffine, self).__init__()
        self.bias_x = bias_x
        self.bias_y = bias_y
        self.U = self.add_weight(
            name='weight1', shape=(in_size + int(bias_x), 
            out_size, in_size + int(bias_y)),trainable=True)
        #U.shape = [in_size,out_size,in_size]
    def call(self, input1, input2):
        if self.bias_x:
            input1 = tf.concat((input1, tf.ones_like(input1[..., :1])), axis=-1)
        if self.bias_y:
            input2 = tf.concat((input2, tf.ones_like(input2[..., :1])), axis=-1)
        # bxi,oij,byj->boxy
        logits_1 = tf.einsum('bxi,ioj,byj->bxyo', input1, self.U, input2)
        return logits_1

完整模型代码:

def build_model(pretrained_path,config,MAX_LEN,Cs_num,cs_em_size,R_num):
    ids = tf.keras.layers.Input((MAX_LEN,), dtype=tf.int32)
    att = tf.keras.layers.Input((MAX_LEN,), dtype=tf.int32)
    cs = tf.keras.layers.Input((MAX_LEN,), dtype=tf.int32)
    
    config.output_hidden_states = True
    bert_model = TFBertModel.from_pretrained(pretrained_path,config=config,from_pt=True)
    x, _, hidden_states = bert_model(ids,attention_mask=att)
    layer_1 = hidden_states[-1]
    
    start_logits = tf.keras.layers.Dense(Cs_num,activation = 'sigmoid')(layer_1)
    start_logits = tf.keras.layers.Lambda(lambda x: x**2,name='s_start')(start_logits)
    
    end_logits = tf.keras.layers.Dense(Cs_num,activation = 'sigmoid')(layer_1)
    end_logits = tf.keras.layers.Lambda(lambda x: x**2,name='s_end')(end_logits)
    
    cs_emb = tf.keras.layers.Embedding(Cs_num,cs_em_size)(cs)
    concat_cs = tf.keras.layers.Concatenate(axis=-1)([layer_1,cs_emb])
    
    f1 = tf.keras.layers.Dense(128,activation='relu')(concat_cs)
    f2 = tf.keras.layers.Dense(128,activation='relu')(concat_cs)
    
    Biaffine_layer = Biaffine(128,R_num,bias_y=True)
    output_logist = Biaffine_layer(f1,f2)
    output_logist = tf.keras.layers.Activation('sigmoid')(output_logist)
    output_logist = tf.keras.layers.Lambda(lambda x: x**4,name='relation')(output_logist)
    
    model = tf.keras.models.Model(inputs=[ids,att,cs], outputs=[start_logits,end_logits,output_logist])
    model_2 = tf.keras.models.Model(inputs=[ids,att], outputs=[start_logits,end_logits])
    model_3 = tf.keras.models.Model(inputs=[ids,att,cs], outputs=[output_logist])
    return model,model_2,model_3

模型效果:
F1 = 0.7964 相比Multi-head Selection F1提高了0.05左右

二、Named Entity Recognition as Dependency Parsing

  1. After obtaining the word representations from the BiLSTM, we apply two separate FFNNs to create different representations (hs/he) for the start/end of the spans. Using different representations for the start/end of the spans allow the system to learn to identify the start/end of the spans separately. This improves accuracy compared to the model which directly uses the outputs of the LSTM since the context of the start and end of the entity are different. Finally, we employ a biaffine model over the sentence to create a l×l×c scoring tensor(rm), where l is the length of the sentence and c is the number of NER categories + 1(for non-entity). We compute the score for a span i by:

    where si and ei are the start and end indices of the span i, Um is a d × c × d tensor, Wm is a 2d × c matrix and bm is the bias.
  2. 原文在BERT、fastText & Char Embeddings提取特征的基础上,通过BiLSTM捕获word representations后,同样使用两组全连接层来表示实体的头和尾,这比直接使用encode结果后直接输出实体的头和尾来说更加准确,毕竟两者所表示的信息是不同的。
  3. 之后将这两组特征丢入我们的主角:Biaffine矩阵。这个任务中我们不仅要识别实体的头和尾,还要识别出实体的类别C,因此我们目标是得到一个LLC的结果矩阵(其中L为序列长度,C为实体类别数目)
  4. 重点关注公式,其中Um、Wm、bm的shape及其表示意义如下:
    1. Um:tensor shape of dcd: 对hs(i)为头he(i)为尾的实体类别后验概率建模
    2. Wm:tensor shape of 2d*c: 对hs(i) 或 he(i)为尾的实体类别的后验概率分别建模
    3. bm:tensor shape of c: 对实体类别c的先验概率建模
    4. 对于头为hs(i),尾为he(i),类别为C的实体其概率分值为
    5. P(头为hs(i)&尾为he(i),C) + P(头为hs(i),C) + P(尾为he(i),C) + P( C )
  5. 根据公式我们可以构建新的Biaffine矩阵代码:
class Biaffine_2(tf.keras.layers.Layer):
    def __init__(self, in_size, out_size,MAX_LEN):
        super(Biaffine_2, self).__init__()
        self.w1 = self.add_weight(
            name='weight1', 
            shape=(in_size, out_size, in_size),trainable=True)
        self.w2 = self.add_weight(
            name='weight2', 
            shape=(2*in_size + 1, out_size),trainable=True)
        self.MAX_LEN = MAX_LEN
    def call(self, input1, input2):
        f1 = tf.expand_dims(input1,2)
        f2 = tf.expand_dims(input2,1)
        f1 = tf.tile(f1,multiples=(1,1,self.MAX_LEN,1))
        f2 = tf.tile(f2,multiples=(1,self.MAX_LEN,1,1))
        concat_f1f2 = tf.concat((f1,f2),axis=-1)
        concat_f1f2 = tf.concat((concat_f1f2,tf.ones_like(concat_f1f2[..., :1])), axis=-1)
        logits_1 = tf.einsum('bxi,ioj,byj->bxyo', input1, self.w1, input2)
        logits_2 = tf.einsum('bijy,yo->bijo',concat_f1f2,self.w2)
        return logits_1+logits_2 

尝试1

目前尝试中能拿到最好效果的模型方案(持续更新中):

  1. 实体标签作为比较强的特征,取得S和O的实体标签基本可以判断两者的关系,因此将标签直接引入Biaffine矩阵。
  2. 将BERT最后两层编码进⾏Biaffine计算,得到关系矩阵。
  3. 给实体抽取层增加了一层全连接,对实体抽取和关系抽取两个任务做适当的分离。
def build_model_3(pretrained_path,config,MAX_LEN,Cs_num,cs_em_size,R_num):
    ids = tf.keras.layers.Input((MAX_LEN,), dtype=tf.int32)
    att = tf.keras.layers.Input((MAX_LEN,), dtype=tf.int32)
    cs = tf.keras.layers.Input((MAX_LEN,), dtype=tf.int32)
    
    config.output_hidden_states = True
    bert_model = TFBertModel.from_pretrained(pretrained_path,config=config,from_pt=True)
    x, _, hidden_states = bert_model(ids,attention_mask=att)
    layer_1 = hidden_states[-1]
    layer_2 = hidden_states[-2]
    
    
    start_logits = tf.keras.layers.Dense(256,activation = 'relu')(layer_1)
    start_logits = tf.keras.layers.Dense(Cs_num,activation = 'sigmoid')(start_logits)
    start_logits = tf.keras.layers.Lambda(lambda x: x**2,name='s_start')(start_logits)
    
    end_logits = tf.keras.layers.Dense(256,activation = 'relu')(layer_1)
    end_logits = tf.keras.layers.Dense(Cs_num,activation = 'sigmoid')(end_logits)
    end_logits = tf.keras.layers.Lambda(lambda x: x**2,name='s_end')(end_logits)
    
    cs_emb = tf.keras.layers.Embedding(Cs_num,cs_em_size)(cs)
    
    concat_cs = tf.keras.layers.Concatenate(axis=-1)([layer_1,layer_2])
    
    f1 = tf.keras.layers.Dropout(0.2)(concat_cs)
    f1 = tf.keras.layers.Dense(256,activation='relu')(f1)
    f1 = tf.keras.layers.Dense(128,activation='relu')(f1)
    f1 = tf.keras.layers.Concatenate(axis=-1)([f1,cs_emb])
    
    f2 = tf.keras.layers.Dropout(0.2)(concat_cs)
    f2 = tf.keras.layers.Dense(256,activation='relu')(f2)
    f2 = tf.keras.layers.Dense(128,activation='relu')(f2)
    f2 = tf.keras.layers.Concatenate(axis=-1)([f2,cs_emb])
    
    Biaffine_layer = Biaffine_2(128+cs_em_size,R_num,MAX_LEN)
    output_logist = Biaffine_layer(f1,f2)
    output_logist = tf.keras.layers.Activation('sigmoid')(output_logist)
    output_logist = tf.keras.layers.Lambda(lambda x: x**4,name='relation')(output_logist)
    
    model = tf.keras.models.Model(inputs=[ids,att,cs], outputs=[start_logits,end_logits,output_logist])
    model_2 = tf.keras.models.Model(inputs=[ids,att], outputs=[start_logits,end_logits])
    model_3 = tf.keras.models.Model(inputs=[ids,att,cs], outputs=[output_logist])
    return model,model_2,model_3

F1 = 0.7986 比baseline提高了 0.022

尝试2

  1. 实体抽取部分用Biaffine矩阵代替序列标注,softmax激活后输出
  2. 并用bert后两层隐藏层同时编码实体和关系矩阵
def build_model(pretrained_path,config,MAX_LEN,Cs_num,cs_em_size,R_num):
    ids = tf.keras.layers.Input((MAX_LEN,), dtype=tf.int32)
    att = tf.keras.layers.Input((MAX_LEN,), dtype=tf.int32)
    cs = tf.keras.layers.Input((MAX_LEN,), dtype=tf.int32)
    
    config.output_hidden_states = True
    bert_model = TFBertModel.from_pretrained(pretrained_path,config=config,from_pt=True)
    x, pooling, hidden_states = bert_model(ids,attention_mask=att)
    layer_1 = hidden_states[-1]
    layer_2 = hidden_states[-2]
    
    concat_cs = tf.keras.layers.Concatenate(axis=-1)([layer_1,layer_2])
    
    start_logits = tf.keras.layers.Dense(128,activation = 'relu')(concat_cs) 
    end_logits = tf.keras.layers.Dense(128,activation = 'relu')(concat_cs)
    
    S_Biaffine_layer = Biaffine_2(128,Cs_num,MAX_LEN)
    S_logits = S_Biaffine_layer(start_logits,end_logits)
    
    S_output_logist = tf.keras.layers.Activation('softmax',name='s_token')(S_logits)
    
    S_logits = tf.keras.layers.Dense(256,activation = 'relu')(concat_cs)
    S_logits = tf.keras.layers.Dropout(0.2)(S_logits)
    S_logits = tf.keras.layers.Dense(128,activation = 'relu')(S_logits)
    
    O_logits = tf.keras.layers.Dense(256,activation = 'relu')(concat_cs)
    O_logits = tf.keras.layers.Dropout(0.2)(O_logits)
    O_logits = tf.keras.layers.Dense(128,activation = 'relu')(O_logits)

    cs_emb = tf.keras.layers.Embedding(Cs_num,cs_em_size)(cs)

    f1 = tf.keras.layers.Concatenate(axis=-1)([S_logits,cs_emb])
    f2 = tf.keras.layers.Concatenate(axis=-1)([O_logits,cs_emb])
    
    Biaffine_layer = Biaffine_2(128+cs_em_size,R_num,MAX_LEN)
    output_logist = Biaffine_layer(f1,f2)

    output_logist = tf.keras.layers.Activation('sigmoid')(output_logist)
    output_logist = tf.keras.layers.Lambda(lambda x: x**4,name='relation')(output_logist)
    
    model = tf.keras.models.Model(inputs=[ids,att,cs], outputs=[S_output_logist,output_logist])
    model_2 = tf.keras.models.Model(inputs=[ids,att], outputs=[S_output_logist])
    model_3 = tf.keras.models.Model(inputs=[ids,att,cs], outputs=[output_logist])
    return model,model_2,model_3

F1值:0.8016


Biaffine 和 Multi-head对比

  1. 同样的计算开销:N^2
  2. 更多的参数:Baffine attention 矩阵拥有更多的参数,且相比于Muti-head Selection 能捕捉到S和O特征之间的交叉关系,而Muti-head Selection则是通过简单的MLP线性变化进行组合。丑陋手稿如下:
  3. 加入了start和end单独的先验概率
  4. 各模型结果对比

    biaffine + biaffine 8 0.8016

总结

  1. 在关系抽取的任务中,baseline选择 Biaffine 会优于 基本的Multi-head,在这个基础上模型还有很多可以优化的地方。比如 如何更好的构造Biaffine矩阵,如何产生更有效的信息,但不至于过拟合。在我的实验过程中,Biaffine方法一直无法超越之前改良后的层叠式指针网络,这让我非常郁闷(主要是和别人实验结果不同),排除batch_size太小,模型收敛性较差的原因之外,根据模型的训练情况判断可能原因是在抽取实体时要求模型辨别实体的类别,导致该任务收敛性较差,试了多种网络较难拟合,也可能和数据集相关。使用上还存在一定的问题,会持续关注。

  2. 其实学术界已经对这种共享编码的效果提出了质疑,也有不少实验证明,实体抽取和关系抽取这两个任务在独立编码的情况下效果要好于共享编码。Two are Better than One: Joint Entity and Relation Extraction with Table-Sequence Encoders

  3. 本文并没有尝试用CRF来抽取实体,而是直接使用了指针标注的方法,只是收到了大部分实体抽取文章的影响(默认指针标注优于CRF)之后会对这两种方法进行对比。

  4. 关系抽取的系列文章可能要到此告一段落,之后看到这方面重要的论文也会第一时间和大家分享~

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

信息抽取(四)【NLP论文复现】Multi-head Selection和Deep Biaffine Attention在关系抽取中的实现和效果 的相关文章

  • Python 函数句柄 ala Matlab

    在 MATLAB 中可以创建function handles http www mathworks co uk help techdoc ref function handle html与类似的东西 myfun arglist body 这
  • 根据另一个数据框中找到的范围填充数据框中的列

    我试图根据该记录的索引值是否落在另一个数据框中的两列定义的范围内来填充数据框中的列 df1 看起来像 a 0 4 1 45 2 7 3 5 4 48 5 44 6 22 7 89 8 45 9 44 10 23 df2 是 START ST
  • 如何仅选择数组中的第一列并对其求和?

    这是我的代码 import numpy as np contrainte1 1080 0 65 minutes tous les jours contrainte2 720 0 55 minutes du lundi au vendredi
  • 如何在python中确定过去的时区特定日期是否是夏令时?

    有没有办法检查特定时区在我指定的日期是否处于夏令时 test dt datetime year 2015 month 2 day 1 pst pytz timezone America Los Angeles test dt pst loc
  • 从 Django 基于类的视图的 form_valid 方法调用特殊(非 HTTP)URL

    如果你这样做的话 有一个 HTML 技巧 a href New SMS Message a 点击新短信打开手机的本机短信应用程序并预 先填写To包含所提供号码的字段 在本例中为 1 408 555 1212 以及body与提供的消息 Hel
  • Scrapy Splash,如何处理onclick?

    我正在尝试抓取以下内容 我能够收到响应 但我不知道如何访问以下项目的内部数据以抓取它 我注意到访问这些项目实际上是由 JavaScript 和分页处理的 这种情况我该怎么办 下面是我的代码 import scrapy from scrapy
  • Python:绘制甘特图的模块

    有没有一个好的Python绘图模块甘特图 http en wikipedia org wiki Gantt chart 我试过了开罗情节 http linil wordpress com 2008 09 16 cairoplot 11 但它
  • telethon 库:如何通过电话号码添加用户

    我正在研究 Telegram 的 Telethon 库 它可以使用 Telegram API 充当 Telegram 客户端 重要提示 这是电报客户端 API https core telegram org telegram api 而不是
  • 在 Qt 5 中嵌入 Python

    我想将 Python 解释器嵌入到 Qt 5 应用程序中 我在 Qt 5 中有一个工作应用程序 但是当我把 include
  • 如何在自定义 django 命令中抽象出命令代码

    我正在我的应用程序下编写自定义 django 命令management commands目录 目前我在该目录中有 6 个不同的文件 每个文件都有不同的命令来解决独特的需求 然而 有一些实用程序是它们所共有的 抽象出这些公共代码的最佳方法是什
  • 使用 conda 安装额外功能

    With pip我们可以使用方括号安装子包 例如与阿帕奇气流 https pythonhosted org airflow installation html pip install airflow all 有类似的东西吗conda或者我必
  • Pandas 中的数据透视表小计

    我有以下数据 Employee Account Currency Amount Location Test 2 Basic USD 3000 Airport Test 2 Net USD 2000 Airport Test 1 Basic
  • 获取 python 模块的 2 个独立实例

    我正在与以非 OO 方式编写的 python 2 x API 进行交互 它使用模块全局范围来处理一些内部状态驱动的东西 在它不再是单例的情况下需要它 并且修改原始代码 不是我们的 不是一个选择 如果不使用单独解释器的子进程运行 有什么方法可
  • 如何输入可变的默认参数

    Python 中处理可变默认参数的方法是将它们设置为无 https stackoverflow com a 366430 5049813 例如 def foo bar None bar if bar is None else bar ret
  • Django 按小时过滤

    我找到了那个链接 http code djangoproject com attachment ticket 8424 time filters diff http code djangoproject com attachment tic
  • 重写 PyGObject 中的虚拟方法

    我正在尝试实施高宽几何管理 http developer gnome org gtk3 3 2 GtkWidget html geometry management在 GTK 和 Python 中用于我的自定义小部件 我的小部件是来自的子类
  • “yield item”与 return iter(items) 相比有何优点?

    在下面的示例中 resp results 是一个迭代器 版本1 items for result in resp results item process result items append item return iter items
  • gnuplot:第 1 行:无效命令

    stackoverflow 上可爱的人们大家好 我正在尝试使用 gnuplot 绘制数据 我首先阅读表格并提取我想要的数据 我将此数据写入 dat 文件 截至目前 我只是尝试通过命令行绘制它 但会添加必要的代码以在 python 脚本工作后
  • 如何从 python 中的字符串中删除 ANSI 转义序列

    这是包含我的字符串的片段 ls r n x1b 00m x1b 01 31mexamplefile zip x1b 00m r n x1b 01 31m 该字符串是从我执行的 SSH 命令返回的 我无法使用当前状态下的字符串 因为它包含 A
  • 如何从Python枚举类中获取所有值?

    我正在使用 Enum4 库创建一个枚举类 如下所示 class Color Enum RED 1 BLUE 2 我要打印 1 2 作为某处的列表 我怎样才能实现这个目标 您可以执行以下操作 e value for e in Color

随机推荐

  • Codeforces-1260-E. Tournament贪心

    题目描述 You are organizing a boxing tournament where n boxers will participate n is a power of 2 and your friend is one of
  • 利用celebA数据集训练MTCNN网络

    利用celebA数据集训练MTCNN网络 celebA数据集简介 训练数据的处理 网络和训练 侦测部分 结果展示 有问题可以联系我的邮箱 2487429219 qq com 关于MTCNN网络可以看我上一篇博客 链接 人脸检测算法 mtcn
  • 请用C + +语言写一个生成随机数程序

    include
  • windows系统更改远程桌面端口

    开启远程桌面 我的电脑属性 远程设置 步骤一 在服务器运行窗口中输入 regedit 打开注册表编辑器 步骤二 在注册表HKEY LOCAL MACHINE SYSTEM CurrentControlSet Control Terminal
  • 注解-Annotation Types学习

    前言 注解是JDK5 之后推出的特性 可修饰包 类 方法 变量等 通过在编译 加载和运行时读取其信息 可执行相应的处理 基本原理 java注解类型实质上是一个标记 如 Autowired private TestBean testBean
  • win10家庭中文版开启Hyper-V功能

    问题描述 由于win10家庭中文版是被阉割过的 有些功能就被禁用了 启用和禁用程序中就没有了Hyper V功能 如果想要更多的功能可以将系统升级成为专业版和企业版即可解决 但如果想直接在家庭中文版下创建也是可行 这里有一位大神给出了自己的方
  • LeetCode题解--160. 相交链表

    题目 编写一个程序 找到两个单链表相交的起始节点 例如 下面的两个链表 A a1 a2 c1 c2 c3 B b1 b2 b3 在节点 c1 开始相交 注意 如果两个链表没有交点 返回 null 在返回结果后 两个链表仍须保持原有的结构 可
  • html5中拖放,HTML5中的拖放

    关于HTML5中的拖放 拖放 Drag 和 Drop 是一种常见的特性 即抓取对象以后拖到另一个位置 在 HTML5 中 拖放是标准的组成部分 在HTML5中用户可以使用鼠标选择一个可拖动元素 将元素拖动到一个可放置元素 并通过释放鼠标按钮
  • OpenHarmony dump渲染和合成图层SurfaceBuffer指南

    OpenHarmony dump渲染和合成图层SurfaceBuffer指南 引言 博客停更很久了 提起笔来渐感生疏啊 看来 还是得抽出时间来更新更新啊 好了 感慨也发完了 是时候切入正题了 本篇博客主要以本人在实际项目的开发中 为了定位O
  • 韩信点兵的算法

    秦朝末年 楚汉相争 韩信率兵打仗 某次 他急需点兵迎战 就命士兵布阵三次 命3人一排 多出2名 命5人一排 多出3名 命7人一排 多出2名 后 直言有1073名勇士可击垮敌兵 其神机妙算鼓舞士气 旌旗摇动 大败楚军 首先这个故事的真实性很低
  • Linux下用户的创建与删除

    我们在Linux下创建用户主要有两种方式 adduser和useradd 它们的区别以及主要用法如下 adduser adduser的用法很简单 只需adduser username即可 如下 sudo adduser alvin 这个命令
  • python深入笔记--装饰函数

    装饰函数的参数是被装饰的函数对象 返回原函数对象 装饰的实质语句 hanshu zhuangshi hanshu 第一步 最简单的函数 第二步 对函数进行装饰 hanshu zhuangshi hanshu 定义zhuangshi函数功能
  • Vue中vuex的使用(四)

    四个map方法的使用 1 mapState方法 用于帮助我们映射state中的数据为计算属性 computed 借助mapState生成计算属性 sum school subject 从state中读取数据 对象写法 mapState su
  • RISCV架构单周期CPU设计

    指令选取 R类型指令 31 25 24 20 20 19 15 14 11 7 6 0 funct7 rs2 rs1 funct3 rd opcode 编号 指令 名称 1 add rd rs1 rs2 加 2 and rd rs1 rs2
  • 6个Python童年小游戏,开始敲起来,玩吧!

    你的童年 我的童年好像都一样 谁的童年又没玩过游戏呢 这些小游戏应该只有玩过才会懂吧 虽然程序员敲代码多年 但童心还是一直都在的 今天就分享一些私藏的童年游戏 十几行代码就能进入使用Python开发的小游戏快乐玩耍 1 五子棋 童年游戏不可
  • STM32驱动_旋转编码器EC11(中断触发版本)

    STM32驱动 旋转编码器EC11 中断触发版本 说明 根据示波器测量的真实波形 可以看到 旋转编码器转动一格 实际上只有波形的一个电平变化 并不是输出一个完整周期的波形 中断触发方案 将旋转编码器的A和B两个引脚设置为A下降沿触发 B上升
  • SQL 通配符

    在 SQL 中 通配符与 SQL LIKE 操作符一起使用 SQL 通配符用于搜索表中的数据 在 SQL 中 可使用以下通配符 通配符 描述 替代 0 个或多个字符 替代一个字符 charlist 字符列中的任何单一字符 charlist
  • 家庭IOT监测之摄像头数据上传ONENET

    本篇目标 将摄像头OV7670的照片数据 转换成BMP二进制 上传到ONENET平台 用于远程监测 材料准备 之前移植的温湿度及红外修改工程 温湿度及红外修改工程 继续往里面移植摄像头驱动上传代码 STM32F407最终摄像头上传ONENE
  • MQTT Qos详解(一)

    本文基于标准MQTT讨论 不适合其他对MQTT机制做了修改的非标准MQTT协议 MQTT设计了一套保证消息稳定传输的机制 包括消息应答 存储和重传 在这套机制下 提供了三种不同层次QoS Quality of Service QoS0 发送
  • 信息抽取(四)【NLP论文复现】Multi-head Selection和Deep Biaffine Attention在关系抽取中的实现和效果

    Multi head Selection和Deep Biaffine Attention在关系抽取中的应用 前言 Multi head Selection 一 Joint entity recognition and relation ex