知识图谱构建步骤:数据获取、知识抽取、关系提取、图数据库构建和语义搜索(本质就是把检索的问题转换为查询语句)
知识图谱实践:构建红楼梦人物关系知识图谱
1、获取数据和数据整理
'''
数据获取:http://www.360doc.com/content/16/1206/07/10310181_612337990.shtml
数据整理
'''
import re
hlm = open('hlm.txt','r+',encoding='utf8')
s=hlm.readlines()
s = [t.strip() for t in s if t.strip()!='']#去除空格
p = re.compile(r'([^\(]+):(.+)')
dict1 = {}#人物属性
for t in s:
ma = p.match(t)
if ma and len(ma.group(1))<10:
dict1[ma.group(1)] = ma.group(2)
dict1
{'七尼': '妙玉、智能、智通、智善、圆信、大色空、净虚。',
'七彩': '彩屏、彩儿、彩凤、彩霞、彩鸾、彩明、彩云。',
'五小': '小鹊、小红、小蝉、小舍儿、小螺。',
'五王': '王夫人、王熙凤、王子腾、王仁、薛姨妈。',
'十二丫环': '晴雯、麝月、袭人、鸳鸯、雪雁、紫鹃、碧痕、平儿、香菱、金钏、司棋、抱琴。',
'十二儿': '庆儿、昭儿、兴儿、隆儿、坠儿、喜儿、寿儿、丰儿、住儿、小舍儿、李十儿、玉柱儿。',
'十二官': '琪官、芳官、藕官、蕊官、药官、玉官、宝官、龄官、茄官、艾官、豆官、葵官。',……
p2 = re.compile(r'([^\(]+)--(.+)')
dict2 = {}#主仆关系
for t in s:
ma = p2.match(t)
if ma and len(ma.group(1))<10:
dict2[ma.group(1)] = ma.group(2)
dict2
{'史湘云': '翠缕、笑儿、篆儿。',
'林黛玉': '紫鹃、雪雁、春纤。',
'贾宝玉': '茗烟、袭人、晴雯。',
'贾惜春': '入画、彩屏、彩儿。',
'贾探春': '侍画、翠墨、小蝉。',
'贾迎春': '彩凤、彩云、彩云、彩霞'}
p3 = re.compile(r'.+[(府)(其他)]人物.*')
num = []
for t in range(len(s)):
ma = p3.match(s[t])
if ma:
num.append(t)
profiles = {}#人物简介
for n in range(len(num)):
begin = num[n]+1
if n<(len(num)-1):
end = num[n+1]
else:
end = len(s)
while begin<end:
if len(s[begin])<6:
profiles[s[begin]] = s[begin+1]
begin += 2
profiles
{'佩凤': '佩凤,贾珍之妾,年青姣憨之女子。',
'偕鸾': '偕鸾,贾珍之妾,年青姣憨之女子。',
'傻大姐': '傻大姐,贾母房内专做粗活的小丫头。她生得体肥面阔,两只大脚,做起粗活来很是爽利简捷。由于生性愚顽,一无知识,出言便使人发笑,贾母喜欢,便起名 “傻大姐”。正因为她有些弱智,去大观园玩时拾到一个五彩绣香囊,却不认得这是个春意儿,拿在手上只管瞧,才被邢夫人发现,引起了抄检大观园的轩然大波。在贾母等人实施“掉包计”时,她也不明白为什么她说了句“宝二爷要娶宝姑娘” 的话就被人打了一个嘴巴。她觉得委屈,跑到沁芳桥边呜呜咽咽地哭,被黛玉听见,询问何故,她就一一告诉,使黛玉听后如雷轰顶,惟求速死。',
'入画': '入画,贾惜春的丫环。因她父母在南方,她和哥哥只好跟随叔叔过日子,可叔叔婶婶只知道喝酒赌钱,她哥哥只好把做小厮所得的赏赐托人交给她保管,抄检大观园时被发现。贾惜春非要把她撵走,入画跪地哀求,百般苦告,尤氏只得叫人将入画带到宁府。',
'冷子兴': '冷子兴,周瑞家的女婿,都城中的古董商,和贾雨村是好朋友。他曾对贾雨村演说过荣国府。有一次卖古董和人打官司,便叫女儿来向荣国府讨情,周瑞家的求求凤姐就成了。',……
2、图数据库的建立
'''
图数据库的建立
'''
#Graph()实例化
from py2neo import Graph
graph = Graph("http://localhost:7474",user='neo4j',password='123')
from py2neo import Node, Relationship,size ,order
personSet = set()#存储所有的人物实体
for key, value in dict1.items():
v=re.sub(r'[和。]','、',value)#把和替换为、
lv = [t for t in v.split('、') if t!='']
for t in range(len(lv)):
p = re.compile(r'(.+)(.+')
ma = p.match(lv[t])
if ma:
lv[t] = ma.group(1)
if len(lv[t])!=1:
personSet.add(lv[t])
for key, value in dict2.items():
v=re.sub(r'[和。]','、',value)#把和替换为、
lv = [t for t in v.split('、') if t!='']
for t in range(len(lv)):
p = re.compile(r'(.+)(.+')
ma = p.match(lv[t])
if ma:
lv[t] = ma.group(1)
if len(lv[t])!=1:
personSet.add(lv[t])
for key, value in profiles.items():
personSet.add(key)
import pickle
with open('personSet.pkl','wb') as f:
pickle.dump(list(personSet),f)
with open('alias.pkl','wb') as f:
pickle.dump(list(dict1.keys()),f)
#创建人员实体
for t in personSet:
n = Node('Person', name=t)
graph.create(n)
#创建别名alias实体
for key, value in dict1.items():
n = Node('Alias', name=key)
graph.create(n)
#创建alias实体和person实体的关系Belong
for key, value in dict1.items():
v=re.sub(r'[和。]','、',value)#把和替换为、
lv = [t for t in v.split('、') if t!='']
for t in range(len(lv)):
p = re.compile(r'(.+)(.+')
ma = p.match(lv[t])
if ma:
lv[t] = ma.group(1)
graph.run('Match (p:Person {name:"'+lv[t]+'"}) Match (a:Alias {name:"'+key+'"}) CREATE (p)-[:Belong]->(a)')
#创建person实体和person实体的关系Own
for key, value in dict2.items():
v=re.sub(r'[和。]','、',value)#把和替换为、
lv = [t for t in v.split('、') if t!='']
for t in range(len(lv)):
p = re.compile(r'(.+)(.+')
ma = p.match(lv[t])
if ma:
lv[t] = ma.group(1)
graph.run('Match (p1:Person {name:"'+lv[t]+'"}) Match (p2:Person {name:"'+key+'"}) CREATE (p2)-[:Own]->(p1)')
#添加人物简介
for key, value in profiles.items():
graph.run('Match (p:Person {name:"'+key+'"}) SET p.profile="'+value+'"')



3、语义搜索
#Graph()实例化
from py2neo import Graph
graph = Graph("http://localhost:7474",user='neo4j',password='123')
from py2neo import Graph,Node,walk,Relationship,NodeSelector
#查询node
def find_node(graph,node_name,output=None):
s=NodeSelector(graph)
try:
if(output):
return output(s.select(name=node_name).first())
else:
return s.select(name=node_name).first()
except:
return '没有这个'+node_name+'节点,查询失败'
find_node(graph,'贾宝玉',dict)
#查询node的属性
def find_node_property(graph,node_name,props):
tmp=find_node(graph,node_name,output=dict)
if(type(props)==list):
result={}
for i in props:
try:
result[i]=tmp[i]
except:
print('输入的属性'+i+'有误,没有这个属性')
return result
else:
return tmp[props]
find_node_property(graph,'林黛玉','profile')
#查询与节点有特定关系的节点
def find_node_from_rela(graph,node_name,relationship):
node=find_node(graph,node_name)
tmp=[]
try:
for n in graph.match(start_node=node,rel_type=relationship):
tmp.append(n.end_node()["name"])
return tmp
except:
return '输入有误,查询失败'
find_node_from_rela(graph,'林黛玉','Own')
#查询两个节点的关系
def find_rela(graph,node_name_1,node_name_2):
node_1=find_node(graph,node_name_1)
node_2=find_node(graph,node_name_2)
try:
rel=graph.match_one(start_node=node_1,end_node=node_2)
return node_name_1+rel.type()+node_name_2
except:
try:
rel=graph.match_one(start_node=node_2,end_node=node_1)
return node_name_2+rel.type()+node_name_1
except:
return node_name_1+'与'+node_name_2+'没有关系'
print(find_rela(graph,'贾惜春','入画'))
print(find_rela(graph,'平儿','贾惜春'))
print(find_rela(graph,'金陵十二钗正册','贾惜春'))
#查询两个node之间是否存在特定关系
def is_node_rela(graph,node_name_1,node_name_2,rela):
tmp=find_rela(graph,node_name_1,node_name_2)
return rela in tmp
is_node_rela(graph,'史湘云','翠缕','Own')
import pickle
with open('personSet.pkl','rb') as f:
personSet = pickle.load(f)
with open('alias.pkl','rb') as f:
alias = pickle.load(f)
nodes = list(personSet)+list(alias)
#关系列表
relation=['Belong','Own']
#属性词表
propertys=['profile']
#同义词处理
own=['Own','拥有','主人','仆人','主仆关系']
belong=['Belong','属于']
synonym=[own,belong]
'''
同义词替换
'''
def synonym_replace(question,synonym):
replace_word=[]
for words in synonym:
for i in words:
if(i in question):
question=question.replace(i,words[0])
replace_word.append(i)
return question,replace_word
synonym_replace('袭人的主人是谁',synonym)
'''
查询语句的处理
'''
def get_node(s,nodes):
node=[h for h in nodes if h in s]
return node
print(get_node('袭人的主人是谁',nodes))
print(get_node('贾宝玉的别称是绛洞花王吗',nodes))
def get_rela(s,relationship):
rela=[r for r in relationship if r in s]
return rela
get_rela('袭人Belong什么',relation)
def get_property(s,propertys):
prop=[p for p in propertys if p in s]
return prop
get_property('贾迎春的profile',propertys)
'''
查询模板编写
'''
def search(s,syn,nodes,relation,propertys,graph,output=None):
node=get_node(s,nodes)
rela=get_rela(s,relation)
prop=get_property(s,propertys)
n=len(node)
r=len(rela)
p=len(prop)
if(all([n==1,r==0,p==0])):
return find_node(graph,node[0],output=output)
elif(all([n==1,r==0,p!=0])):
if(prop[0]=='property'):
tmp=find_node_property(graph,node[0],prop)['property'].split(' ')
for t in tmp:
res=[f for f in syn if f in t]
if(res):
return res
else:
return node[0]+'没有这个属性'
else:
return find_node_property(graph,node[0],prop)
elif(all([n==1,r==1,p==0])):
return find_node_from_rela(graph,node[0],rela[0])
elif(all([n==2,r==0,p==0])):
return find_rela(graph,node[0],node[1])
elif(all([n==2,r==1,p==0])):
return is_node_rela(graph,node[0],node[1],rela[0])
else:
return '查询的问题太复杂,暂时无能为力'
4、测试
search('贾宝玉的别称是绛洞花王吗','',nodes,relation,propertys,graph,output=None)
'贾宝玉Belong绛洞花王'
search('紫鹃和林黛玉是什么关系','',nodes,relation,propertys,graph,output=None)
'林黛玉Own紫鹃'
search('跪求贾探春的简介','',nodes,relation,propertys,graph,output=None)
(b5974f6:Person {name:"贾探春",profile:"贾探春,贾政与妾赵姨娘所生,排行为贾府三小姐。她精明能干,有心机,能决断,连王夫人与凤姐都让她几分,有“ 玫瑰花”之诨名。她的封建等级观念特别强烈,所以对处于婢妾地位的生母赵姨娘轻蔑厌恶,冷酷无情。抄检大观园时,她为了在婢仆面前维护作主子的威严,“令丫环秉烛开门而待”,只许别人搜自己的箱柜,不许人动一下她丫头的东西。“心内没有成算的”王善保家的,不懂得这一点,对探春动手动脚的,所以当场挨了一巴掌。探春对贾府面临的大厦将倾的危局颇有感触,她想用“兴利除弊”的微小改革来挽救,但无济于事。最后贾探春远嫁他乡。"})
网友评论