程序员必知:医疗知识图谱与自动问答

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 程序员必知:医疗知识图谱与自动问答

一、项目介绍

  本项目立足医药领域,以垂直型医药网站为数据来源,以疾病为核心,使用爬虫脚本data_spider.py,构建起一个包含7类规模为4.4万的知识实体,11类规模约30万实体关系的知识图谱。原始数据包含8000多种病,包括与肝病相关的有200多种病。

  本项目schema的设计根据所采集的结构化数据生成,对网页的结构化数据进行xpath解析,项目的数据存储采用Neo4j图数据库,并基于传统规则的方式完成了知识问答,并最终以cypher查询语句作为问答搜索sql,支持了问答服务。同时尝试把数据存储在mongodb上。项目源码已上传GitHub。

  基于规则的问答系统没有复杂的算法,一般采用模板匹配的方式寻找匹配度最高的答案,回答结果依赖于问句类型、模板语料库的覆盖全面性,面对已知的问题,可以给出合适的答案,对于模板匹配不到的问题或问句类型,经常遇到不合适的回答。整个问答系统的优劣依赖于知识图谱中知识的数量与质量,大多数知识图谱规模不足,主要原因还是数据来源以及技术上知识的抽取与推理困难。本项目中关于疾病的起因、预防等,实际返回的是一大段文字,这里其实可以引入事件抽取的概念,进一步将原因结构化表示出来。这个可以后面进行尝试。

  本项目将包括以下两部分的内容:

基于垂直网站数据的医药知识图谱构建

基于医药知识图谱的自动问答

二、项目效果

话不多少,直接上图。以下是实际问答运行过程中的截图:

三、项目运行方式

1、配置要求:要求配置neo4j数据库及相应的python依赖包。neo4j数据库用户名密码记住,并修改相应文件。

2、知识图谱数据导入:python build_medicalgraph.py,导入的数据较多,估计需要几个小时。

3、启动问答:python chat_graph.py

四、实现方案

一、医疗知识图谱构建

1.1 业务驱动的知识图谱构建框架

项目主要文件目录如下:

'''

├── QASystemOnMedicalKG

├── answer_search.py # 问题查询及返回

├── build_medicalgraph.py # 将结构化json数据导入neo4j

├── chatbot_graph.py # 问答程序脚本

├── QASystemOnMedicalKG/data

├── hepatopathy.json # 肝病知识数据

├── medical.json //代码效果参考:http://www.zidongmutanji.com/bxxx/167806.html # 全科知识数据

├── QASystemOnMedicalKG/dict

├── check.txt # 诊断检查项目实体库

├── deny.txt # 否定词库

├── department.txt # 医疗科目实体库

├── disease.txt # 疾病实体库

├── drug.txt # 药品实体库

├── food.txt # 食物实体库

├── producer.txt # 在售药品库

├── symptom.txt # 疾病症状实体库

├── QASystemOnMedicalKG/prepare_data

├── build_data.py # 数据库操作脚本

├── data_spider.py # 数据采集脚本

├── max_cut.py # 基于词典的最大前向///代码效果参考:http://www.zidongmutanji.com/bxxx/411846.html后向匹配

├── question_classifier.py # 问句类型分类脚本

├── question_parser.py # 问句解析脚本

'''

1.2 脚本目录

prepare_data/data_spider.py:数据采集脚本

prepare_data/build_data.py:数据预处理脚本

prepare_data/max_cut.py:基于词典的最大向前/向后切分脚本

build_medicalgraph.py:知识图谱入库脚本  

1.3 爬虫部分

爬虫部分我没有实际操作,简单看了一下源码。

数据来源为寻医问药网的疾病百科 。

爬取疾病介绍页的简介、病因、预防、症状、检查、治疗、并发症、饮食保健等详情页的内容。

爬虫模块使用的是urllib库,数据存在MongoDB数据库中。

其中并发症使用了自己写的max_cut匹配脚本中的双向最大向前匹配max_biward_cut。

1.4 医药领域知识图谱规模

1.4.1 neo4j图数据库存储规模

1.4.2 知识图谱实体类型

实体类型中文含义实体数量举例

Check

诊断检查项目

3,353

支气管造影;关节镜检查

Department

医疗科目

54

整形美容科;烧伤科

Disease

疾病

8,807

血栓闭塞性脉管炎;胸降主动脉动脉瘤

Drug

药品

3,828

京万红痔疮膏;布林佐胺滴眼液

Food

食物

