知识图谱:AI和数据科学的游戏改变者

知识图谱:AI和数据科学的游戏改变者' -> '知识图谱:AI和数据科学的游戏改变者

介绍

知识图谱已经成为人工智能和数据科学中记录结构化信息以促进成功的数据检索、推理和推断的强大而多功能的方法。本文探讨了最先进的知识图谱,包括构建、表示、查询、嵌入、推理、对齐和融合。

我们还讨论了知识图谱的许多应用,例如推荐引擎和问答系统。最后,为了为新的进展和研究机会铺平道路,我们探讨了该主题的问题和潜在的未来发展方向。

知识图谱通过提供一种灵活且可扩展的机制来表达实体和特征之间复杂连接的方式,彻底改变了信息的组织和使用方式。在这里,我们对知识图谱的概念、重要性以及在各个领域中的潜在应用进行了一般性介绍。

学习目标

  • 了解知识图谱作为结构化信息的表示形式的概念和目的。
  • 了解知识图谱的关键组成部分:节点、边和属性。
  • 探索构建过程,包括数据提取和集成技术。
  • 了解知识图谱嵌入如何将实体和关系表示为连续向量。
  • 探索推理方法,从现有知识中推断出新的见解。
  • 深入了解知识图谱可视化以获得更好的理解。

本文作为数据科学博客马拉松的一部分发表。

什么是知识图谱?

知识图谱可以存储信息提取操作期间提取的信息。许多基本的知识图谱实现利用三元组的概念,三元组是一个包含三个元素(主语、谓语和宾语)的集合,可以保存关于任何事物的信息。

图是由节点和边组成的集合。

这是我们可以设计的最小的知识图谱,也称为三元组。知识图谱有多种形式和尺寸。在这里,节点A和节点B是两个独立的事物。这些节点通过一条边连接,表示两个节点之间的关系。

知识图谱中的数据表示

以以下短语为例:

伦敦是英格兰的首都。威斯敏斯特位于伦敦。

我们将看到一些基本的处理方法,但最初我们将得到两个三元组,如下所示:

(伦敦,是首都,英格兰),(威斯敏斯特,位于,伦敦)

在这个例子中,我们有三个不同的实体(伦敦、英格兰和威斯敏斯特)和两个关系(首都、位置)。构建知识图谱只需要在网络中有两个相关节点,节点表示实体,边表示关系。得到的结构如下:手动创建知识图谱不具备可扩展性。没有人会浏览数百页来提取所有实体及其关系!

机器比人更适合处理这项工作,因为它们可以轻松处理数百甚至数千篇论文。然而,机器无法理解自然语言,这是另一个困难。在这种情况下,使用自然语言处理(NLP)非常重要。

如果我们想要从文本中创建知识图谱,让计算机理解自然语言是至关重要的。使用NLP方法来实现这一点,包括句子分割、依存分析、词性标注和实体识别。

导入依赖项和加载数据集

import re
import pandas as pd
import bs4
import requests
import spacy
from spacy import displacy
nlp = spacy.load('en_core_web_sm')

from spacy.matcher import Matcher 
from spacy.tokens import Span 

import networkx as nx

import matplotlib.pyplot as plt
from tqdm import tqdm

pd.set_option('display.max_colwidth', 200)
%matplotlib inline

# 导入维基百科句子
candidate_sentences = pd.read_csv("../input/wiki-sentences1/wiki_sentences_v2.csv")
candidate_sentences.shape

candidate_sentences['sentence'].sample(5)

句子分割

将文本文章或文档分割成句子是创建知识图谱的第一阶段。然后,我们只选择那些确切有一个主语和一个宾语的短语。

doc = nlp("the drawdown process is governed by astm standard d823")

for tok in doc:
  print(tok.text, "...", tok.dep_)

实体提取

一个句子的单词组件可以很容易地被移除。我们可以通过使用词性标注(POS标签)来迅速实现这一点。名词和专有名词将成为我们的实体。

当一个实体跨越多个单词时,仅仅使用词性标签是不够的。需要解析语句的依赖树。

节点及其关系在开发知识图谱时非常重要。

