基于中医药知识图谱智能问答(二)

鸣谢:该项目基于刘焕勇老师、IrvingBei这两位的代码启发下,才有了我这么一个辣鸡项目。期间我的学业导师,给了我很多指导帮助。站在前人的肩膀上,我们可以看得更远

前文我们已经构建了知识图谱,我们知道知识图谱的一个重要应用就是智能问答,接下来我们要基于中医药知识图谱完成智能问答系统的搭建
基于知识图谱的智能问答就是依托一个大型知识库(知识图谱、结构化数据库等),将用户的自然语言问题转化成结构化查询语句,直接从知识库中导出用户所需的答案。对于智能问答的关键在于处理用户提出的自然语言,在学术界常用研究方法可分为三大类:基于语义解析、基于信息检索、基于概率模型。项目伊始采用基于条件随机概率模型,但是经过测试发现其效率低下,严重影响用户体验。依据刘焕勇老师的模板匹配设计了中医药问答的模板,模板匹配特别适合用于小规模的垂直领域的知识图谱问答系统。

系统思路

  1. 依据特征词进行分类
  2. 基于分类进一步分析,并生成查询语句
  3. 基于查询语句在neo4j进行查询,并对返回的数据进行处理
    基于中医药知识图谱智能问答(二)_第1张图片

代码重点详解

1. 基于AC自动机多模匹配算法处理自然语言问句
  在自然语言处理过程中,最为关键的步骤就是实体提取和意图分类。中文相比于英文不太容易去区分词与词的界限,没有了空格的分割,加大了自然语言处理对语句分割、语义理解上的困难。在多模式匹配算法中,Aho-Corasick 算法(Aho-Corasick automaton algorithm)是最经典的算法,通过将模式串构成Trie树,将主串匹配的过程变为在 Trie 树上转移的过程,其时间复杂度是 O(N+N×M),自动机状态转移规则放在Trie树上进行搜索匹配,能对目标串进行高效式搜索增大了主串和模式串匹配的可能性,可以有效地提高处理自然问句的效率,因此本研究决定基于AC自动机算法处理自然语言问句。利用知识图谱中存在的实体名字构成特征库(语料库)。建立AC树(Aho-Corasick Tree),利用AC自动机算法,匹配问句中是否存在特征词,即查询问句中是否存在知识图谱实体名字,来实现实体提取。AC自动机用于在输入的一串字符串中匹配有限组“字典”中的子串。它与普通字符串匹配的不同点在于同时与所有字典串进行匹配。算法均摊情况下具有近似于线性的时间复杂度,约为字符串的长度加所有匹配的数量。
2. 模板匹配分析自然语言问句
   分析自然语言问句是整个智能问答的核心,其实质上是一个分类问题。为了满足对自然语言问答速度的需要,本研究采用模板匹配分析自然语言问句。首先基于规则的方法,依据经验构建数种疑问句关键词字典,再根据这些关键字是否存在于问句来确定问句的类型。比如“腊雪的药性是什么”可以通过模板提取出(药性)特征词。由AC自动机,得到实体类型。根据二者组合成知识子图,并在知识图谱中查询对应的结果。由于医学领域较为严肃,要确保数据的准确性,本研究人工构建了中医药中文问句集(如表3所示)来支持智能问答的实现。

                                                             表3 中医药领域问句部分样本集

