精华内容
下载资源
问答
  • 三元组抽取

    2021-03-04 16:50:11
    下面记录一下三元组抽取的步骤: 首先是人工标引三元组,然后依次执行以下步骤: 步骤一:从excel标引文件中生成json格式数据;去除头尾放入index_ann.json文件中,上传到ann_four_data目录下; 步骤二:运行 ...

    下面记录一下三元组抽取的步骤:

    首先是人工标引三元组,然后依次执行以下步骤:

         步骤一:从excel标引文件中生成json格式数据;去除头尾放入index_ann.json文件中,上传到ann_four_data目录下;

         步骤二:运行 python sao.py 进行训练;

         步骤三:从excel未标引文件中生成测试数据;去除头尾放入index_wait.csv文件中,上传到wait_ann_four_data目录下;

         步骤四:运行 python sao.py 进行三元组抽取;

         步骤五:运行processcsv.java文件,处理index_wait_label.csv文件,生成三元组。

    表:三元组标引示例

    专利文档 subject action objection
    The graphene capacitor has high conductivity graphene capacitor  has  high conductivity
    展开全文
  •   本文将会介绍笔者在2019语言与智能技术竞赛的三元组抽取比赛方面的一次尝试。由于该比赛早已结束,笔者当时也没有参加这个比赛,因此没有测评成绩,我们也只能拿到训练集和验证集。但是,这并不耽误我们在这方面...
  • 目录信息抽取实战:三元组抽取一、限定领域的三元组抽取比赛介绍数据分析序列标注模型关系分类模型三元组提取总结开放领域的三元组抽取标注平台文本分类新数据进行三元组抽取总结参考文献 一、限定领域的三元组抽取 ...

    信息抽取实战:三元组抽取

    一、限定领域的三元组抽取

      本项目将会介绍在2019语言与智能技术竞赛三元组抽取比赛方面的一次尝试。由于该比赛早已结束,当时也没有参加这个比赛,因此没有测评成绩,我们也只能拿到训练集和验证集。但是,这并不耽误我们在这方面做实验。

    比赛介绍

      该比赛的网址为:http://lic2019.ccf.org.cn/kg,给定schema约束集合及句子sent,其中schema定义了关系P以及其对应的主体S和客体O的类别,例如(S_TYPE:人物,P:妻子,O_TYPE:人物)、(S_TYPE:公司,P:创始人,O_TYPE:人物)等。比如下面的例子:

    {
      "text": "九玄珠是在纵横中文网连载的一部小说,作者是龙马",
      "spo_list": [
        ["九玄珠", "连载网站", "纵横中文网"],
        ["九玄珠", "作者", "龙马"]
      ]
    }
    

      该比赛一共提供了20多万标注质量很高的三元组,其中17万训练集,2万验证集和2万测试集,实体关系(schema)50个
      在具体介绍的思路和实战前,先介绍下本次任务的处理思路
    在这里插入图片描述

      首先是对拿到的数据进行数据分析,包括统计每个句子的长度及三元组数量,每种关系的数量分布情况。接着,对数据单独走序列标注模型和关系分析模型。最后在提取三元组的时候,用Pipeline模型,先用序列标注模型预测句子中的实体,再对实体(加上句子)走关系分类模型,预测实体的关系,最后形成有效的三元组。
      接下来将逐一介绍,项目结构图如下:
    在这里插入图片描述

    数据分析

      我们对训练集做数据分析,训练集数据文件为train_data.json
      数据分析会统计训练集中每个句子的长度及三元组数量,还有关系的分布图,代码如下:

    # -*- coding: utf-8 -*-
    import json
    from pprint import pprint
    import pandas as pd
    from collections import defaultdict
    import matplotlib.pyplot as plt
    
    plt.figure(figsize=(18, 8), dpi=100)   # 输出图片大小为1800*800
    # # Mac系统设置中文字体支持
    # plt.rcParams["font.family"] = 'Arial Unicode MS'
    # 解决中文显示问题
    plt.rcParams['font.sans-serif'] = ['SimHei'] # 指定默认字体
    plt.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号'-'显示为方块的问题
    
    # 加载数据集
    def load_data(filename):
        D = []
        with open(filename, 'r', encoding='utf-8') as f:
            content = f.readlines()
    
        content = [_.replace(' ', '').replace('\u3000', '').replace('\xa0', '').replace('\u2003', '') for _ in content]
    
        for l in content:
            l = json.loads(l)
            D.append({
                'text': l['text'],
                'spo_list': [
                    (spo['subject'], spo['predicate'], spo['object'])
                    for spo in l['spo_list']
                ]
            })
        return D
    
    filename = '../data/train_data.json'
    
    D = load_data(filename=filename)
    pprint(D)
    
    # 创建text, text_length, spo_num的DataFrame
    text_list = [_["text"] for _ in D]
    spo_num = [len(_["spo_list"])for _ in D]
    
    df = pd.DataFrame({"text": text_list, "spo_num": spo_num} )
    df["text_length"] = df["text"].apply(lambda x: len(x))
    print(df.head())
    print(df.describe())
    
    # 绘制spo_num的条形统计图
    pprint(df['spo_num'].value_counts())
    label_list = list(df['spo_num'].value_counts().index)
    num_list = df['spo_num'].value_counts().tolist()
    
    # 利用Matplotlib模块绘制条形图
    x = range(len(num_list))
    rects = plt.bar(x=x, height=num_list, width=0.6, color='blue', label="频数")
    plt.ylim(0, 80000) # y轴范围
    plt.ylabel("数量")
    plt.xticks([index + 0.1 for index in x], label_list)
    plt.xlabel("三元组数量")
    plt.title("三元组频数统计图")
    
    # 条形图的文字说明
    for rect in rects:
        height = rect.get_height()
        plt.text(rect.get_x() + rect.get_width() / 2, height+1, str(height), ha="center", va="bottom")
    
    plt.savefig('./spo_num_bar_chart.png')
    plt.show()
    
    plt.close()
    
    # 关系统计图
    plt.figure(figsize=(18, 8), dpi=100)   # 输出图片大小为1800*800
    # # Mac系统设置中文字体支持
    # plt.rcParams["font.family"] = 'Arial Unicode MS'
    # 解决中文显示问题
    plt.rcParams['font.sans-serif'] = ['SimHei'] # 指定默认字体
    plt.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号'-'显示为方块的问题
    
    # 关系统计图
    relation_dict = defaultdict(int)
    
    for spo_dict in D:
        # print(spo_dict["spo_list"])
        for spo in spo_dict["spo_list"]:
            relation_dict[spo[1]] += 1
    
    label_list = list(relation_dict.keys())
    num_list = list(relation_dict.values())
    
    # 利用Matplotlib模块绘制条形图
    x = range(len(num_list))
    rects = plt.bar(x=x, height=num_list, width=0.6, color='blue', label="频数")
    # plt.ylim(0, 80000) # y轴范围
    plt.ylabel("数量")
    plt.xticks([index + 0.1 for index in x], label_list)
    plt.xticks(rotation=45) # x轴的标签旋转45度
    plt.xlabel("三元组关系")
    plt.title("三元组关系频数统计图")
    
    # 条形图的文字说明
    for rect in rects:
        height = rect.get_height()
        plt.text(rect.get_x() + rect.get_width() / 2, height+1, str(height), ha="center", va="bottom")
    
    plt.savefig('./relation_bar_chart.png')
    plt.show()
    

      输出结果如下:
    在这里插入图片描述
    在这里插入图片描述

      句子的平均长度为54,最大长度为300;每句话中的三元组数量的平均值为2.1,最大值为25
      每句话中的三元组数量的分布图如下:
    在这里插入图片描述

      关系数量的分布图如下:
    在这里插入图片描述

    序列标注模型

      我们将句子中的主体和客体作为实体,分别标注为SUBJ和OBJ,标注体系采用BIO。一个简单的标注例子如下:

    如 O
    何 O
    演 O
    好 O
    自 O
    己 O
    的 O
    角 O
    色 O
    , O
    请 O
    读 O
    《 O
    演 O
    员 O
    自 O
    我 O
    修 O
    养 O
    》 O
    《 O
    喜 B-SUBJ
    剧 I-SUBJ
    之 I-SUBJ
    王 I-SUBJ
    》 O
    周 B-OBJ
    星 I-OBJ
    驰 I-OBJ
    崛 O
    起 O
    于 O
    穷 O
    困 O
    潦 O
    倒 O
    之 O
    中 O
    的 O
    独 O
    门 O
    秘 O
    笈 O
    

      序列标注的模型采用ALBERT+Bi-LSTM+CRF结构图如下:
    在这里插入图片描述

      模型方面的代码不再具体给出,有兴趣的同学可以参考文章信息抽取实战:命名实体识别NER【ALBERT+Bi-LSTM模型 vs. ALBERT+Bi-LSTM+CRF模型】(附代码),也可以参考文章最后给出的Github项目网址。
      模型设置文本最大长度为128,利用ALBERT做特征提取,在自己的电脑上用CPU训练5个epoch,结果如下:

    
    Train on 173109 samples, validate on 21639 samples
    Epoch 1/10
    173109/173109 [==============================] - 422s 2ms/step - loss: 0.4460 - crf_viterbi_accuracy: 0.8710 - val_loss: 0.1613 - val_crf_viterbi_accuracy: 0.9235
    Epoch 2/10
    173109/173109 [==============================] - 417s 2ms/step - loss: 0.1170 - crf_viterbi_accuracy: 0.9496 - val_loss: 0.0885 - val_crf_viterbi_accuracy: 0.9592
    Epoch 3/10
    173109/173109 [==============================] - 417s 2ms/step - loss: 0.0758 - crf_viterbi_accuracy: 0.9602 - val_loss: 0.0653 - val_crf_viterbi_accuracy: 0.9638
    Epoch 4/10
    173109/173109 [==============================] - 415s 2ms/step - loss: 0.0586 - crf_viterbi_accuracy: 0.9645 - val_loss: 0.0544 - val_crf_viterbi_accuracy: 0.9651
    Epoch 5/10
    173109/173109 [==============================] - 422s 2ms/step - loss: 0.0488 - crf_viterbi_accuracy: 0.9663 - val_loss: 0.0464 - val_crf_viterbi_accuracy: 0.9654
    Epoch 6/10
    173109/173109 [==============================] - 423s 2ms/step - loss: 0.0399 - crf_viterbi_accuracy: 0.9677 - val_loss: 0.0375 - val_crf_viterbi_accuracy: 0.9660
    Epoch 7/10
    173109/173109 [==============================] - 415s 2ms/step - loss: 0.0293 - crf_viterbi_accuracy: 0.9687 - val_loss: 0.0265 - val_crf_viterbi_accuracy: 0.9664
    Epoch 8/10
    173109/173109 [==============================] - 414s 2ms/step - loss: 0.0174 - crf_viterbi_accuracy: 0.9695 - val_loss: 0.0149 - val_crf_viterbi_accuracy: 0.9671
    Epoch 9/10
    173109/173109 [==============================] - 422s 2ms/step - loss: 0.0049 - crf_viterbi_accuracy: 0.9703 - val_loss: 0.0036 - val_crf_viterbi_accuracy: 0.9670
    Epoch 10/10
    173109/173109 [==============================] - 429s 2ms/step - loss: -0.0072 - crf_viterbi_accuracy: 0.9709 - val_loss: -0.0078 - val_crf_viterbi_accuracy: 0.9674
               precision    recall  f1-score   support
    
          OBJ     0.9593    0.9026    0.9301     44598
         SUBJ     0.9670    0.9238    0.9449     25521
    
    micro avg     0.9621    0.9104    0.9355     70119
    macro avg     0.9621    0.9104    0.9355     70119
    

      利用seqeval模块做评估,在验证集上的F1值约为93.55%
    在这里插入图片描述

    关系分类模型

      需要对关系做一下说明,因为会对句子(sent)中的主体(S)和客体(O)组合起来,加上句子,形成训练数据。举个例子,在句子历史评价李氏朝鲜的创立并非太祖大王李成桂一人之功﹐其五子李芳远功不可没,三元组为[{“predicate”: “父亲”, “object_type”: “人物”, “subject_type”: “人物”, “object”: “李成桂”, “subject”: “李芳远”}, {“predicate”: “国籍”, “object_type”: “国家”, “subject_type”: “人物”, “object”: “朝鲜”, “subject”: “李成桂”}]},在这句话中主体有李成桂,李芳远,客体有李成桂和朝鲜,关系有父亲(关系类型:2)和国籍(关系类型:22)。按照的思路,这句话应组成4个关系分类样本,如下:

    2 李芳远$李成桂$历史评价李氏朝鲜的创立并非太祖大王###一人之功﹐其五子###功不可没
    0 李芳远$朝鲜$历史评价李氏##的创立并非太祖大王李成桂一人之功﹐其五子###功不可没
    0 李成桂$李成桂$历史评价李氏朝鲜的创立并非太祖大王###一人之功﹐其五子李芳远功不可没
    22 李成桂$朝鲜$历史评价李氏##的创立并非太祖大王###一人之功﹐其五子李芳远功不可没
    

      因此,就会出现关系0(表示“未知”),这样我们在提取三元组的时候就可以略过这条关系,形成真正有用的三元组。
      因此,关系一共为51个(加上未知关系:0)。关系分类模型采用ALBERT+Bi-GRU+ATT,结构图如下:
    在这里插入图片描述

      模型方面的代码不再具体给出,有兴趣的同学可以参考文章信息抽取实战:人物关系抽取【BERT模型】(附代码),也可以参考文章最后给出的Github项目网址。
      模型设置文本最大长度为128,利用ALBERT做特征提取,在自己的电脑上用CPU训练30个epoch(实际上,由于有early stopping机制,训练不到30个eopch),在验证集上的评估结果如下:

    Epoch 23/30
    396766/396766 [==============================] - 776s 2ms/step - loss: 0.1770 - accuracy: 0.9402 - val_loss: 0.2170 - val_accuracy: 0.9308
    
    Epoch 00023: val_accuracy did not improve from 0.93292
    49506/49506 [==============================] - 151s 3ms/step
    
    在测试集上的效果: [0.21701653493155634, 0.930776059627533]
        precision    recall  f1-score   support
    
              未知       0.87      0.76      0.81      5057
              祖籍       0.92      0.73      0.82       181
              父亲       0.79      0.88      0.83       609
            总部地点       0.95      0.95      0.95       310
             出生地       0.94      0.95      0.94      23301.00      1.00      1.00      1271
              面积       0.90      0.92      0.91        79
              简称       0.97      0.99      0.98       138
            上映时间       0.94      0.98      0.96       463
              妻子       0.91      0.83      0.87       680
            所属专辑       0.97      0.97      0.97      1282
            注册资本       1.00      1.00      1.00        63
              首都       0.92      0.96      0.94        47
              导演       0.92      0.94      0.93      26030.96      0.97      0.97       339
              身高       0.98      0.98      0.98       393
            出品公司       0.96      0.96      0.96       851
            修业年限       1.00      1.00      1.00         2
            出生日期       0.99      0.99      0.99      2892
             制片人       0.69      0.88      0.77       127
              母亲       0.75      0.88      0.81       425
              编剧       0.82      0.80      0.81       771
              国籍       0.92      0.92      0.92      1621
              海拔       1.00      1.00      1.00        43
            连载网站       0.98      1.00      0.99      1658
              丈夫       0.84      0.91      0.87       678
              朝代       0.85      0.92      0.88       419
              民族       0.98      0.99      0.99      14340.95      0.99      0.97       197
             出版社       0.98      0.99      0.99      2272
             主持人       0.82      0.86      0.84       200
            专业代码       1.00      1.00      1.00         3
              歌手       0.89      0.94      0.91      2857
              作词       0.85      0.81      0.83       884
              主角       0.86      0.77      0.81        39
             董事长       0.81      0.74      0.78        47
            毕业院校       0.99      0.99      0.99      1433
            占地面积       0.89      0.89      0.89        61
            官方语言       1.00      1.00      1.00        15
            邮政编码       1.00      1.00      1.00         4
            人口数量       1.00      1.00      1.00        45
            所在城市       0.90      0.94      0.92        77
              作者       0.97      0.97      0.97      4359
            成立日期       0.99      0.99      0.99      1608
              作曲       0.78      0.77      0.78       849
              气候       1.00      1.00      1.00       103
              嘉宾       0.76      0.72      0.74       158
              主演       0.94      0.97      0.95      7383
             改编自       0.95      0.82      0.88        71
             创始人       0.86      0.87      0.86        75
    
        accuracy                           0.93     49506
       macro avg       0.92      0.92      0.92     49506
    weighted avg       0.93      0.93      0.93     49506
    

    在这里插入图片描述

    三元组提取

      最后一部分,也是本次比赛的最终目标,就是三元组提取
      三元组提取采用Pipeline模式先用序列标注模型预测句子中的实体,然后再用关系分类模型判断实体关系的类别,过滤掉关系为未知的情形,就得到提取的三元组了
      三元组提取的代码如下:

    # -*- coding: utf-8 -*-
    import os, re, json, traceback
    
    import json
    import numpy as np
    from keras_contrib.layers import CRF
    from keras_contrib.losses import crf_loss
    from keras_contrib.metrics import crf_accuracy, crf_viterbi_accuracy
    from keras.models import load_model
    from collections import defaultdict
    from pprint import pprint
    from text_classification.att import Attention
    
    from albert_zh.extract_feature import BertVector
    
    # 读取label2id字典
    with open("./sequence_labeling/ccks2019_label2id.json", "r", encoding="utf-8") as h:
        label_id_dict = json.loads(h.read())
    
    id_label_dict = {v: k for k, v in label_id_dict.items()}
    # 利用ALBERT提取文本特征
    bert_model = BertVector(pooling_strategy="NONE", max_seq_len=128)
    f = lambda text: bert_model.encode([text])["encodes"][0]
    
    # 载入NER模型
    custom_objects = {'CRF': CRF, 'crf_loss': crf_loss, 'crf_viterbi_accuracy': crf_viterbi_accuracy}
    ner_model = load_model("./sequence_labeling/ccks2019_ner.h5", custom_objects=custom_objects)
    
    # 载入分类模型
    best_model_path = './text_classification/models/per-rel-19-0.9329.h5'
    classification_model = load_model(best_model_path, custom_objects={"Attention": Attention})
    
    # 分类与id的对应关系
    with open("./data/relation2id.json", "r", encoding="utf-8") as g:
        relation_id_dict = json.loads(g.read())
    
    id_relation_dict = {v: k for k, v in relation_id_dict.items()}
    
    
    # 从预测的标签列表中获取实体
    def get_entity(sent, tags_list):
    
        entity_dict = defaultdict(list)
        i = 0
        for char, tag in zip(sent, tags_list):
            if 'B-' in tag:
                entity = char
                j = i+1
                entity_type = tag.split('-')[-1]
                while j < min(len(sent), len(tags_list)) and 'I-%s' % entity_type in tags_list[j]:
                    entity += sent[j]
                    j += 1
    
                entity_dict[entity_type].append(entity)
    
            i += 1
    
        return dict(entity_dict)
    
    class TripleExtract(object):
    
        def __init__(self, text):
            self.text = text.replace(" ", "")    # 输入句子
    
        # 获取输入句子中的实体(即:主体和客体)
        def get_entity(self):
            train_x = np.array([f(self. text)])
            y = np.argmax(ner_model.predict(train_x), axis=2)
            y = [id_label_dict[_] for _ in y[0] if _]
    
            # 输出预测结果
            return get_entity(self.text, y)
    
        # 对实体做关系判定
        def relation_classify(self):
            entities = self.get_entity()
            subjects = list(set(entities.get("SUBJ", [])))
            objs = list(set(entities.get("OBJ", [])))
    
            spo_list = []
    
            for subj in subjects:
                for obj in objs:
                    sample = '$'.join([subj, obj, self.text.replace(subj, '#'*len(subj)).replace(obj, "#"*len(obj))])
                    vec = bert_model.encode([sample])["encodes"][0]
                    x_train = np.array([vec])
    
                    # 模型预测并输出预测结果
                    predicted = classification_model.predict(x_train)
                    y = np.argmax(predicted[0])
    
                    relation = id_relation_dict[y]
                    if relation != "未知":
                        spo_list.append([subj, relation, obj])
    
            return spo_list
    
        # 提取三元组
        def extractor(self):
    
            return self.relation_classify()
    

      运行三元组提取脚本,代码如下:

    # -*- coding: utf-8 -*-
    import os, re, json, traceback
    from pprint import pprint
    
    from triple_extract.triple_extractor import TripleExtract
    
    text = "真人版的《花木兰》由新西兰导演妮基·卡罗执导,由刘亦菲、甄子丹、郑佩佩、巩俐、李连杰等加盟,几乎是全亚洲阵容。"
    # text = "《冒险小王子》作者周艺文先生,教育、文学领域的专家学者以及来自全国各地的出版业从业者参加了此次沙龙,并围绕儿童文学创作这一话题做了精彩的分享与交流。"
    # text = "宋应星是江西奉新人,公元1587年生,经历过明朝腐败至灭亡的最后时期。"
    # text = "韩愈,字退之,河阳(今河南孟县)人。"
    # text = "公开资料显示,李强,男,汉族,出生于1971年12月,北京市人,北京市委党校在职研究生学历,教育学学士学位,1996年11月入党,1993年7月参加工作。"
    # text = "杨牧,本名王靖献,早期笔名叶珊,1940年生于台湾花莲,著名诗人、作家。"
    # text = "杨广是隋文帝杨坚的第二个儿子。"
    # text = "此次权益变动后,何金明与妻子宋琦、其子何浩不再拥有对上市公司的控制权。"
    # text = "线上直播发布会中,谭维维首次演绎了新歌《章存仙》,这首歌由钱雷作曲、尹约作词,尹约也在直播现场透过手机镜头跟网友互动聊天。"
    # text = "“土木之变”后,造就了明代杰出的民族英雄于谦。"
    # text = "真纳大学坐落在伊斯兰堡市,是一所创建于1967年7月的公立研究型大学。"
    # text = "黄书豪毕业于泰国博仁大学,并签约北京经纪公司,2018年参加《偶像练习生》,曾在节目中表演洗脑神曲《容易动情的人》,让全场嗨到又唱又跳,节目最终排名第43位。"
    # text = "浙江金裕包装有限公司坐落于杭州湾畔金国最大的铝氧化生产基地----江南古镇沥海工业园区,公司西近杭州,紧靠杭州湾世纪大道,距上海100公里余,是一家专业生产氧化铝化妆品配套产品,兼产酒瓶盖、小家电等铝包装产品的制造企业"
    # text = "另外,哈尔滨历史博物馆也是全国面积最小的国有博物馆,该场馆面积只有50平方米,可称之“微缩博物馆”。"
    # text = "孙杨的妈妈叫杨明,孙杨的名字后面一个字也是来源于她的名字。"
    # text = "企查查显示,达鑫电子成立于1998年6月,法定代表人张高圳,注册资本772.33万美元,股东仅新加坡达鑫控股有限公司一名。"
    
    
    triple_extract = TripleExtract(text)
    print("原文: %s" % text)
    entities = triple_extract.get_entity()
    print("实体: ", end='')
    pprint(entities)
    
    spo_list = triple_extract.extractor()
    print("三元组: ", end='')
    pprint(spo_list)
    

      我们在网上找几条样本进行测试,测试的结果如下:
    在这里插入图片描述

    总结

      本项目标题为限定领域的三元组抽取,之所以取名为限定领域,是因为该任务的实体关系是确定,一共为50种关系
      当然,上述方法还存在着诸多不足,参考苏建林的文章基于DGCNN和概率图的轻量级信息抽取模型 ,我们发现不足之处如下:

    • 主体和客体的标注策略有问题,因为句子中有时候主体和客体会重叠在一起;
    • 新引入了一类关系:未知,是否有办法避免引入;
    • 其他(暂时未想到)

      从比赛的角度将,本项目的办法效果未知,应该会比联合模型的效果差一些。但是,这是作为自己的模型,算法是一种尝试,之所以采用这种方法,是因为一开始是从开放领域的三元组抽取入手的,而这种方法方便扩展至开放领域。关于开放领域的三元组抽取,稍后就会写文章介绍。
      本项目的源代码已经公开至Github,网址为:https://github.com/chenlian-zhou/ccks_triple_extract/tree/master/limite_extract_platform

    二、开放领域的三元组抽取

      关于三元组抽取的基本介绍和常用办法,之前已经在不少文章中描述过,这里不再过多介绍,有兴趣的读者可以参考文章利用关系抽取构建知识图谱的一次尝试 。本文将会介绍在开放领域做三元组抽取的一次尝试。
      本项目已经开源至Github,文章最后会给出相应的网址。本项目的项目结构如下:
    在这里插入图片描述

      本项目一共分为四部分,主要模块介绍如下:

    • extract_example: 利用训练好的模型对基本小说和新闻进行三元组抽取,形成知识图谱例子
    • sequence_labeling训练标注,对标注的实体数据进行序列标注算法训练
    • spo_tagging_platform标注平台,标注subject,predicate和object以及三元组是否有效
    • text_classification文本分类,用于判别抽取的三元组是否有效

      本项目的抽取系统流程图如下:
    在这里插入图片描述

    标注平台

      用tornado搭建了简易的标注平台,在标注页面中,标注人员需要输入标注的句子(句子级别的抽取)以及subject,predicate,object,点击“显示SPO”,将有效的三元组标注为1,无效的三元组标注为0。之所以采取这种标注方法,是因为我们可以在句子中标注subject,predicate,object,这些标注的实体就会形成可能的三元组组合,再利用0,1来标注这种三元组是否有效,这样就能做到在开放领域进行三元组抽取。
      一个简单的标注例子如下:
    在这里插入图片描述

      再对以上的标注结果做一些说明,我们的标注是以句子为单位,进行句子级别的标注,不同要素在标注的时候加 # 区分,标注了两个subject,1个predicate(共用)和2个object,其中predidate是这些subject和object公用的,所以只需要标注一次。这样,点击“显示SPO”,一共会显示4个三元组,s,p,o用#隔开,0,1表示是否是有效三元组,默认为0
      利用空余时间,一共标注了3200多个样本,对于序列标注来说,就是3200多个样本,对于文本分类来说,就是9000多个样本了

    序列标注

      对于上述的标注例子,会形成如下的标注序列

    美	B-SUBJ
    国	I-SUBJ
    疾	I-SUBJ
    控	I-SUBJ
    中	I-SUBJ
    心	I-SUBJ
    主	B-PRED
    任	I-PRED
    雷	B-OBJ
    德	I-OBJ
    菲	I-OBJ
    尔	I-OBJ
    德	I-OBJ
    (	O
    左	O
    圈	O
    )	O
    和	O
    美	B-SUBJ
    国	I-SUBJ
    国	I-SUBJ
    立	I-SUBJ
    卫	I-SUBJ
    生	I-SUBJ
    研	I-SUBJ
    究	I-SUBJ
    院	I-SUBJ
    过	I-SUBJ
    敏	I-SUBJ
    和	I-SUBJ
    传	I-SUBJ
    染	I-SUBJ
    病	I-SUBJ
    研	I-SUBJ
    究	I-SUBJ
    所	I-SUBJ
    主	B-PRED
    任	I-PRED
    福	B-OBJ
    西	I-OBJ
    (	O
    右	O
    圈	O
    )	O
    

      将数据集分为训练集和测试集,比例为8:2。采用经典的深度学习模型ALBERT+Bi-LSTM+CRF进行实体识别,设置最大文本长度为128,训练100个epoch。关于该模型的介绍,可以参考文章信息抽取实战:命名实体识别NER【ALBERT+Bi-LSTM模型 vs. ALBERT+Bi-LSTM+CRF模型】(附代码)
      在测试集上的训练结果如下:

    accuracy:  93.69%; precision:  76.26%; recall:  82.33%; FB1:  79.18
    
    OBJ: precision:  80.47%; recall:  88.81%; FB1:  84.44  927
    PRED: precision:  76.89%; recall:  83.69%; FB1:  80.14  1021
    SUBJ: precision:  71.72%; recall:  75.32%; FB1:  73.48  983
    

      在测试集上的总体F1值接近80%

    文本分类

      关于文本分类,需要多做一些说明。
      虽然本文的题目是关于在开发领域的三元组抽取的尝试,但实际我在标注的时候,还是更多地标注人物头衔,人物关系,公司与人的关系,影视剧主演、导演信息等。形成的有效的文本分类的样本为9000多个,一共有关系1365个,数量最多的前20个关系,如下图:
    在这里插入图片描述

      以上述的标注数据为例,形成的标注数据如下:

    美国疾控中心#主任#雷德菲尔德#1#美国疾控中心主任雷德菲尔德(左圈)和美国国立卫生研究院过敏和传染病研究所主任福西(右圈)
    美国疾控中心#主任#福西#0#美国疾控中心主任雷德菲尔德(左圈)和美国国立卫生研究院过敏和传染病研究所主任福西(右圈)
    美国国立卫生研究院过敏和传染病研究所#主任#雷德菲尔德#0#美国疾控中心主任雷德菲尔德(左圈)和美国国立卫生研究院过敏和传染病研究所主任福西(右圈)
    美国国立卫生研究院过敏和传染病研究所#主任#福西#1#美国疾控中心主任雷德菲尔德(左圈)和美国国立卫生研究院过敏和传染病研究所主任福西(右圈)
    

      在实际模型训练的时候,会将原文中的subject用S*len(subject)代替,predicate用P,object用O
      将数据集分为训练集和测试集,比例为8:2。采用经典的深度学习模型ALBERT+Bi-GRU+ATT+FC,设置文本的最大长度为为128,训练30个epoch,采用early stopping机制,训练过程的loss和acc图像如下:
    在这里插入图片描述

      最终在测试集上的accuracy约为96%

    新数据进行三元组抽取

      上述的模型训练完毕后,我们就可以将其封装成HTTP服务。对于新输入的句子,我们先利用序列标注模型预测出其中的subject,predicate和object,组合成三元组与句子的拼接,输入到文本分类模型,判别该三元组是否有效,0为无效,1为有效
      从网上找几个例子,预测的结果如下:
    在这里插入图片描述在这里插入图片描述在这里插入图片描述

      extract_example目录中为抽取的效果,包括几本小说和一些新闻上的效果,关于这方面的演示,可以参考另一个项目:https://github.com/percent4/knowledge_graph_demo 。也可以参考文章知识图谱构建举例 https://blog.csdn.net/jclian91/article/details/104685424 中给出的几个知识图谱的建构的例子。

    总结

      本文写的过程较为简单,也没有代码,这是因为在之前的文章中做了大量的铺垫,主要是集中在模型方面。况且,这个项目比较大,也不适合在这里详细讲述,只在这里给出思路和大概的处理流程,具体的实现代码可以参考下方的Github地址。
      在实际的抽取过程中,一些句子也存在抽取出大量无用的三元组的情况,导致召回率高,这是因为本项目针对的是开放领域的三元组抽取,因此效果比不会有想象中的那么好,提升抽取效果的办法如下:

    • 增加数据标注量,目前序列标注算法的样本仅3200多个
    • 模型方面:现在是pipeline形式,各自的效果还行,但总体上不如Joint形式好
    • 对于自己想抽的其他三元组的情形,建议增加这方面的标注
    • 文本预测耗时长(该问题已经解决)。

      本项目作为在开放领域的三元组抽取的一次尝试,在此之前关于这方面的文章或者项目还很少,因此可以说是探索阶段。
      源码和数据已经在Github项目中给出,网址为 https://github.com/chenlian-zhou/ccks_triple_extract/tree/master/spo_extract_platform/spo_extract_platform

    参考文献

    1. 信息抽取实战:命名实体识别NER【ALBERT+Bi-LSTM模型 vs. ALBERT+Bi-LSTM+CRF模型】(附代码)
    2. 信息抽取实战:人物关系抽取【BERT模型】(附代码)
    3. 基于DGCNN和概率图的轻量级信息抽取模型
    4. 利用关系抽取构建知识图谱的一次尝试: https://www.cnblogs.com/jclian91/p/11107323.html
    5. 《知识图谱 方法、实践与应用》 王昊奋、漆桂林、陈华钧著,中国工信出版集团、电子工业出版社出版。
    展开全文
  • 针对当前网络安全领域知识获取中所依赖的流水线模式存在实体识别错误的传播,未考虑实体识别与关系抽取任务间的联系,以及模型训练缺乏标签语料的问题,提出一种融合对抗主动学习的端到端网络安全知识三元组抽取方法...
  • 2019年百度的三元组抽取比赛,一个baseline
  • 2019年百度的三元组抽取比赛,“科学空间队”源码
  • 一种用于实体关系三元组抽取的位置辅助分布标记方法 该方法是一种属于共享参数的联合抽取方式,本论文将关系作为预设属性,通过分布标记出主、客实体得到三元组。模型有两类抽取器,一类用于确定主实体S,另一种用于...

    一种用于实体关系三元组抽取的位置辅助分布标记方法

    此文为论文笔记,非本人论文哟!论文地址:https://kns.cnki.net/kcms/detail/10.1478.G2.20210604.1552.002.html

    简述:

    该方法是一种属于共享参数的联合抽取方式,该论文将关系作为预设属性,通过分布标记出主、客实体得到三元组。模型有两类抽取器,一类用于确定主实体S,另一种用于确定关系R和其对应的客实体O,即先抽取出所有的S,然后将其逐一送入具有不同关系属性R的抽取器中抽取对应的O。这种方式为所有主体和客体提供了在每种关系下组合的机会,涵盖了包括单实体重叠和实体对重叠在内所有可能的三元组,即解决了SPO(Single Entity Overlap)和EPO(Entity Pair Overlap)。

    该论文特点:

    结合自然语言形态结构特点设置了多重位置感知信息作为重要辅助。另一方面,优化了编码的共享方式,不是所有的特征都共享。该模型引入了位置信息等前序结果特征并辅以注意力机制来共享底层编码。

    基于位置感知的标记方法简述

    三元组<主实体S,关系R,客实体O>,抽取逻辑采用O=R(S),将关系R视作主实体S和客实体O之间一种映射关系,如下图所示,先根据主实体抽取器抽取出所有的主实体S,然后每个主实体S经过n个不同的客实体抽取器(带有预设的关系属性)获得对应的所有客实体O。因为只有与当前S存在关系的O才会被抽取出来,可以避免无关实体对冗余的情况。

    在这里插入图片描述

    基于位置感知的标记方法详细设计

    抽取器个数,一个主实体抽取器[可以确定所有的主实体S],N个客实体抽取器(和关系种类数有关)[因为自带关系属性,所有同时确定了每个主实体对应的R和O]。这两类抽取器通过采用两个标记器分别标记出实体的头和尾的位置,从而根据头和尾获得实体。

    1)Bert中文字向量编码

    这个就不多说了,简而言之就是从输入的句子中提取出特征信息 。

    2)主实体抽取器

    主实体抽取任务是由2个标记器分别对应的两个序列标记子任务组成的,第一个标记器用来标记实体的头位置,记为start,第二个标记器用来标记实体的尾位置,记为end。S抽取器采用实体类型作为标签。如果一个字符是一个主实体的头部或尾部位置则该字符被标记为该实体对应的实体类型,否则标记为“O”,图中的X1和X2代表了两种不同的实体类型。头部标记器接收bert编码结果序列作为输入,而尾部标记器的输入在bert编码结果序列上还增加了头部位置信息,即当前位置i与其左侧的 istart的相对距离,如果左侧未出现start,则该位置记为-1,如果当前位置就是start的位置,则记为0。将结合头部位置嵌入的编码序列和bert编码结果序列记为hie ,字符xi的标记结果由下面公式计算而来,其中k代表概率最大的标签项,最终得到两个标记结果序列,通过就近原则将具有相同实体类型的标记的头和尾进行配对得出所有主实体。
    在这里插入图片描述
    其中hi表示原始编码结果序列,psi 表示头部相对位置嵌入向量,二者拼接所得hei则是尾部标记器的输入序列。

    3)共享编码机制

    此论文中的两类抽取共享bert编码层,S抽取器直接采用BERT编码结果序列作为输入,而OforR抽取器则将BERT编码序列与S抽取结果进行融合作为输入。将S中包含的所有字符的平均向量表示该S,记为Vs,并将其相加至编码序列每一个字符的向量表示xi中得到新的序列hisub,hisub={x1+vs, …,xn+vs}。另一个方面,在hisub的基础上拼接一个相对位置向量pisub,表示当前位置与对应S的相对距离,得到h’i序列, h’i=[hisub;pisub],因为S是由多字符构成的,作者将S的相对位置都用0表示,S左侧的字符的相对位置取与该S头部位置的相对距离,S右侧字符的相对位置取与该S尾部位置的相对距离。然后经过自注意力层,自注意力可以捕获同一个句子中字词之间的一些句法特征以及语义特征,可捕获全局的联系,对长短距离依赖关系有着更强的捕捉能力。

    4)指定关系下客实体抽取器

    此论文分别针对每一种关系设置一个客实体抽取器,简称OforRn。与S抽取器处理逻辑相同,OforR抽取器也通过标记头尾位置得出实体,但每个OforR只负责抽取出该关系下的O。OforRn抽取器内两个标记器均采用全连接层加sigmod的方式,标签采用1/0,若某一位置上的字符被判定为一个客实体的头部或尾部的可能性大于所设定的阈值则标记为1,否则标记为0。考虑到存在多个O的情况下,根据就近原则将标签为1的头尾进行两两配对得出所有的客实体。
    在这里插入图片描述
    其中 hoi表示 OforRn 的输入序列, psi表示头部相对位置嵌入向量,二者拼接所得的 hoei即尾部标记器的输入序列

    5)全局流程图

    在这里插入图片描述

    6)关于模型的评价指标

    本文采用准确率 Precision、召回率 Recall 和 F1 值来评估实验结果。只有当一个三元组中的主实体、客实体以及关系类型都判断正确时,该三元组才被认为是正确的抽取结果。
    最后附上作者的对比结果
    在这里插入图片描述

    展开全文
  • A Novel Cascade Binary Tagging Framework for Relational Triple Extraction 此文为论文笔记,非本人论文哟! ...论文特点 将关系建模为S到O的映射,...三元组抽取问题就被分解为如下的两步过程:确定句子中所有可能的头

    A Novel Cascade Binary Tagging Framework for Relational Triple Extraction

    *此文为论文笔记,非本人论文哟!
    论文地址:https://arxiv.org/pdf/1909.03227.pdf
    论文源码:https://github.com/weizhepei/CasRel

    论文特点

    采用层叠式指针标注框架,将关系建模为S到O的映射,每个抽取器都将在给定关系和头实体的条件下识别出所有可能的尾实体。

    详细设计

    三元组抽取问题就被分解为如下的两步过程:确定句子中所有可能的头实体;针对每个头实体,使用关系特定的抽取器来同时识别出所有可能的关系和对应的尾实体。

    1)Bert encoder

    对句子进行编码,获取每个词的隐层表示,也可以使用LSTM替换Bert
    在这里插入图片描述
    S:输入词的独热编码
    s:表示embedding表
    p :表示句子中p位置的位置向量
    Trans : 表示一个transformer的encoder层

    2)头实体标记模块

    主要任务是识别所有可能的实体,对BERT Encoder获取到的词的隐层表示解码,构建2个二分类器预测主实体S的开始和结束位置,即对每个词计算其作为start和end的概率,根据设定的阈值,大于该阈值则标记为1,否则为0。此论文只是找出主体和客体的边界,没有判断客实体类型。首先找出句子中所有的subject, 用0和1两个类别判定, subject的开始和结束都用1表示,不是边界的词用0表示,对于句子中的多个subject, 会出现多个间隔的1, 此时确定subject用了就近原则,离得近的两个1及其中间的词就是一个subject。
    计算公式:
    在这里插入图片描述

    3)Cascade Decoder—Relation-specific Object Taggers

    Vksub 代表 subject 特征向量,若一个subject存在多个token,将对该subject包括的所有token求向量平均作为该subject的向量表示,即Vnsub(假设该subject是该句子的第n个subject),hN代表 BERT 编码向量。也就是说解码的时候不仅考虑了Bert的隐层向量还引入了识别出的subject 特征。对于识别出来的每一个 subject,遍历所有的关系r, 为每一个关系r会解码出其 object 的 start 和 end 索引位置,与 Subject Tagger 类似,如果object不存在,则用None表示。
    公式如下:
    在这里插入图片描述
    i :表示subject tagger模块下的第i个主体的编码表示向量

    4)全局流程图

    框架图

    5)关于模型的评价指标

    本文采用准确率 Precision、召回率 Recall 和 F1 值来评估实验结果。只有当一个三元组中的主实体、客实体以及关系类型都判断正确时,该三元组才被认为是正确的抽取结果。
    在这里插入图片描述

    展开全文
  • 这是要解决知识图谱中的其中一个问题:从非结构文本中抽取三元组。 要解决这个问题,总体思路有通过模板抽取、通过模型抽取三元组工业界一般都存储在neo4j中,学术界会采用RDF形式存储。 1结构化数据抽取 定义好...
  • NLP(二十六)限定领域的三元组抽取的一次尝试

    千次阅读 热门讨论 2020-03-15 18:39:14
      本文将会介绍笔者在2019语言与智能技术竞赛的三元组抽取比赛方面的一次尝试。由于该比赛早已结束,笔者当时也没有参加这个比赛,因此没有测评成绩,我们也只能拿到训练集和验证集。但是,这并不耽误我们在这方面...
  •   当我写下这篇文章的时候,我的内心是激动的,这是因为,自从去年6月份写了文章利用关系抽取构建知识图谱的一次尝试 后,我就一直在试图寻找一种在开放领域能够进行三元组抽取的办法,也有很多读者问过我这方面的...
  • 关系抽取的主要任务就是,给定一段句子文本,抽取句子中的两个实体以及实体之间的关系,以次来构成一个三元组(s,p,o),s是subject表示主实体,o为object表示客实体,p为predicate表示两实体间的关系。...
  • <div><p>该提问来源于开源项目:ownthink/Jiagu</p></div>
  • 知识图谱三元组抽取

    万次阅读 2017-11-03 17:54:06
    http://blog.csdn.net/u013412066/article/details/68065518
  • 实体识别和关系抽取是最核心的内容 而关系抽取也隶属于三元组抽取的一部分,所以研究三元组的抽取对知识图谱的构建有比较重要的意义。 2.Bert 原理 模型介绍 代码实践 bert 原理 Bert的全名是Bidirectional Encoder ...
  •  EventTriplesExtraction based on dependency parser and semantic role labeling, 基于依存句法与语义角色标注的事件三元组抽取  文本表示一直是个重要问题,如何以清晰,简介的方式对一个文本信息进行有效表示...
  • 该任务来源于2019年百度举办的一个竞赛"2019语言与智能技术竞赛",其实它是一个信息抽取的任务,更精确地说是“三元组抽取任务,示例数据如下: { "text": "杜秉离 男,1948年8月生,河南商水县巴村镇娄庄人", ...
  • deepdive系统可以实现高质量:PaleoDeepDive比志愿者更高质量在提取复杂的知识科学领域和获奖的性能进入实体关系抽取的比赛 deepdive知道数据往往是嘈杂和不准确的:名字拼错的,自然语言是模糊的,人会犯错误。以...
  • <div><p>环境: - TF 1.15 - python 3 - GPU cuda 10 - bert4keras 0.5.6 模型: - albert 中文预训练模型,albert_tiny_google_zh_489k.zip - 语料,当时比赛的数据,...
  • 二、基于依存句法的三元组抽取  1、对于每个词生成一个该词的依存句法的儿子节点,主要存储关系和对应儿子词的位置;  2、对没歌词生成一个该词的父子数组的依存结构,主要是记录该词的词性、父节点的词性...

空空如也

空空如也

1 2 3 4 5 ... 13
收藏数 246
精华内容 98
关键字:

三元组抽取