基于知识图谱的问答系统构建流程

文章目录
  1. 1. 问题类型分类
  2. 2. 问题解析
  3. 3. 查找回答
  4. 4. 问答控制

KGQA是个有意思的东西,之前复现了刘老师的项目,现在自己在另外一个领域也做了类似的工作,故在这里记录如何基于知识图谱来构建一个完整的问答系统。

标题即问答系统构建的处理顺序。

问题类型分类

1. 存储特征词
在生成知识图谱的过程中,我们保存了图谱中的一系列结点词放在 txt 文件中,以医疗知识图谱为例:

所有文件中保存下来的词就是问题中出现会用到得特征词,特征词有两种处理:
(1)每类特征词分别存储为一个 list 类型的变量;
(2)将所有的特征词全部汇总存储为一个较大的 list 变量。

注意:上述两种操作都需要完成。

2. 构建actree
构建actree需要的就是第一步中所有特征词的汇总
这其中会用到之前安装中差点把本人逼疯的ahocorasick库,这属于多匹配模式中的经典算法,简称AC自动机。
多模式匹配就是有多个模式串P1,P2,P3…,Pm,求出所有这些模式串在连续文本T1…n中的所有可能出现的位置。
参考来源:python ahocorasick介绍
构建actree的目的就是为了识别问题串中的关键词:

1
2
3
4
5
6
def build_actree(wordlist):
actree=ahocorasick.Automaton()
for index,word in enumerate(wordlist):
actree.add_word(word,(index,word))
actree.make_automaton()
return actree

3. 构建特征词类型的词典
这个操作的意义就是使用dict类型的数据来标识清楚每个特征词的种类。
举个例子:

1
{'拜仁慕尼黑':'team_name'}

从上述字典中清晰的就能看出词语对应的类型是什么,比如拜仁慕尼黑就是球队名字。

4. 构造问句疑问句
QA系统肯定是一问一答,所以需要根据不同类型的问题来建立相应的list。

tips:由于之前已经生成了特征词表,所以只需要根据每种特征词可能的问法来构建疑问句即可。
例子:以球队为例,球队的特征词就包含了建立时间,主教练,主场等,所以就要分别对应建立时间,主教练,主场这三个问题进行提问。
关键词可以后期不断补充。

5. 问句过滤
这里就需要用到之前通过AC自动机生成的actree。
使用其提供的 iter 函数来得出问题中的特征词,将关键词加入到一个 list 中备用,同时需要生成停用词。
停用词的判断标准,这里举个例子说明,假如关键词列表中有 wd1 和 wd2 两个词,如果 wd2 包含了 wd1,同时 wd1 不等于 wd2,那么就把 wd1 加入停用词,因为 wd2 就能代表wd1。
最后,去掉关键词列表中所有的停用词,再生成一个词典,字典包含了关键词及其类型。
可以参考先前医疗知识图谱的构建部分:

6. 问题分类
上面已经完成了基本的判断部分,这部分着重说明如何根据上面的条件来进行分类。
(1)存储经问句过滤得到的dict
问句过滤完之后会返回一个包含了问题特征词及特征词类别的dict,这里重新再定义一个 dict,使用键值 args 来存储先前返回的 dict。
(2)收集问句中涉及的特征词类型并存储到定义的 types list中
(3)使用 if 来判断问题类型
a.判断问题中是否包含之前疑问句中设置的疑问词

1
2
3
4
5
def check_words(self,wds,sent):
for wd in wds:
if wd in sent:
return True
return False

b.判断问题类型是否在(2)中收集的问题类型中
若以上两点同时满足,则将问题类型定义后返回。

如果没有查找到问题的类型则将问题类型定义为 other.
在(1)中重新定义的dict中加入一个键值 question_types,对应的值为先前设定的问题类型。
参考医疗知识图谱,其他问题类型这里不再展开。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def classify(self,question):
data={}
medical_dict=self.check_medical(question)
if not medical_dict:
return {}
data['args']=medical_dict

# 收集问句当中涉及到得实体类型
types=[]
for type_ in medical_dict.values():
types+=type_
# 定义问题类型
question_type='others'

question_types=[]
# 症状 : disease 对应的是疾病的名称
if self.check_words(self.symptom_qwds,question) and ('disease' in types):
question_type='disease_symptom'
question_types.append(question_type)

# 若没有查到相关的外部查询信息,那么则将该疾病的描述信息返回
if question_types == [] and 'disease' in types:
question_types = ['disease_desc']

# 将多个分类结果进行合并处理,组装成一个字典
data['question_types'] = question_types
return data

问题解析

1. 构造实体节点
数据生成来源于 上述问题分类 的结果,根据设定的参数取出其中的特征值和特征值对应的类型重新构造实体dict。

2. 针对不同类型的问题构造sql查询
以拜仁慕尼黑为例,已经确认问题类型是 查询球队的建立时间:

1
MATCH (m:Team_Name)-[]-(n:Build_Time) WHERE m.name='拜仁慕尼黑' RETURN n.name

假如存在多个特征词,就通过循环来构造一个sql问题查询的list,将生成的sql语句返回和问题对应的类型整合为一个新的dict,最后封装在一个list返回.
完整操作可以查看医疗知识图谱的 question_parser.py。
指路:question_parser.py

查找回答

1.配置 neo4j 链接
查找回答需要在图数据库中查找,所以需要先配置数据库的连接。

2. 根据问题解析中生成的sql在数据库中进行查询
将查询得到的结果作为最终答案返回。

问答控制

1. 导入问题分类块
2. 导入问题解析块
3. 导入查找回答块
4. 进入问答环节
(1)获取问题分类,若查询不到问题分类,回答:不知道;
(2)根据得到的分类对问题进行解析;
(3)根据解析得到的sql语句在图数据库中进行查询,查询无果则回答:不知道;反之,显示答案。