python构建知识图谱_Python从零开始构建知识图谱
他人的智慧结晶,《—heaven's stone, by the giants' feet》》,以巨人的肩膀为起点起步,在科研探索的路上需要借助东风助力前行。为此我们特意整理了丰富的资源包——包含代码链接、数据集、软件及相关技术资料等——并开设了《—heaven's stone》专题栏目,《heaven's stone》《heaven's stone, by the giants' feet》助力你在科研征途上稳扎稳打地前行,请持续关注!
作者:知乎—wxj630
地址:https://www.zhihu.com/people/wxj630
- 知识 indexer 是数据科学领域中最引人入胜的概念之一。
- 掌握构建知识 indexer 的方法论框架是从维基百科页面中提取相关信息。
- 通过 Python 实践,我们将利用 spaCy 这一流行工具库来实际构建一个完整的知识 indexer。
01
知识图谱
1、什么是知识图谱
It is possible to conceptualize a graph as comprising nodes and edges. Knowledge graphs are structured as triplets composed of nodes and edges. Node A and Node B represent distinct entities. Nodes are connected by edges that represent relationships between them, also referred to as triples.

例如头实体“普京”和尾实体“俄罗斯”的关系是“是总统”:

还可以增加“普京在克格勃工作过”的三元组:

还可以增加“俄罗斯是APEC组织成员”的三元组:

识别实体和它们之间的关系对我们来说相对不具挑战性。现有的有监督命名实体识别(NER)和关系抽取模型都已较为完善。然而,在构建大规模的标注数据集方面仍需耗费大量的人力物力。因此作为初学者我们通常会采用以下一系列NLP技术来实现实体识别、关系抽取以及知识图谱构建:句子分割依赖解析词性标注与实体识别等技术
2、句子分割Sentence Segmentation
构建知识图谱的第一步是将电子文档或文章分解为独立的句子。随后,在这些句子中筛选出仅包含一个主语和一个谓语的短语性句,并将其整合为完整的段落结构。为了便于后续分析与处理,请参考以下示例文本:
印度网球选手Sumit Nagal在最新男子单打排名中实现了从排名135向129的新突破。
在以下四个句子中我们选择其中的第二和第四个句子因为它们各自都拥有独立的主谓结构在第二个句子中1994年出生的小将作为主体其主要赛事领域为ATP巡回赛系列中的年轻球员竞技而在第四个句子当中其主要选手由印度尼西亚选手Nagal主导其首盘比赛成绩成为整个赛事的关注焦点

然而我们无法对每一个句子进行人工抽取因此必须采用实体识别和关系抽取技术
3、实体识别Entities Recognition
为了实现抽取实体的目标,在知识图谱上定义为节点
import spacynlp = spacy.load('en_core_web_sm')doc = nlp("The 22-year-old recently won ATP Challenger tournament.")for tok in doc: print(tok.text, "...", tok.dep_)'''输出:The … det22-year … amod– … punctold … nsubjrecently … advmodwon … ROOTATP … compoundChallenger … compoundtournament … dobj. … punct'''
通过依赖解析器分析后发现这句话中的主语(nsubj)属于old。这不是我们希望提取的实体。我们希望将'22-year' 作为一个目标实体进行提取。其依赖标签为amod属性表明这是一个属于old的修饰词。因此我们需要制定一条规则用于提取这些实体。这条新规则包括以下几点:首先应分别提取主题/对象及其相应的修饰符;其次应记录两者之间的标点符号;然后应关注句子中的宾语(dobj)部分;具体而言,在此例中仅包含锦标赛这一单一信息而不具备明显的修饰关系;因此我们可以将上述描述稍作修改:不仅需要分别识别主题/对象及其相关的合成词成分;还需同时记录两者之间的标点符号;这样一来我们就成功地获得了两个关键信息22-year-old 和ATP Challenger tournament
4、关系抽取Extract Relations
然后我们需要提取关系,也就是知识图谱上的“边”:

你能推断出这两个句子中主语和宾语的关系吗?这两句话具有相同的动词形式won。我们来看看如何提取这些关系?我们将再次使用依赖解析。
doc = nlp("Nagal won the first set.")for tok in doc: print(tok.text, "...", tok.dep_)'''输出:Nagal … nsubjwon … ROOTthe … detfirst … amodset … dobj. … punct'''
为了解析这种关系的本质,我们必须深入研究其语义结构,并准确识别其核心成分——句子的核心动词。从而我们得以确定这一关系所对应的‘won’标记。提取出的实体-关系如下:

02
知识图谱Python实践 基于维基百科文章的电影集合开发了一个新的知识图谱系统。我们系统性地收集了约4300个经过精炼的文本片段,并将它们组织成一个个完整的知识节点与关系链路。每个文本片段都由两个实体组成:主语和宾语。欢迎访问此资源以获取所有提取的文本片段
1、导入相关库Import Libraries
import reimport pandas as pdimport bs4import requestsimport spacyfrom spacy import displacynlp = spacy.load('en_core_web_sm')from spacy.matcher import Matcher from spacy.tokens import Span import networkx as nximport matplotlib.pyplot as pltfrom tqdm import tqdmpd.set_option('display.max_colwidth', 200)%matplotlib inline
2、读取文本数据Read Data
# import wikipedia sentencescandidate_sentences = pd.read_csv("wiki_sentences_v2.csv")candidate_sentences.shape'''(4318, 1)'''
`` 采样看下数据长啥样:
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_)
``

只有单一的核心部分(process)与标准(standard)之间存在关联。其他人可采取类似的策略来审查其他段落。
3、抽取"主语-宾语"对Entity Pairs Extraction
在维基百科句子中出现的节点即为所指实体。边则代表了这些实体之间的相互连接关系。
我们采用无监督的方法来提取这些元素。
也就是说,在分析过程中我们主要关注的是如何识别并处理这些问题。
然而,
红葡萄酒这类跨多词表达的存在会带来一定的挑战。
依赖关系解析器通常仅标注单个单词作为主语或宾语。
因此,
在下文我们将设计一个辅助函数来解决这一问题。
def get_entities(sent): ## chunk 1 # 我在这个块中定义了一些空变量。prv tok dep和prv tok text将分别保留句子中前一个单词和前一个单词本身的依赖标签。前缀和修饰符将保存与主题或对象相关的文本。 ent1 = "" ent2 = "" prv_tok_dep = "" # dependency tag of previous token in the sentence prv_tok_text = "" # previous token in the sentence prefix = "" modifier = "" ############################################################# for tok in nlp(sent): ## chunk 2 # 接下来,我们将遍历句子中的记号。我们将首先检查标记是否为标点符号。如果是,那么我们将忽略它并转移到下一个令牌。如果标记是复合单词的一部分(dependency tag = compound),我们将把它保存在prefix变量中。复合词是由多个单词组成一个具有新含义的单词(例如“Football Stadium”, “animal lover”)。 # 当我们在句子中遇到主语或宾语时,我们会加上这个前缀。我们将对修饰语做同样的事情,例如“nice shirt”, “big house” # if token is a punctuation mark then move on to the next token if tok.dep_ != "punct": # check: token is a compound word or not if tok.dep_ == "compound": prefix = tok.text # if the previous word was also a 'compound' then add the current word to it if prv_tok_dep == "compound": prefix = prv_tok_text + " "+ tok.text # check: token is a modifier or not if tok.dep_.endswith("mod") == True: modifier = tok.text # if the previous word was also a 'compound' then add the current word to it if prv_tok_dep == "compound": modifier = prv_tok_text + " "+ tok.text ## chunk 3 # 在这里,如果令牌是主语,那么它将作为ent1变量中的第一个实体被捕获。变量如前缀,修饰符,prv tok dep,和prv tok文本将被重置。 if tok.dep_.find("subj") == True: ent1 = modifier +" "+ prefix + " "+ tok.text prefix = "" modifier = "" prv_tok_dep = "" prv_tok_text = "" ## chunk 4 # 在这里,如果令牌是宾语,那么它将被捕获为ent2变量中的第二个实体。变量,如前缀,修饰符,prv tok dep,和prv tok文本将再次被重置。 if tok.dep_.find("obj") == True: ent2 = modifier +" "+ prefix +" "+ tok.text ## chunk 5 # 一旦我们捕获了句子中的主语和宾语,我们将更新前面的标记和它的依赖标记。 # update variables prv_tok_dep = tok.dep_ prv_tok_text = tok.text ############################################################# return [ent1.strip(), ent2.strip()]
试下这个函数的效果:
get_entities("the film had 200 patents")'''Output: [‘film’, ‘200 patents’]'''
实现了预期目标,并通过该函数处理数据集内的句子以识别实体对:
entity_pairs = []for i in tqdm(candidate_sentences["sentence"]): entity_pairs.append(get_entities(i))entity_pairs[10:20]