这些节点将由在维基百科文本中找到的实体组成。边反映了这些元素之间的关系。我们将使用无监督的方法从短语结构中提取这些元素。

基本思想是读取一个短语,并在遇到主语和宾语时识别它们。然而,存在一些缺点。例如,“红酒”是一个跨越多个词的短语实体,而依赖分析器只能识别单个词作为主语或宾语。

因为上述问题,我创建了下面的代码来从句子中提取主语和宾语(实体)。为了方便起见,我将代码拆分为多个部分:

def get_entities(sent):
  ## chunk 1
  ent1 = ""
  ent2 = ""

  prv_tok_dep = ""    # 句子中前一个标记的依赖关系标签
  prv_tok_text = ""   # 句子中前一个标记

  prefix = ""
  modifier = ""

  #############################################################
  
  for tok in nlp(sent):
    ## chunk 2
    # 如果标记是一个标点符号,则继续到下一个标记
    if tok.dep_ != "punct":
      # 检查:标记是否是一个复合词
      if tok.dep_ == "compound":
        prefix = tok.text
        # 如果前一个词也是一个'compound',则将当前词添加到它之后
        if prv_tok_dep == "compound":
          prefix = prv_tok_text + " "+ tok.text
      
      # 检查:标记是否是一个修饰词
      if tok.dep_.endswith("mod") == True:
        modifier = tok.text
        # 如果前一个词也是一个'compound',则将当前词添加到它之后
        if prv_tok_dep == "compound":
          modifier = prv_tok_text + " "+ tok.text
      
      ## chunk 3
      if tok.dep_.find("subj") == True:
        ent1 = modifier +" "+ prefix + " "+ tok.text
        prefix = ""
        modifier = ""
        prv_tok_dep = ""
        prv_tok_text = ""      

      ## chunk 4
      if tok.dep_.find("obj") == True:
        ent2 = modifier +" "+ prefix +" "+ tok.text
        
      ## chunk 5  
      # 更新变量
      prv_tok_dep = tok.dep_
      prv_tok_text = tok.text
  #############################################################

  return [ent1.strip(), ent2.strip()]

部分 1

上面的代码块定义了几个空变量。前一个单词的依赖项和单词本身将保存在变量 prv_tok_dep 和 prv_tok_text 中,分别。前缀和修饰符将保存与主语或宾语相关的文本。

部分 2

然后,我们将逐个检查短语中的所有标记。首先,我们将确定标记是否为标点符号。如果是这种情况,我们将忽略它并继续下一个标记。如果标记是复合短语的组成部分(依赖关系标签为“compound”),我们将将其放入前缀变量中。

人们将许多单词组合在一起形成一个复合词,并生成具有新含义的新短语(例如“足球场”和“动物爱好者”)。

他们将在每个句子的主语或宾语中添加此前缀。类似的方法也适用于形容词,例如“漂亮的衬衫”、“大房子”等。

部分 3

如果在这种情况下标记是主语,则将其输入到 ent1 变量中作为第一个实体。所有变量前缀、修饰符、prv_tok_dep 和 prv_tok_text 将被重置。

部分 4

如果标记是宾语,则将其放置在 ent2 变量中作为第二个实体。所有变量前缀、修饰符、prv_tok_dep 和 prv_tok_text 将被重置。

部分 5

确定了短语的主语和宾语后,我们将更新前一个标记及其依赖关系标签。

让我们使用一个短语来测试这个函数:

get_entities("这部电影有200项专利")

哇,一切都按计划进行。在上面的短语中,“电影”是主题,“200项专利”是目标。

现在我们可以使用这种方法来提取我们数据中所有短语的这些实体配对:

entity_pairs = []

for i in tqdm(candidate_sentences["sentence"]):
  entity_pairs.append(get_entities(i))

列表entity_pairs包含了来自维基百科句子的所有主-宾配对。让我们看一下其中的一部分。

entity_pairs[10:20]

正如您所见,这些实体配对中存在一些代词,如“我们”,“它”,“她”等。相反,我们希望是专有名词或名词。我们可能需要更新get_entities()函数的代码以过滤掉代词。

