python从零开始构建知识图谱笔记

2021-01-05  本文已影响0人  jiarf

教程:https://zhuanlan.zhihu.com/p/243211697
前面都进行的好好的,具体的理论因为我已经了解一点了,就不在赘述,教程里面有,直接开始实践,
前面都没啥问题,直接上代码

image.png
image.png
import re
import pandas as pd
import numpy as np
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
# import wikipedia sentences
candidate_sentences = pd.read_csv("wiki_sentences_v2.csv",header=0)
candidate_sentences.shape

candidate_sentences.sample(7)

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


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

好了接下来重点在这里,接下来的一个函数,怎么也对不起行列,出来的答案为空,很奇怪

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")

这里上面教程那个函数的空格对其是有点问题好像,,换这个函数就行:https://gist.github.com/prateekjoshi565/6833da973d65338216d0f6b99755d120,韩被我改了好久,真的是!!!(主要还是自己比较菜)
对的格式是这个:

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")
image.png

在这里因为之前的教程里面的内容数据下载不到,就自己复制了一些,


image.png

因为原来读入进去有两列,是因为自己的文件不是csv的,现在,直接做一个excel,另存为--其他格式,然后就可以保存为csv文件了,读入进去就正常很多了。
因为这里文件没法上传到简书,所以就给大家看看。还是很简单能做出来的。。。

达到了预期效果,我们对数据集中的句子使用这个函数,提取这些句子中的实体对:

image.png image.png

好像和正确的不太一样


image.png

原来是这样,因为我的数据只有五句话,没有人家的多,所以根本没有十个到二十个,所以为空,接着我把他调小,就可以了,看起来还行,
原文中还有,


image.png

“如你所见,在这些实体对中有一些代词,如we, it, she等。我们希望用专有名词或名词来代替。也许我们可以进一步改进get entities()函数来过滤代词。但是指代消解是比较高级的技术,现在,让我们让它保持原样,继续到关系提取部分。”
所以这个后来在看看指代消解技术。

关系抽取Relation / Predicate Extraction

“句子中捕获这样的谓词。在这里,我使用了spaCy的基于规则的匹配”



绝望了,这篇老是对不齐,但是这句还好不难对齐

#抽提句子关系,,V
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)

函数中定义的模式试图找到句子中的词根或主要动词。一旦确定了词根,该模式就会检查它后面是介词(prep)还是代理词。如果是,则将其添加到根词中。试试一下这个函数:

get_relation("John completed the task")
image.png

没得问题了。
用在我们的数据集上:
对了人家的数据有名字sentence所以是这样的:

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


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

但是我是自己做的,所以没有名字,都把这个名字删掉就行。


image.png
relations = [get_relation(i) for i in tqdm(candidate_sentences)]

pd.Series(relations).value_counts()[:2]
image.png

但是这个函数可能需要改改,因为我的数据及的结果只有一个is
以下是我的数据集,可以看出来绝对不止一个is是动词,


image.png

可能 因为作者的数据集都是主谓宾这样的形式,但是我们的还加了前置和倒装什么的,不只是主谓宾,还有主谓的,他就识别不好,

5、构建知识图谱Build a Knowledge Graph

最后,我们将从提取的实体(主语-宾语对)和谓词(实体之间的关系)创建知识图。

让我们创建一个实体和谓词的dataframe:

# extract subject
source = [i[0] for i in entity_pairs] 
# extract object
target = [i[1] for i in entity_pairs] 


kg_df = pd.DataFrame({'source':source, 'target':target, 'edge':relations})  
# create a directed-graph from a dataframe
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()

但是因为作者的数据集比较大,所以还需要重新提出来看,但是我的这个哈哈哈就不需要了,太少了实在是。


image.png

END!!!!

上一篇下一篇

猜你喜欢

热点阅读