教程:https://zhuanlan.zhihu.com/p/243211697
前面都进行的好好的,具体的理论因为我已经了解一点了,就不在赘述,教程里面有,直接开始实践,
前面都没啥问题,直接上代码
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")
在这里因为之前的教程里面的内容数据下载不到,就自己复制了一些,
因为原来读入进去有两列,是因为自己的文件不是csv的,现在,直接做一个excel,另存为--其他格式,然后就可以保存为csv文件了,读入进去就正常很多了。
因为这里文件没法上传到,所以就给大家看看。还是很简单能做出来的。。。
达到了预期效果,我们对数据集中的句子使用这个函数,提取这些句子中的实体对:
好像和正确的不太一样
原来是这样,因为我的数据只有五句话,没有人家的多,所以根本没有十个到二十个,所以为空,接着我把他调小,就可以了,看起来还行,
原文中还有,
“如你所见,在这些实体对中有一些代词,如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")
没得问题了。
用在我们的数据集上:
对了人家的数据有名字sentence所以是这样的:
relations = [get_relation(i) for i in tqdm(candidate_sentences['sentence'])]
pd.Series(relations).value_counts()[:50]
但是我是自己做的,所以没有名字,都把这个名字删掉就行。
relations = [get_relation(i) for i in tqdm(candidate_sentences)]
pd.Series(relations).value_counts()[:2]
但是这个函数可能需要改改,因为我的数据及的结果只有一个is
以下是我的数据集,可以看出来绝对不止一个is是动词,
可能 因为作者的数据集都是主谓宾这样的形式,但是我们的还加了前置和倒装什么的,不只是主谓宾,还有主谓的,他就识别不好,
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()
但是因为作者的数据集比较大,所以还需要重新提出来看,但是我的这个哈哈哈就不需要了,太少了实在是。
END!!!!