关系提取

实体的提取只是任务的一半。我们需要边来连接节点(实体)以形成知识图。这些边表示两个节点之间的连接。

根据我们的假设,谓词是短语中的主要动词。例如,在陈述句“1929年有60部好莱坞音乐剧上映”中,“上映”这个动词被用作由这个句子形成的三元组的谓词。

下面的函数可以从句子中提取这样的谓词。我在这种情况下使用了spaCy的基于规则的匹配:

def get_relation(sent):

  doc = nlp(sent)

  # Matcher类对象
  matcher = Matcher(nlp.vocab)

  #定义模式
  pattern = [{'DEP':'ROOT'}, 
            {'DEP':'prep','OP':"?"},
            {'DEP':'agent','OP':"?"},  
            {'POS':'ADJ','OP':"?"}] 

  matcher.add("matching_1", None, pattern) 

  matches = matcher(doc)
  k = len(matches) - 1

  span = doc[matches[k][1]:matches[k][2]] 

  return(span.text)

这个函数的模式试图发现短语的ROOT词或主要动词。在确定了ROOT之后,模式会检查它是否后面跟着一个介词(’prep’)或一个代理词。如果是这样,它会将其附加到ROOT词上。让我演示一下这个函数:

get_relation("约翰完成了任务")

relations = [get_relation(i) for i in tqdm(candidate_sentences['sentence'])]

让我们看一下我们刚刚提取的最常见的关系或谓词:

pd.Series(relations).value_counts()[:50]

构建知识图

最后,我们将使用提取的实体(主-宾配对)和谓词(实体之间的关系)构建一个知识图。让我们用实体和谓词构建一个数据框:

# 提取主语
source = [i[0] for i in entity_pairs]

# 提取宾语
target = [i[1] for i in entity_pairs]

kg_df = pd.DataFrame({'source':source, 'target':target, 'edge':relations})

然后,我们将使用networkx库从这个数据框中构建一个网络。节点将表示实体,而节点之间的边缘或连接将反映节点之间的关系。

这将是一个有向图。换句话说,每个链接的节点对的关系只是单向的,从一个节点到另一个节点。

# 从数据框创建一个有向图
G=nx.from_pandas_edgelist(kg_df, "source", "target", 
                          edge_attr=True, create_using=nx.MultiDiGraph())

plt.figure(figsize=(12,12))

pos = nx.spring_layout(G)
nx.draw(G, with_labels=True, node_color='skyblue', edge_cmap=plt.cm.Blues, pos = pos)
plt.show()

让我们用一个小例子绘制网络:

import networkx as nx
import matplotlib.pyplot as plt

# 创建一个KnowledgeGraph类
class KnowledgeGraph:
    def __init__(self):
        self.graph = nx.DiGraph()

    def add_entity(self, entity, attributes):
        self.graph.add_node(entity, **attributes)

    def add_relation(self, entity1, relation, entity2):
        self.graph.add_edge(entity1, entity2, label=relation)

    def get_attributes(self, entity):
        return self.graph.nodes[entity]

    def get_related_entities(self, entity, relation):
        related_entities = []
        for _, destination, rel_data in self.graph.out_edges(entity, data=True):
            if rel_data["label"] == relation:
                related_entities.append(destination)
        return related_entities