序号 模板问题 特征词
1 n属于在本草纲目中什么部类 属于、部类、什么部…
2 n的药性是什么 药性、气味、有无毒…
3 n的别称是什么 别称、别名、其他称号…
4 n有那些基础药方 基础药方、如何食用…
5 m该去那个科室看病 科室、看病、去哪里看…
6 m属于什么类别病症 类别、病症…
7 m的经验药方是什么 经验药方、如何治疗…
import os
import ahocorasick   #是一种多匹配的模块,在这里用于匹配问句里面的特征词
class QuestionClassifier:
    def __init__(self):
        #加载特征词路径
        self.part_path=r'E:\前端\中医药\data\part.txt'
        self.name_path=r'E:\前端\中医药\data\name.txt'
        self.alias_path=r'E:\前端\中医药\data\alias.txt'
        self.smell_path=r'E:\前端\中医药\data\smell.txt'
        self.cure_path=r'E:\前端\中医药\data\cure.txt'
        self.drug_path=r'E:\前端\中医药\data\drug.txt'
        self.department_path=r'E:\前端\中医药\data\Department.txt'
        self.prescript_path=r'E:\前端\中医药\data\prescript.txt'
        self.onepart_path=r'E:\前端\中医药\data\onepart.txt'
        #加载特征词
        self.part_wds=[i.strip() for i in open(self.part_path,encoding="utf-8") if i.strip()]
        self.name_wds = [i.strip() for i in open(self.name_path, encoding="utf-8") if i.strip()]
        self.alias_wds = [i.strip() for i in open(self.alias_path, encoding="utf-8") if i.strip()]
        self.smell_wds = [i.strip() for i in open(self.smell_path, encoding="utf-8") if i.strip()]
        self.cure_wds = [i.strip() for i in open(self.cure_path, encoding="utf-8") if i.strip()]
        self.drug_wds = [i.strip() for i in open(self.drug_path, encoding="utf-8") if i.strip()]
        self.department_wds = [i.strip() for i in open(self.department_path, encoding="utf-8") if i.strip()]
        self.prescript_wds = [i.strip() for i in open(self.prescript_path, encoding="utf-8") if i.strip()]
        self.onepart_wds = [i.strip() for i in open(self.onepart_path, encoding="utf-8") if i.strip()]
        # 创建了包含9类实体特征词的元素集
        self.region_words = set(self.part_wds + self.name_wds + self.alias_wds+ self.smell_wds+ self.cure_wds+self.department_wds+self.onepart_wds+self.prescript_wds+self.drug_wds)
        # 构造领域actree
        self.region_tree = self.build_actree(list(self.region_words))
        # 构建词典
        self.wdtype_dict = self.build_wdtype_dict()
        # 问句疑问词,对于不同的问题,赋予特征词
        self.belong_qwds = ['属于什么部类', '什么部', '部类', '哪个部']  # 询问部类
        self.smell_qwds = ['什么气味','什么味道','闻起来怎么样','什么味的','吃起来苦嘛','吃了是什么味','什么品质','有毒嘛','有毒性嘛']
        self.alias_qwds = ['有其他别名嘛','别名是什么','还有其他称呼','别称','别名','还可以怎么叫','其他名字','那些别称']
        self.cure_qwds = ['疗法', '咋治', '咋吃',  '如何食用','食疗方法','可以治什么','治那些病','可以治什么症状','有那些食用方法','如何食用','最佳食用方法','有什么好处', '有什么益处', '有何益处', '用来', '用来做啥', '用来作甚', '需要','治愈啥', '主治啥', '主治什么', '有什么用', '有何用']
        self.prescript_qwds = ['该怎么治疗','如何治疗','有什么秘方','有什么偏方','咋治','医治方式']
        self.department_qwds = ['该去什么科室','去那个科室看病','应当去那个科室治疗','科室']
        self.onepart_qwds = ['从属什么类秘方','隶属什么秘方']
        print('问答系统启动中......')
        #print(self.wdtype_dict)
        return
    def build_wdtype_dict(self):
        # 该函数是检查问句中涉及的5类实体,并返回一个列表
        wd_dict = dict()
        for wd in self.region_words:
            wd_dict[wd] = []
            if wd in self.part_wds:
                wd_dict[wd].append('part')
            if wd in self.name_wds:
                wd_dict[wd].append('name')
            if wd in self.alias_wds:
                wd_dict[wd].append('alias')
            if wd in self.smell_wds:
                wd_dict[wd].append('smell')
            if wd in self.cure_wds:
                wd_dict[wd].append('cure')
            if wd in self.prescript_wds:
                wd_dict[wd].append('prescript')
            if wd in self.department_wds:
                wd_dict[wd].append('department')
            if wd in self.onepart_wds:
                wd_dict[wd].append('onepart')
            if wd in self.drug_wds:
                wd_dict[wd].append('drug')
        return wd_dict

    '''基于特征词进行分类'''

    def check_words(self, wds, sent):
        for wd in wds:
            if wd in sent:
                return True
        return False
#检查是否有实体类型的特征词
    '''构造actree,加速过滤'''

    def build_actree(self, wordlist):
        # 往actree中添加数据,这是已经封装好的模块
        actree = ahocorasick.Automaton()
        for index, word in enumerate(wordlist):
            actree.add_word(word, (index, word))
        actree.make_automaton()
        return actree

    '''构造词对应的类型'''
    def check_medical(self, question):
        #该模块是通过匹配找到问句中存在的5类实体
        region_wds = []
        #iter()是迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退
        for i in self.region_tree.iter(question):#对问句进行多匹配模式的迭代
            wd = i[1][1]
            region_wds.append(wd)
        stop_wds = []
        for wd1 in region_wds:
            for wd2 in region_wds:
                if wd1 in wd2 and wd1 != wd2:
                    stop_wds.append(wd1)
        final_wds = [i for i in region_wds if i not in stop_wds]
        final_dict = {i : self.wdtype_dict.get(i) for i in final_wds}

        return final_dict
    '''分类主函数'''

    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 = []
        #  这是对遍历选择的方式,依次判断问句中是否存在实体,以及问句中实体类型是否在types中
        #  属于什么部类
        if self.check_words(self.belong_qwds, question) :
            question_type = 'name_part'
            question_types.append(question_type)
        #print(question_types)
        # #别名
        if self.check_words(self.alias_qwds,question) :
            question_type = 'name_alias'
            question_types.append(question_type)
        #气味品质
        if self.check_words(self.smell_qwds,question) :
            question_type = 'name_smell'
            question_types.append(question_type)
        #主治方法
        if self.check_words(self.cure_qwds,question) :
            question_type = 'name_cure'
            question_types.append(question_type)
        #科室
        if self.check_words(self.department_qwds,question):
            question_type = 'drug_department'
            question_types.append(question_type)
        #药方
        if self.check_words(self.prescript_qwds,question):
            question_type = 'drug_prescript'
            question_types.append(question_type)
        #疾病属于什么秘方类
        if self.check_words(self.onepart_qwds,question):
            question_type = 'drug_onepart'
            question_types.append(question_type)
        # 若没有查到相关的外部查询信息,那么则将该疾病的描述信息返回
        if question_types == [] :
            question_types = ['找不到']
        # 将多个分类结果进行合并处理,组装成一个字典
        data['question_types'] = question_types
        return data





if __name__ == '__main__':
    handler = QuestionClassifier()
    while 1:
        question = input('输入您的问题:')
        data = handler.classify(question)
        print(data)
        #{'args': {'腊雪': ['name']}, 'question_types': ['name_part']}

该项目已经上传到我的github,还是老样子,别忘了给我点star,点star,点star(重要的事说三边)
另:上传的是1.0版本,今年我又修改了web框架,由web.py框架修改为flask框架,并且已经部署到我的个人网站上,后期会出一期如何部署pythonweb在服务器的教程,敬请期待;我和我那优秀的学业导师在这此项目基础上进行了深度研究,研究成果已成功被CCC2020录用,等EI收录后我会把论文上传,供大家学习

你可能感兴趣的:(知识图谱)