4,870

番茄冲菜牛肉丸汤;竹笋炖羊肉

Producer

在售药品

17,201

通药制药青霉素V钾片;青阳醋酸地塞米松片

Symptom

疾病症状

5,998

乳腺组织肥厚;脑实质深部出血

Total

总计

44,111

约4.4万实体量级

1.4.3 知识图谱实体关系类型

实体关系类型中文含义关系数量举例

belongs_to

属于

8,844

common_drug

疾病常用药品

14,649

do_eat

疾病宜吃食物

22,238

drugs_of

药品在售药品

17,315

need_check

疾病所需检查

39,422

no_eat

疾病忌吃食物

22,247

recommand_drug

疾病推荐药品

59,467

recommand_eat

疾病推荐食谱

40,221

has_symptom

疾病症状

5,998

acompany_with

疾病并发疾病

12,029

Total

总计

294,149

约30万关系量级

(注意:belongs_to包括 科室属于科室 和 疾病属于科室 两种关系)

1.4.4 知识图谱属性类型

属性类型中文含义举例

name

疾病名称

喘息样支气管炎

desc

疾病简介

又称哮喘性支气管炎...

cause

疾病病因

常见的有合胞病毒等...

prevent

预防措施

注意家族与患儿自身过敏史...

cure_lasttime

治疗周期

6-12个月

cure_way

治疗方式

"药物治疗","支持性治疗"

cured_prob

治愈概率

95%

easy_get

疾病易感人群

无特定的人群

(注意:疾病的属性还包括cure_department)

知识库的构建是通过build_medicalgraph.py脚本实现。

二、基于医疗知识图谱的自动问答

2.1 技术架构

问答系统完全基于规则匹配实现,通过关键词匹配,对问句进行分类,医疗问题本身属于封闭域类场景,对领域问题进行穷举并分类,然后使用cypher的match去匹配查找neo4j,根据返回数据组装问句回答,最后返回结果。

2.2 脚本结构

question_classifier.py:问句类型分类脚本

question_parser.py:问句解析脚本

chatbot_graph.py:问答程序脚本

chatbot_graph.py

首先从需要运行的chatbot_graph.py文件开始分析。

该脚本构造了一个问答类ChatBotGraph,定义了QuestionClassifier类型的成员变量classifier、QuestionPase类型的成员变量parser和AnswerSearcher类型的成员变量searcher。

class ChatBotGraph:

def init(self):

self.classifier = QuestionClassifier()

self.parser = QuestionPaser()

self.searcher = AnswerSearcher()

该问答类的成员函数仅有一个chat_main函数

chat_main函数

首先传入用户输入问题,调用self.classifier.classify进行问句分类,如果没有对应的分类结果,则输出模板句式。如果有分类结果,则调用self.parser.parser_main对问句进行解析,再调用self.searcher.search_main查找对应的答案,如果有则返回答案,如果没有则输出模板句式。

def chat_main(self, sent):

answer = '您好,我是问答小助手,希望可以帮到您。祝您身体棒棒!'

res_classify = self.classifier.classify(sent)

if not res_classify:

return answer

res_sql = self.parser.parser_main(res_classify)

final_answers = self.searcher.search_main(res_sql)

if not final_answers:

return answer

else:

return '\n'.join(final_answers)

question_classifier.py

该脚本构造了一个问题分类的类QuestionClassifier,定义了特征词路径、特征词、领域actree、词典、问句疑问词等成员变量。

特征词除了7类实体还包括由全部7类实体词构成的领域词region_words、否定词库deny_words。

构建领域actree通过调用self.build_actree实现。

构建词典通过调用self.build_wdtype_dict()实现。

问句疑问词包含了疾病的属性和边相关的问题词,参考上文中问答系统支持的问答类型。

build_actree函数

该函数构建领域actree,加速过滤。通过python的ahocorasick库实现。

ahocorasick是一种字符串匹配算法,由两种数据结构实现:trie和Aho-Corasick自动机。

Trie是一个字符串索引的词典,检索相关项时时间和字符串长度成正比。

AC自动机能够在一次运行中找到给定集合所有字符串。AC自动机其实就是在Trie树上实现KMP,可以完成多模式串的匹配。

def build_actree(self, wordlist):

actree = ahocorasick.Automaton() # 初始化trie树

for index, word in enumerate(wordlist):

actree.add_word(word, (index, word)) # 向trie树中添加单词

actree.make_automaton() # 将trie树转化为Aho-Corasick自动机