if __name__ == "__main__":
    # 初始化知识图谱
    knowledge_graph = KnowledgeGraph()

    # 添加实体及其属性
    knowledge_graph.add_entity("美国", {"首都": "华盛顿特区", "大陆": "北美洲"})
    knowledge_graph.add_entity("法国", {"首都": "巴黎", "大陆": "欧洲"})
    knowledge_graph.add_entity("中国", {"首都": "北京", "大陆": "亚洲"})

    # 添加实体之间的关系
    knowledge_graph.add_relation("美国", "邻居", "加拿大")
    knowledge_graph.add_relation("美国", "邻居", "墨西哥")
    knowledge_graph.add_relation("法国", "邻居", "西班牙")
    knowledge_graph.add_relation("法国", "邻居", "意大利")
    knowledge_graph.add_relation("中国", "邻居", "印度")
    knowledge_graph.add_relation("中国", "邻居", "俄罗斯")

    # 获取并打印属性和相关实体
    print("法国的属性:", knowledge_graph.get_attributes("法国"))
    print("中国的邻居:", knowledge_graph.get_related_entities("中国", "邻居"))

    # 可视化知识图谱
    pos = nx.spring_layout(knowledge_graph.graph, seed=42)
    edge_labels = nx.get_edge_attributes(knowledge_graph.graph, "label")

    plt.figure(figsize=(8, 6))
    nx.draw(knowledge_graph.graph, pos, with_labels=True, 
                      node_size=2000, node_color="skyblue", font_size=10)
    nx.draw_networkx_edge_labels(knowledge_graph.graph, pos, 
                                edge_labels=edge_labels, font_size=8)
    plt.title("知识图谱:国家及其首都")
    plt.show()

这并不是我们想要的(但仍然非常壮观!)。我们发现我们生成了一个包含所有关系的图。这样关系或谓词过多的图变得非常难以看清。

因此,最好只使用一些关键关系来可视化图形。我将一次处理一个关系。让我们从关系“composed by”开始:

G=nx.from_pandas_edgelist(kg_df[kg_df['edge']=="composed by"], 
                            "source", "target", 
                          edge_attr=True, create_using=nx.MultiDiGraph())

plt.figure(figsize=(12,12))
pos = nx.spring_layout(G, k = 0.5) 
nx.draw(G, with_labels=True, node_color='skyblue', 
                                node_size=1500, edge_cmap=plt.cm.Blues, pos = pos)
plt.show()

这是一个更好的图。这种情况下的箭头指向作曲家。在上面的图中,著名的音乐作曲家A.R. Rahman与“soundtrack score”、“film score”和“music”等事物相关联。

让我们看一些额外的连接。现在我想为“written by”关系绘制图形:

G=nx.from_pandas_edgelist(kg_df[kg_df['edge']=="written by"], "source", 
                            "target", 
                          edge_attr=True, create_using=nx.MultiDiGraph())

plt.figure(figsize=(12,12))
pos = nx.spring_layout(G, k = 0.5)
nx.draw(G, with_labels=True, node_color='skyblue', node_size=1500, 
    edge_cmap=plt.cm.Blues, pos = pos)
plt.show()

这个知识图谱为我们提供了一些惊人的数据。著名的作词家包括Javed Akhtar、Krishna Chaitanya和Jaideep Sahni;这张图形象地描绘了他们之间的关系。

让我们看一下另一个关键谓词“released in”的知识图谱:

G = nx.from_pandas_edgelist(kg_df[kg_df['edge']=="发布于"],
                           "source", "target", 
                          edge_attr=True, create_using=nx.MultiDiGraph())

plt.figure(figsize=(12,12))
pos = nx.spring_layout(G, k = 0.5)
nx.draw(G, with_labels=True, node_color='skyblue', node_size=1500,
                       edge_cmap=plt.cm.Blues, pos = pos)
plt.show()

结论

总而言之,知识图谱已经成为人工智能和数据科学中一种强大而多功能的工具,用于表示结构化信息,实现高效的数据检索、推理和推断。在本文中,我们探讨了知识图谱在不同领域中的重要性和影响,并总结出以下要点:

  • 知识图谱以节点、边和属性的图形格式提供了信息的结构化表示。
  • 它们支持灵活的数据建模,无需固定的模式,可以从多个来源集成数据。
  • 知识图谱推理能够根据现有知识推断出新的事实和见解。
  • 应用领域涵盖自然语言处理、推荐系统和语义搜索引擎等。
  • 知识图谱嵌入将实体和关系表示为连续向量,可以在图上进行机器学习。

总而言之,知识图谱已经成为组织和理解大量相互关联信息的重要工具。随着研究和技术的进步,知识图谱无疑将在AI、数据科学、信息检索和决策系统等各个领域中发挥核心作用,塑造未来。

常见问题

本文中显示的媒体不归Analytics Vidhya所有,仅由作者自行决定使用。