跳转至

实体消歧应用

学习目标

  • 理解实体消歧的思想和原理
  • 掌握实现实体消歧的代码

1 实体消岐

  • 实体消歧的本质在于一个单词很可能有多个意思,也就是在不同的上下文中所表达的含义可能不太一样。
  • 举例说明:

    • 首先我们需要准备一个类似于下面的这种实体库:
    id 实体名称 实体描述
    1001 苹果 苹果公司没几年
    1002 苹果 水果的一种,一般产自于。。。
    • 当我们拿到text时,比如“今天苹果发布了新的手机”,我们可以将实体库中的实体描述,全部转换为向量。

      [今天,发布了新的手机] ⇒ 向量(tf-idf) ;水果中的一种,一般产自于 ⇒ 向量(tf-idf);美国一家高科技公司,经典的产品有iphone 手机 ⇒ 向量(tf-idf)

      基于上述向量做相似度的计算,如果S1>S2 分类成Fruit, 反之分类成苹果公司


2 案例介绍

  • 将句中识别的实体与知识库中实体进行匹配,解决实体歧义问题。
  • 数据介绍

    • 在data/entity_disambiguation目录中,有两个csv文件,其中entity_list.csv是已经存在的真实实体信息,valid_data.csv是需要消歧的语句。
    • entity_list.csv数据格式
    entity_id,entity_name,desc
    1001,小米|小米公司|小米科技有限责任公司|XIAOMI,"北京小米科技有限责任公司成立于2010年3月3日 [1]  ,是一家专注于智能硬件和电子产品研发的移动互联网公司,同时也是一家专注于高端智能手机、互联网电视以及智能家居生态链建设的创新型科技企业。 [2] 
    为发烧而生”是小米的产品概念。小米公司创造了用互联网模式开发手机操作系统、发烧友参与开发改进的模式。小米还是继苹果、三星、华为之后第四家拥有手机芯片自研能力的科技公司。"
    1002,小米,小米,原名:粟,也称作粱、狗尾草、黄粟、粟米,拉丁文名:Setaria italica (L.) Beauv. var. germanica (Mill.) Schrad. 禾本科、狗尾草属一年生草本,须根粗大,秆粗壮,粟是谷子去皮后的结果,谷子是谷类植物,禾木本的一种,粟的营养价值很高,含丰富的蛋白质和脂肪和维生素,它不仅供食用,入药有清热、清渴,滋阴,补脾肾和肠胃,利小便、治水泻等功效,又可酿酒。其茎叶又是牲畜的优等饲料,它含粗蛋白质5-7%,超过一般牧草的含量1.5-2倍,而且纤维素少,质地较柔软,为骡、马所喜食;其谷糠又是猪、鸡的良好饲料。
    1003,小米,小米是在电视剧《武林外传》中登场的次要人物,同福客栈门口的乞丐,丐帮四袋弟子。他是一个有休息日的乞丐,每逢初一十五,小米自己给自己放假,如果你把铜板扔到他的破碗里,他会毫不犹豫还给你。不过你可别以为他就这么不要了,哈哈,当休息日一过,小米又开工时,他可是会抢回本属于自己的铜板的哦!
    1004,苹果|apple,"苹果是水果中的一种,是蔷薇科苹果亚科苹果属植物,其树为落叶乔木。苹果的果实富含矿物质和维生素,是人们经常食用的水果之一。
    

    entity_list.csv格式分为三列,第一列entity_id代表实体的编号;第二列entity_name为实体名称,第三列desc为实体的描述信息,不同列之间用","分隔开。


    • valid_data.csv数据格式
    id,sentence
    1,"一说到华盛顿特区(Washington, D.C.),大家心中浮现的一定是气势恢宏的美国总统官邸─白宫(White House),这个掌控美国、牵动全球政经活动的白色建筑,不仅在历史上占有举足轻重的地位,更是每个人来到华盛顿特区非去不可的景点之一。到这里除了跟白宫合照,留下到此一游的证据外,还有许多重要地标与藏品丰富的博物馆,错过这些景点,虽不至于遗憾终身,但可能不算到过华盛顿喔!"
    2,"很多的网友都会有这样的一个问题,明明华盛顿和乾隆死在了同一年,为何却给人的感觉不是一个时代的人呢?"
    3,"1799年12月14日,美利坚合众国的开国元勋华盛顿溘然长逝。然后,美国人对当年华盛顿的死因,仍然心存疑虑,事隔200多年后,人们对他的死因又有新的发现。"
    

    valid_data.csv格式分为两列,第一列id代表要消岐的实体编码id;第二列sentence描述实体的原始句子,不同列之间用","分割开。


  • 实现步骤:
    1. 读取数据:首先读取包含实体列表的entity_list.csv和包含待处理句子的valid_data.csv
    2. 处理实体名称:将entity_list.csv中的实体名称添加到分词词典中,确保可以在后续分词和匹配过程中识别这些实体。
    3. 计算TF-IDF特征矩阵:将每个实体的描述通过分词处理后生成TF-IDF特征矩阵,用于后续的相似度计算。
    4. 匹配句子中的实体:在valid_data.csv中的句子中找到关键词,并通过TF-IDF相似度计算找到与关键词匹配的实体ID。
    5. 输出结果:将句子中匹配到的实体及其位置与对应的实体ID存储为新的CSV文件。
  • 代码实现

    import pandas as pd
    import numpy as np
    import os
    import collections
    import jieba
    from sklearn.feature_extraction.text import TfidfVectorizer
    from sklearn.metrics.pairwise import cosine_similarity
    
    # 获取当前工作目录路径
    bast_path = os.getcwd()
    
    # TODO:将entity_list.csv中已知实体的名称导入分词词典
    # 读取实体列表文件(entity_list.csv),包含实体名称和描述信息
    entity_data = pd.read_csv(os.path.join(bast_path, 'data/entity_disambiguation/entity_list.csv'), encoding='utf-8')
    print(f'entity_data--》{entity_data.head()}')
    
    # TODO:对每句句子识别并匹配实体
    # 读取包含待处理句子的文件(valid_data.csv)
    valid_data = pd.read_csv(os.path.join(bast_path, 'data/entity_disambiguation/valid_data.csv'), encoding='gb18030')
    print(f'valid_data--》{valid_data.head()}')
    
    # 将实体名称拼接成一个长字符串,并用'|'分隔,用于统计实体名称的出现次数
    s = ''
    keyword_list = []
    for i in entity_data['entity_name'].values.tolist():
        s += i + '|'
    
    # 统计实体名称在字符串中的出现次数,如果某个名称出现次数超过一次,则将其加入keyword_list(关键词列表)
    for k, v in collections.Counter(s.split('|')).items():
        if v > 1:
            keyword_list.append(k)
    
    # 生成TF-IDF矩阵
    
    # 对实体的描述信息进行分词,将每个实体描述分词后的结果存入train_sentence列表中
    train_sentence = []
    for i in entity_data['desc'].values:
        train_sentence.append(' '.join(jieba.cut(i)))
    print(len(train_sentence))
    
    # 初始化TF-IDF向量化工具
    vectorizer = TfidfVectorizer()
    
    # 将实体描述信息转换为TF-IDF特征矩阵
    X = vectorizer.fit_transform(train_sentence)
    print(X)
    print(X.toarray().shape)
    
    # 定义获取实体ID的函数,根据给定的句子计算其与实体描述的TF-IDF余弦相似度,返回最相似的实体ID
    def get_entityid(sentence):
        id_start = 1001  # 假设实体ID从1001开始
        a_list = [' '.join(jieba.cut(sentence))]  # 对输入句子分词
        print(f'a_list--》{a_list}')
        print(vectorizer.transform(a_list))  # 将句子转换为TF-IDF特征
        print(cosine_similarity(vectorizer.transform(a_list), X))  # 计算句子与所有实体描述的余弦相似度
        res = cosine_similarity(vectorizer.transform(a_list), X)[0]  # 获取相似度结果
        top_idx = np.argsort(res)[-1]  # 获取最相似的实体在TF-IDF矩阵中的索引
        print(f'np.argsort(res)==>{np.argsort(res)}')
        return id_start + top_idx  # 返回实体ID
    
    # TODO:将计算结果存入文件
    print(f'keyword_list--》{keyword_list}')
    
    # 初始化行计数器和结果列表
    row = 0
    result_data = []
    neighbor_sentence = ''
    
    # 遍历valid_data中的每一个句子,处理其中的关键词
    for sentence in valid_data['sentence']:
        print(f'sentence--》{sentence}')
        res = [row]  # 初始化结果列表,首先添加当前行号
        for keyword in keyword_list:
            if keyword in sentence:  # 如果句子中包含关键词
                print(f'keyword--》{keyword}')
                k_len = len(keyword)  # 计算关键词的长度
                print(f'k_len--》{k_len}')
                ss = ''
                for i in range(len(sentence) - k_len + 1):
                    if sentence[i:i+k_len] == keyword:  # 如果在句子中找到关键词
                        s = str(i) + '-' + str(i + k_len) + ':'  # 获取关键词在句子中的位置(如"0-5")
                        print(f's-->{s}')
                        # 获取包含关键词的邻近句子,用于计算实体相似度
                        if i > 10 and i + k_len < len(sentence) - 9:
                            neighbor_sentence = sentence[i-10:i+k_len+9]
                        elif i < 10:
                            neighbor_sentence = sentence[:20]
                        elif i + k_len > len(sentence) - 9:
                            neighbor_sentence = sentence[-20:]
    
                        # 调用get_entityid函数,获取与邻近句子最相似的实体ID
                        s += str(get_entityid(neighbor_sentence))
                        ss += s + '|'  # 将位置和实体ID拼接成字符串
                res.append(ss[:-1])  # 将处理结果加入到当前行的结果中
            break
        result_data.append(res)  # 将当前句子的处理结果加入结果列表
        row += 1  # 行计数器加1
    
    # 将结果保存为CSV文件,文件路径为'entity_disambiguation_submit.csv'
    pd.DataFrame(result_data).to_csv('entity_disambiguation_submit.csv', index=False)
    

小节总结

本小节主要介绍了实体消岐的原理,以及如何通过代码实现实体消岐任务。