return actree

build_wdtype_dict函数

该函数根据7类实体构造 {特征词:特征词对应类型} 词典。

wd_dict = dict()

for wd in self.region_words:

wd_dict【wd】 = 【】

if wd in self.disease_wds:

wd_dict【wd】.append('disease')

...

check_medical函数

通过ahocorasick库的iter()函数匹配领域词,将有重复字符串的领域词去除短的,取最长的领域词返回。功能为过滤问句中含有的领域词,返回{问句中的领域词:词所对应的实体类型}。

def check_medical(self, question):

region_wds = 【】

for i in self.region_tree.iter(question): # ahocorasick库 匹配问题 iter返回一个元组,i的形式如(3, (23192, '乙肝'))

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) # stop_wds取重复的短的词,如region_wds=【'乙肝', '肝硬化', '硬化'】,则stop_wds=【'硬化'】

final_wds = 【i for i in region_wds if i not in stop_wds】 # final_wds取长词

final_dict = {i:self.wdtype_dict.get(i) for i in final_wds}

return final_dict

check_word函数

该函数检查问句中是否含有某实体类型内的特征词。

def check_words(self, wds, sent):

for wd in wds:

if wd in sent:

return True

return False

classify函数

该函数为分类主函数。

首先调用check_medical函数,获取问句中包含的领域词及其所在领域,并收集问句当中所涉及到的实体类型;

接着基于特征词进行分类,即调用check_word函数,看问句中是否包含某领域特征词,以及该领域是否在问句中包含的region_words的实体类型(types)里,以此来判断问句属于哪种类型。

# 症状

if self.check_words(self.symptom_qwds, question) and ('disease' in types):

question_type = 'disease_symptom'

question_types.append(question_type)

if self.check_words(self.symptom_qwds, question) and ('symptom' in types):

question_type = 'symptom_disease'

question_types.append(question_type)

#已知食物找疾病

if self.check_words(self.food_qwds+self.cure_qwds, question) and 'food' in types:

deny_status = self.check_words(self.deny_words, question)

if deny_status:

question_type = 'food_not_disease'

else:

question_type = 'food_do_disease'

question_types.append(question_type)

如果没有查到若没有查到相关的外部查询信息,且类型为疾病,那么则将该疾病的描述信息返回(question_types = 【'disease_desc'】);若类型为症状,那么则将该症状的对应的疾病信息返回(question_types = 【'symptom_disease'】)。

然后将分类结果进行合并处理,组装成一个字典返回。

注意:

食物相关的问题需要检查否定词self.deny_words来判断是do_eat还是not_eat。

已知食物找疾病和已知检查项目查相应疾病的时候,check_words需要加上self.cure_qwds。

question_parser.py

问句分类后需要对问句进行解析。

该脚本创建一个QuestionPaser类,该类包含三个成员函数。

build_entitydict函数

例如:从分类结果的{'args': {'头痛': 【'disease', 'symptom'】}, 'question_types': 【'disease_cureprob'】}中获取args,返回{'disease': 【'头痛'】, 'symptom': 【'头痛'】}的形式。

sql_transfer函数

该函数真的不同的问题类型,转换为Cypher查询语言并返回。

# 查询疾病的原因

if question_type == 'disease_cause':

sql = 【"MATCH (m:Disease) where m.name = '{0}' return m.name, m.cause".format(i) for i in entities】

# 查询疾病的忌口

elif question_type == 'disease_not_food':

sql = 【"MATCH (m:Disease)-【r:no_eat】->(n:Food) where m.name = '{0}' return m.name, r.name, n.name".format(i) for i in entities】

注意:

查询可能为查询中心疾病节点的属性,也可能为查询关联边。

疾病的并发症需要双向查询。

建议吃的东西包括do_eat和recommand_eat两种关联边。

查询药品相关记得扩充药品别名,包括common_drug和recommand_durg两种关联边。

parser_main函数

该函数为问句解析主函数。

首先传入问句分类结果,获取问句中领域词及其实体类型。

接着调用build_entitydict函数,返回形如{'实体类型':【'领域词'】,...}的entity_dict字典。

然后对问句分类返回值中【‘question_types’】的每一个question_type,调用sql_transfer函数转换为neo4j的Cypher语言。

最后组合每种question_type转换后的sql查询语句。

answer_search.py

问句解析之后需要对解析后的结果进行查询。

该脚本创建了一个AnswerSearcher类。与build_medicalgraph.py类似,该类定义了Graph类的成员变量g和返回答案列举的最大个数num_list。