观察到,在这些实体对中存在一些代词,并非仅仅局限于we、it、she等具体形式。我们的目标是通过引入专有名词或名词来进行替代操作。或许我们可以优化get entities()函数以实现对代词的过滤功能?然而需要注意的是,指代消解是一种较为复杂的技术,在此阶段我们暂且将其保留现状,并继续推进关系提取工作
4、关系抽取Relation / Predicate Extraction
我们假设谓语在句中扮演主要角色。例如,在句子中选取1929年上映的60部好莱坞音乐剧时发现这一现象:动词‘在’被使用,并且这正是我们所寻求的核心元素——这些三元组中的一个核心元素。下面介绍的方法能够有效地提取这些三元组并进行分析。为此目的,我们采用了spaCy基于规则模式识别技术来实现这一目标。
def get_relation(sent): doc = nlp(sent) # Matcher class object matcher = Matcher(nlp.vocab) #define the pattern 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),该模式会考察其后是否接有介词(prep)或代指符号(demonstrative)。如果是,则将其附加在主干之后。试试一下这个函数:
该模式旨在识别句子中的主语成分或多义动作用以表示状态的对象性特征和行为性质的主体性特征。当一个实体被描述为"具有...性"时, 人们往往认为它是被描述者所拥有的某种属性或者状态, 或者说是被描述者所从事的一种活动, 或者说是被描述者所经历的一种情境
get_relation("John completed the task")'''Output: completed'''
`` 用在我们的数据集上:
relations = [get_relation(i) for i in tqdm(candidate_sentences['sentence'])]pd.Series(relations).value_counts()[:50]
``
`

`
改写说明
5、构建知识图谱Build a Knowledge Graph
最后, 我们将从提取出来的实体(主宾关系对)和关系(实体之间的联系)创建知识图谱. 让我们建立一个主体与关系的数据框:
# extract subjectsource = [i[0] for i in entity_pairs] # extract objecttarget = [i[1] for i in entity_pairs] kg_df = pd.DataFrame({'source':source, 'target':target, 'edge':relations})
接下来,我们将基于networkx库从这个dataframe构建一个网络图。每个节点代表一个实体,在这种设置下,边或连接将直接反映两个实体之间的互动关系。这表明这种网络结构是一个有向图:这样的连接仅是从一个节点指向另一个节点的关系。例如,“约翰吃意大利面”:
# create a directed-graph from a dataframeG=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()

实体和关系太多了,我们抽“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) # k regulates the distance between nodesnx.draw(G, with_labels=True, node_color='skyblue', node_size=1500, edge_cmap=plt.cm.Blues, pos = pos)plt.show()

这样就清晰多了
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 ”关系:在这一图表中我可以观察到许多有趣的事实例如在这一图表中可以观察到两种情况一种是“几部动作恐怖电影发行于1980年代”一种是“pk在4844块荧幕上”。这些都是事实本身它们向我们展示了如何从文本中提取这些信息

03
总结 本文探讨了基于三元组形式的知识抽取方法,并在此基础上构建了相应的Knowledge Graph。然而,在本研究中我们仅考虑包含两个实体的语句作为处理对象。即便如此,在这种限制下仍能构建出非常有价值的Knowledge Graph。展望未来,在海量非结构化数据中挖掘隐含知识的能力将成为关键!!! 参考连接
Knowledge Graph – A Robust Data-Driven Science Methodology for Extracting Knowledge from Text (with Python code)
利用Spacy构建知识图谱文本的方法是什么?这篇博客文章详细解释了如何利用Spacy构建知识图谱文本的方法与步骤。知识图谱构建过程可以分解成几个关键步骤:第一步是建立实体及其关系的知识库;第二步是从给定文本中提取实体及其关系;第三步是将提取到的信息与知识库进行关联;最后一步则是基于关联结果生成完整的知识图谱结构。
[2] spacy文档:
https://github.com/explosion/spaCy
[3] spacy中文教程:
https://www.jianshu.com/p/e6b3565e159d

