源码可以看刘焕庸老师的GitHub
一、爬取数据
二、建立图谱(build_medicalgraph.py)
1. 连接neo4j数据库
1. 连接neo4j数据库
通过py2neo来链接与操作数据库
g=Graph("http://localhost:7474",username="neo4j",password="**********")
2. 读取文件
将结构化json数据导入neo4j中,观察数据可知
(1)实体类型有药品(drug)、食物(food)、诊断检查项目(check)、医疗科室(department)、在售药品(producer)、疾病(disease)、疾病症状(symptom);
(2)实体关系类型有属于、疾病常用药品、疾病宜吃食物、药品在售药品、疾病所需检查、疾病忌吃食物、疾病推荐食物、疾病推荐食谱、疾病症状、疾病并发疾病;
(3)属性类型有疾病名称、疾病简介、疾病原因、预防措施、治疗周期、治疗方式、治愈概率、疾病易感人群。
json文件中保存的数据都是以疾病为中心的,每条数据都记录着某个疾病的所有属性信息。首先从文件中读取 json,通过json.load方法将其转化成Python字典,然后遍历每个疾病的“字段名:值”键值对中的信息,对其中的“值”创建相关实体节点集合,通过梳理数据间的关系,利用 “字段名”创建实体间的关系及其属性的集合,遍历完所有疾病数据后,就可以构建知识三元组,得到医疗问答系统所需的数据。对于json里的字典键,如果是疾病的属性,则加入disease_dict中(比如desc);如果和疾病有关系,则加入对应的关系列表(比如acompany);如果是某个其他实体,则加入对应的实体列表(比如(check);
对数据进行处理,最后函数返回set去重后的所有实体、疾病属性信息和实体间关系。
3. 创建图谱节点和边
定义一个MedicalGraph类,创建它的对象,然后用create_graphnodes()函数创建图谱节点,用create_graphrels()函数创建图谱关系边。
(1)用create_graphnodes()函数创建图谱节点:
首先调用read_nodes函数得到存储实体和实体间关系的变量,然后创建有属性的节点(疾病)和没有属性的节点(比如食物,药品等等),对于每一个疾病属性都进行添加后 使用g.create(node)创建
(2)用create_graphrels()函数创建实体关系边:
最后创建两个实体之间的关系(比如食物与疾病之间的推荐食谱、忌吃;疾病与科室之间的所属关系等等),首先调用read_nodes函数得到存储实体和实体间关系的变量,再直接调用定义好的create_relationship()函数,在create_relationship中创建关联边的时候首先是用set()函数进行去重处理,防止有重复的关系,接着调用py2neo库中Graph类的run函数,对于关联边的两个实体用Cypher语言直接执行语句表示出来,对每一对实体关系在neo4j里创建边即可。
4.导出数据,保存起来。
将抽取出的各类实体保存在对应的txt文件中,为下文健康问答系统的设计做准备。
三、问答系统(chatbot_graph.py)
问答系统完全基于规则匹配实现,通过关键词匹配,对问句进行分类,医疗问题本身属于封闭域类场景,对领域问题进行穷举并分类,然后使用Cypher的match去匹配查找neo4j,根据返回数据组装问句回答,最后返回结果。首先传入用户输入问题,调用self.classifier.classify进行问句分类,如果没有对应的分类结果,则输出模板句式。如果有分类结果,则调用self.parser.parser_main对问句进行解析,再调用self.searcher.search_main查找对应的答案,如果有则返回答案,如果没有则输出模板句式。
1. 问句分类(question_classifier.py)
(1)首先加载特征词,特征词除了7类实体还包括由全部7类实体词构成的领域词region_words、否定词库deny_words,构造问句疑问词其中包含了疾病的属性和边相关的问题词,
构造领域树actree,可以加速问题分类的过滤通过python的ahocorasick库实现,ahocorasick是一种字符串匹配算法,由两种数据结构实现:trie和Aho-Corasick自动机。trie是一个字符串索引的词典,检索相关项时时间和字符串长度成正比;AC自动机能够在一次运行中找到给定集合所有字符串。
build_wdtype_dict函数根据7类实体构造{特征词:特征词对应类型}的词典。
(2)对问句进行过滤,通过ahocorasick库的iter()函数匹配领域词,将有重复字符串的领域词去除短的,取最长的领域词返回。功能为过滤问句中含有的领域词,返回{问句中的领域词:词所对应的实体类型}。
check_word函数检查问句中是否含有某实体类型内的特征词。
(3)分类主函数:
将问题输入进来,首先调用check_medical()来对问句进行过滤,获取问句中包含的领域词及其所在领域,并收集问句当中所涉及到的实体类型;接着基于特征词进行分类,即调用check_word函数,看问句中是否包含某领域特征词,以及该领域是否在问句中包含的region_words的实体类型里,以此来判断问句属于哪种类型。
2.问句解析(question_paser.py)
从分类结果中已经得到了问题涉及到的领域词和实体类型,接着调用build_entitydict函数,返回形如{'实体类型':['领域词'],...}的entity_dict字典。然后对问句分类返回值中[‘question_types’]的每一个问题类型,调用sql_transfer函数转换为neo4j的Cypher语言。
3.答案搜索(answer_searcher.py)
首先就是连接neo4j,接着进行cypher查询,将问句解析模块中得到的语句输入到搜索主函数中,调用g.run( )函数来搜索答案,接着根据问题类型和搜索的答案调用相应的回复模板,最后返回最终的答案;如果没有找到答案,则返回设定好的语句。