该类的成员函数有两个,一个查询主函数一个回复模块。

search_main函数

传入问题解析的结果sqls,将保存在queries里的【‘question_type’】和【‘sql’】分别取出。

首先调用self.g.run(query).data()函数执行【‘sql’】中的查询语句得到查询结果,

再根据【‘question_type’】的不同调用answer_prettify函数将查询结果和答案话术结合起来。

最后返回最终的答案。

answer_prettify函数

该函数根据对应的qustion_type,调用相应的回复模板。

elif question_type == '<span style="color: r

相关文章
|
4月前
|
机器学习/深度学习 人工智能 算法
人工智能在医疗诊断中的革命性作用
【8月更文挑战第5天】随着人工智能技术的飞速发展,其在各个领域的应用正逐步深入。特别是在医疗行业,AI的介入正在改变传统的诊疗模式,提高医疗服务的效率和质量。本文将探讨AI技术在医疗诊断中的具体应用,包括图像诊断、病理分析以及基于大数据的疾病预测模型,旨在揭示AI技术如何助力医生进行更精准的诊断和治疗决策。
|
3月前
|
机器学习/深度学习 人工智能 搜索推荐
探究人工智能在医疗健康中的应用与挑战
本文深入探讨了人工智能(AI)在医疗健康领域中的应用及其所面临的挑战。随着科技的不断进步,AI技术在医疗领域的应用日益广泛,从疾病诊断、治疗方案制定到患者护理等方面都展现出巨大的潜力。然而,尽管AI在医疗健康领域取得了显著成果,但也面临着数据隐私、算法偏见和伦理道德等方面的挑战。本文将详细介绍这些应用和挑战,并探讨可能的解决方案,以期为读者提供对AI在医疗健康领域发展的全面理解。
|
4月前
|
机器学习/深度学习 人工智能 搜索推荐
【人工智能】人工智能在医疗健康中的应用以及实际案例和进展概述
人工智能(Artificial Intelligence, AI)在医疗健康领域的应用日益广泛,为医疗服务的提升和健康管理带来了革命性的变化。以下是人工智能在医疗健康中的主要应用
1335 1
|
6月前
|
机器学习/深度学习 传感器 人工智能
人工智能在环境保护中的作用与价值有哪些?
人工智能技术在环境保护中发挥着重要的作用,可以提高环境监测和管理的效率,帮助解决环境问题,预测环境变化趋势,提高公众环保意识。我们应该加大人工智能技术在环境保护领域的投入和应用,共同保护好我们的环境,让地球变得更加美丽和宜居。
122 4
|
7月前
|
人工智能 自然语言处理 数据可视化
AI日报:2024年世代人工智能将如何改变业务
AI日报:2024年世代人工智能将如何改变业务
AI日报:2024年世代人工智能将如何改变业务
|
人工智能
《以人工智能或无人化、自动化技术影响世界》
《以人工智能或无人化、自动化技术影响世界》
|
传感器 人工智能 监控
以人工智能或无人化、自动化技术影响世界
以人工智能或无人化、自动化技术影响世界
124 0
|
机器学习/深度学习 人工智能 自然语言处理
人工智能和时尚的变化:人工智能如何为零售商和购物者设计优秀可能性
人工智能使企业能够理解消费者行为并根据其行为采取行动。数以百计的数据点被考虑在内,从而得出有价值的见解,可以指导零售商更好地制定销售策略,提高客户体验。
164 0
人工智能和时尚的变化:人工智能如何为零售商和购物者设计优秀可能性
|
机器学习/深度学习 传感器 人工智能
人工智能如何改变预测性维护
建筑物和工厂中性能不佳的厂房和设备可能会在开始时使用过多的能源,如果不及时维护,最终会导致故障。因此,通过像我们这样的智能技术来提醒这些问题,不仅可以节省能源成本,还可以减少所需的能源,减少过程中的碳排放。预测性维护还可以减少建筑的停工时间,在业主和占用者的维修成本变得昂贵之前,问题就已经解决了。
人工智能如何改变预测性维护
|
机器学习/深度学习 传感器 数据采集
人工智能影响医疗保健行业的12个方式
如今的医疗保健行业已经十分成熟,可以进行一些重大变革。从慢性病和癌症到放射学和风险评估,医疗保健行业似乎有着无数的机会利用技术在患者护理方面部署更精确、高效和有效的干预措施。
125 0