300行python代码从零开始构建基于知识图谱的电影问答系统5-答案获取

啦啦啦,终于快写完了,虽然我也知道并没有写什么实质性的东西,至少我坚持下来啦,后面再慢慢多拧拧,少一些水分。

在上一篇中,主要介绍了如何从接收到的用户问题中抽取关键信息,以及如何识别用户的意图,那么接下来就将介绍在得到了这些信息后,如何在知识图谱中查询答案。我在处理这个问题时,想得很直接,简单来说,每个问题模板就对应了一个用户意图,那么就按照每个意图来写查询语句,这是一种简单粗暴的方法,优点就是就只有简单了,缺点当然很多了,比如不利于维护以及扩展等。现在要有维护扩展的意识,但是具体实现是,如果想快速实现一个demo的话,可以使用这种简单粗暴方法。下面就对获取答案进行介绍。

首先,我定义了一个问题模板的方法字典,每一个key对应模板的编号,value就是根据该模板来查询答案的方法,结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
self.q_template_dict={
0:self.get_movie_rating,
1:self.get_movie_releasedate,
2:self.get_movie_type,
3:self.get_movie_introduction,
4:self.get_movie_actor_list,
5:self.get_actor_info,
6:self.get_actor_act_type_movie,
7:self.get_actor_act_movie_list,
8:self.get_movie_rating_bigger,
9:self.get_movie_rating_smaller,
10:self.get_actor_movie_type,
11:self.get_cooperation_movie_list,
12:self.get_actor_movie_num,
13:self.get_actor_birthday
}

当我们得到用户问题,以及该问题的模板后需要做那些工作呢?
首先时利用上一篇介绍的方法来获取问题的关键信息,然后就根据模板编号来调用相应的方法,整体框架的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def get_question_answer(self,question,template):
# 如果问题模板的格式不正确则结束
assert len(str(template).strip().split("\t"))==2
template_id,template_str=int(str(template).strip().split("\t")[0]),str(template).strip().split("\t")[1]
self.template_id=template_id
self.template_str2list=str(template_str).split()

# 预处理问题
question_word,question_flag=[],[]
for one in question:
word, flag = one.split("/")
question_word.append(str(word).strip())
question_flag.append(str(flag).strip())
assert len(question_flag)==len(question_word)
self.question_word=question_word
self.question_flag=question_flag
self.raw_question=question
# 根据问题模板来做对应的处理,获取答案
answer=self.q_template_dict[template_id]()
return answer

进入到对应的方法中,利用Cypher语言来构建查询语句,其基本形式是:

1
match(n)-[r] -(b)

Cypher语言不太懂没关系,也可以直接使用python的库py2neo来操作图数据库neo4j。这里只涉及到查询操作,所以我直接构造了Cypher查询语句,然后使用py2neo库的run方法来查询,数据库的链接和查询我单独的写了一个类,在需要的时候调用即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from py2neo import Graph,Node,Relationship,NodeMatcher

class Query():
def __init__(self):
self.graph=Graph("http://localhost:7474", username="neo4j",password="123456")

# 问题类型0,查询电影得分
def run(self,cql):
# find_rela = test_graph.run("match (n:Person{name:'张学友'})-[actedin]-(m:Movie) return m.title")
result=[]
find_rela = self.graph.run(cql)
for i in find_rela:
result.append(i.items()[0][1])
return result

比如这里以查询某部电影的演员列表为例,查询答案的代码为:

1
2
3
4
5
6
7
8
9
10
11
# 4:nm 演员列表
def get_movie_actor_list(self):
movie_name=self.get_movie_name()
cql = f"match(n:Person)-[r:actedin]->(m:Movie) where m.title='{movie_name}' return n.name"
print(cql)
answer = self.graph.run(cql)
answer_set = set(answer)
answer_list = list(answer_set)
answer = "、".join(answer_list)
final_answer = movie_name + "由" + str(answer) + "等演员主演!"
return final_answer

主要是构造Cypher语句,然后调用py2neo的run方法,对得到的答案进行处理这几步,得到答案返回即可。

这个系列这里也就差不多了吧,除了目录,也只有4篇,所以这并不是一个很详细的教程,主要是想帮助大家对代码的理解,理清思路或者扰乱思路。在做试验的时候,我就是先理清思路再写的,这或许是一个不错的方法,由于这个教程不是很详细,大家有被我搅晕的地方,可以给我发邮件(irvingbei@qq.com )或者直接评论,也可以去网上找找资料。最后,这也是一个python版本,很好上手,大家不妨试一试。