博客 大数据知识图谱——基于知识图谱+深度学习的大数据(KBQA)NLP医疗知识问答可视化系统(1)

大数据知识图谱——基于知识图谱+深度学习的大数据(KBQA)NLP医疗知识问答可视化系统(1)

   数栈君   发表于 2024-04-17 10:25  49  0

一、项目概述
知识图谱是将知识连接起来形成的一个网络。由节点和边组成,节点是实体,边是两个实体的关系,节点和边都可以有属性。知识图谱除了可以查询实体的属性外,还可以很方便的从一个实体通过遍历关系的方式找到相关的实体及属性信息。

基于知识图谱+flask的KBQA医疗问答系统基于医疗方面知识的问答,通过搭建一个医疗领域知识图谱,并以该知识图谱完成自动问答与分析服务。 基于知识图谱+flask的KBQA医疗问答系统以neo4j作为存储,本系统知识图谱建模使用的最大向前匹配是一种贪心算法,从句首开始匹配,每次选择最长的词语。由于只需一次遍历,因此在速度上相对较快。 算法相对简单,容易实现和理解,不需要复杂的数据结构。 对于中文文本中大部分是左向的情况,最大向前匹配通常能够较好地切分。与最大向前匹配相反,最大向后匹配从句尾开始匹配,每次选择最长的词语。适用于大部分右向的中文文本。双向最大匹配结合了最大向前匹配和最大向后匹配的优势,从两个方向分别匹配,然后选择分词数量较少的一种结果。这种方法综合考虑了左向和右向的特点,提高了切分的准确性,以关键词执行cypher查询,并返回相应结果查询语句作为问答。后面我又设计了一个简单的基于 Flask 的聊天机器人应用,利用nlp自然语言处理,通过医疗AI助手根据用户的问题返回结果,用户输入和系统返回的输出结果都会一起自动存储到sql数据库。后面又封装了深度学习模型完成一个完整知识图谱问答可视化系统。
具体详情见另一篇文章:大数据知识图谱之深度学习——基于BERT+LSTM+CRF深度学习识别模型医疗知识图谱问答可视化系统

在多模式匹配方面, Aho-Corasick算法专门用于在一个文本中同时搜索多个模式(关键词)。相比于暴力搜索算法,Aho-Corasick算法的时间复杂度较低,在本知识图谱建模问答系统中,性能更为显著。在线性时间复杂度方面,进行预处理的阶段,Aho-Corasick算法构建了一个确定性有限自动机(DFA),使得在搜索阶段的时间复杂度为O(n),其中n是待搜索文本的长度。这种线性时间复杂度使得算法在本应用中非常高效。在灵活性方面, Aho-Corasick算法在构建有限自动机的过程中,可以方便地添加、删除模式串,而不需要重新构建整个数据结构,提高了算法的灵活性和可维护性。在医疗领域中,针对与医疗相关的疾病、症状、药物、问诊等内容进行系统性的内容定义,也能够通过命名实体识别来对药剂、价格、收款等内容进行定义,这种定义的方式可以通过精准匹配的方式来进行实体边界的识别,并且可以实现对边界的正确标记。命名实体识别的方法主要有三种方式,第一种是通过利用规则法来进行规则的人工编写;第二种是通过HMM、CRF等模型来进行机器学习模板订制;第三种是通过神经网络的方式以LSTM、RNN等算法来进行特征的提取。

二、实现知识图谱的医疗知识问答系统基本流程
配置好所需要的环境(jdk,neo4j,pycharm,python等)

爬取所需要的医学数据,获取所需基本的医疗数据。

对医疗数据进行数据清洗处理。

基于贪心算法进行分词策略。

关系抽取定义与实体识别。

知识图谱建模。

基于 Aho-Corasick算法进行多模式匹配。

设计一个基于 Flask 的聊天机器人AI助手。

设计用户输入和系统输出记录数据自动存储到sql数据库。

封装深度学习模型完整系统——基于BERT+LSTM+CRF深度学习识别模型医疗知识图谱问答可视化系统

三、项目工具所用的版本号
Neo4j版本:Neo4j Desktop1.4.15;
neo4j里面医疗系统数据库版本:4.4.5;
Pycharm版本:2021;
JDK版本:jdk1.8.0_211;
MongoDB版本:MongoDB-windows-x86_64-5.0.14;
flask版本:3.0.0
Django版本:3.2.3

四、所需要软件的安装和使用
(一)安装JAVA
1.下载java安装包:
官网下载链接:https://www.oracle.com/java/technologies/javase-downloads.html


本人下载的版本为JDK-1.8,JDK版本的选择一定要恰当,版本太高或者太低都可能导致后续的neo4j无法使用。

安装好JDK之后就要开始配置环境变量了。 配置环境变量的步骤如下:
右键单击此电脑—点击属性—点击高级系统设置—点击环境变量

在下方的系统变量区域,新建环境变量,命名为JAVA_HOME,变量值设置为刚才JAVA的安装路径,我这里是C:\Program Files\Java\jdk1.8.0_211

编辑系统变量区的Path,点击新建,然后输入 %JAVA_HOME%\bin

打开命令提示符CMD(WIN+R,输入cmd),输入 java -version,若提示Java的版本信息,则证明环境变量配置成功。

2.安装好JDK之后,就可以安装neo4j了
2.1 下载neo4j
官方下载链接:https://neo4j.com/download-center/#community
也可以直接下载我上传到云盘链接:
Neo4j Desktop Setup 1.4.15.exe
链接:https://pan.baidu.com/s/1eXw0QjqQ9nfpXwR09zLQHA?pwd=2023
提取码:2023

打开之后会有一个自己设置默认路径,可以根据自己电脑情况自行设置,然后等待启动就行了

打开之后我们新建一个数据库,名字叫做:“基于医疗领域的问答系统”

详细信息看下图:

数据库所用的是4.4.5版本,其他数据库参数信息如下:

五、项目结构整体目录
├── pycache \\编译结果的保存目录

│ │ ├── answer_search.cpython-39.pyc

│ │ ├── question_classifier.cpython-39.pyc

│ │ │── 基于问句解析.cpython-39.pyc

│ │ │── 基于问句解析结果查询.cpython-39.pyc

│ │ │── 基于问题分类.cpython-39.pyc

├── data\本项目的数据

│ └── medical.json \本项目的数据,通过build_medicalgraph.py导neo4j

├── dict

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

│ ├── deny.txt \否定词词库

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

│ ├── disease.txt \疾病实体库

│ ├── drug.txt \药品实体库

│ ├── food.txt \食物实体库

│ ├── producer.txt \在售药品库

│ └── symptom.txt \疾病症状实体库

├── prepare_data \爬虫及数据处理

│ ├──__pycache__

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

│ ├── data_spider.py \网络资讯采集脚本

│ └── max_cut.py \基于词典的最大向前/向后脚本

│ ├── 处理后的medical数据.xlsx

│ ├── MongoDB数据转为json格式数据文件.py

│ ├──从MongoDB导出的medical.csv

│ ├──data.json #从MongoDB导出的json格式数据

├── static \静态资源文件

├── templates

│ ├──index.html \问答系统前端UI页面

├── app.py \启动flask问答AI脚本主程序

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

├── answer_search.py \基于问题答复脚本

├── build_medicalgraph.py \构建医疗图谱脚本

├── chatbot_graph.py \医疗AI助手问答系统机器人脚本

├── question_parser.py [\基于问句解析脚本](file://基于问句解析脚本)

├── 删除所有关系.py \可有可无的脚本文件

├── 删除关系链.py \可有可无的脚本文件

├── 两节点新加关系.py \可有可无的脚本文件

├── 交互 匹配所有节点.py \可有可无的脚本文件

这里chatbot_graph.py脚本首先从需要运行的chatbot_graph.py文件开始分析。
该脚本构造了一个问答类ChatBotGraph,定义了QuestionClassifier类型的成员变量classifier、QuestionPase类型的成员变量parser和AnswerSearcher类型的成员变量searcher。

question_classifier.py脚本构造了一个问题分类的类QuestionClassifier,定义了特征词路径、特征词、领域actree、词典、问句疑问词等成员变量。question_parser.py问句分类后需要对问句进行解析。该脚本创建一个QuestionPaser类,该类包含三个成员函数。
answer_search.py问句解析之后需要对解析后的结果进行查询。该脚本创建了一个AnswerSearcher类。与build_medicalgraph.py类似,该类定义了Graph类的成员变量g和返回答案列举的最大个数num_list。该类的成员函数有两个,一个查询主函数一个回复模块。

问答系统框架的构建是通过chatbot_graph.py、answer_search.py、question_classifier.py、question_parser.py等脚本实现。

五、系统实现
数据的抓取与存储
安装MongoDB数据库:
MongoDB官方下载地址:
https://www.mongodb.com/try





安装完成:



然后我们进行MongoDB的环境配置:
在变量Path里加入E:\MongoDB\bin

打开终端(cmd)输入mongod --dbpath E:\MongoDB\data\db


在浏览器输入

127.0.0.1:27017
1
查看MongoDB服务是否启动成功:


安装好MongoDB之后开始爬取数据:
数据来源于寻医问药网:http://jib.xywy.com/



具体的疾病详情页面如下:
首先对网址上的疾病链接进行分析,以感冒为例:
感冒的链接:http://jib.xywy.com/il_sii_38.htm


可以看到,上面包含了疾病的简介、病因、预防、症状、检查、治疗、并发症、饮食保健等详情页的内容。下面我们要使用爬虫把信息收集起来。要收集 url 下面对应的数据,具体爬虫代码如下:
之前老版本的insert方法被弃用,再用会出现警告。insert 替换为了 insert_one,这样就不会再收到关于 insert 方法被弃用的警告了。

'''基于寻医问药的医疗数据采集'''
# 使用 insert_one 或 insert_many 方法。提供了更多的灵活性,并且支持更多的功能,比如插入后返回的文档的 _id 值。
class MedicalSpider:
def __init__(self):
self.conn = pymongo.MongoClient()
self.db = self.conn['medical2']
self.col = self.db['data']

def insert_data(self, data):
# 使用 insert_one 方法插入单个文档
self.col.insert_one(data)
1
2
3
4
5
6
7
8
9
10
11
根据url,请求html

用于获取指定 URL 的 HTML 内容的函数,使用了 Python 的 requests 库。在这个代码中,设置请求的头部信息(User-Agent),代理信息(proxies),然后使用 requests.get 方法获取页面内容,并指定了编码为 ‘gbk’。

'''根据url,请求html'''
def get_html(self,url):
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'
}
proxies = {
'http': None,
'https': None
}
html = requests.get(url=url, headers=headers, proxies=proxies)
html.encoding = 'gbk'
return html.text
1
2
3
4
5
6
7
8
9
10
11
12
爬取医疗相关的信息,包括疾病描述、疾病预防、疾病症状、治疗方法等。

url_parser 函数

def url_parser(self, content):
selector = etree.HTML(content)
urls = ['http://www.anliguan.com' + i for i in selector.xpath('//h2[@class="item-title"]/a/@href')]
return urls
1
2
3
4
url_parser 函数函数的作用是解析传入的 HTML 内容,提取出页面中疾病相关的链接。具体来说:

etree.HTML(content): 使用 lxml 库中的 etree 模块将 HTML 内容转换成可被 XPath 解析的对象。
selector.xpath('//h2[@class="item-title"]/a/@href'): 使用 XPath 选择器提取所有 <h2> 标签中 class 属性为 "item-title" 的子节点 <a> 的 href 属性,得到的是一组相对链接。
['http://www.anliguan.com' + i for i in ...]: 将相对链接转换成完整的链接,拼接在 'http://www.anliguan.com' 前面。
这个函数的作用是解析传入的 HTML 内容,提取出页面中疾病相关的链接。具体来说:

spider_main函数是主要的爬虫逻辑,循环遍历页面,爬取不同类型的医疗信息,并将结果存储到数据库中

'''url解析'''
def url_parser(self,content):
selector=etree.HTML(content)
urls=['http://www.anliguan.com'+i for i in selector.xpath('//h2[@class="item-title"]/a/@href')]
return urls

'''主要的爬取的链接'''
def spider_main(self):
# 收集页面
for page in range(1,11000):
try:
basic_url='http://jib.xywy.com/il_sii/gaishu/%s.htm'%page # 疾病描述
cause_url='http://jib.xywy.com/il_sii/cause/%s.htm'%page # 疾病起因
prevent_url='http://jib.xywy.com/il_sii/prevent/%s.htm'%page # 疾病预防
symptom_url='http://jib.xywy.com/il_sii/symptom/%s.htm'%page #疾病症状
inspect_url='http://jib.xywy.com/il_sii/inspect/%s.htm'%page # 疾病检查
treat_url='http://jib.xywy.com/il_sii/treat/%s.htm'%page # 疾病治疗
food_url = 'http://jib.xywy.com/il_sii/food/%s.htm' % page # 饮食治疗
drug_url = 'http://jib.xywy.com/il_sii/drug/%s.htm' % page #药物

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
basic_url、symptom_url、food_url、drug_url 和 cause_url 是根据 page 变量构建的不同 URL 地址,用于访问和爬取不同类型信息的网页。
- data 是一个字典,用于存储从不同 URL 中获取的数据。
2. 对不同 URL 进行爬取和信息提取:
- data['basic_info'] = self.basicinfo_spider(basic_url):调用 basicinfo_spider 方法,从 basic_url 获取基础信息,并将其存储在 data 字典的 'basic_info' 键下。
- data['symptom_info'] = self.symptom_spider(symptom_url):类似地,获取症状信息并存储在 'symptom_info' 键下。
- data['food_info'] = self.food_spider(food_url):获取饮食信息并存储在 'food_info' 键下。
- data['drug_info'] = self.drug_spider(drug_url):获取药物信息并存储在 'drug_info' 键下。
- data['cause_info'] = self.cause_spider(cause_url):获取病因信息并存储在 'cause_info' 键下。
3. 打印和数据库存储:
- print(page, basic_url):打印当前页面和基础 URL,用于调试和跟踪程序执行过程。
- print(data):打印从不同 URL 获取的数据,同样用于调试和查看程序执行结果。
- self.col.insert_one(data):将获取的数据存入 MongoDB 数据库中。
4. 异常处理:
- except Exception as e::捕获所有类型的异常,并将异常对象赋值给变量 e。
- print(e, page):在捕获到异常时打印异常信息和页面信息,以便于调试和追踪出错的原因。
data={}
data['url'] = basic_url
data['basic_info'] = self.basicinfo_spider(basic_url)
data['cause_info'] = self.common_spider(cause_url)
data['prevent_info'] = self.common_spider(prevent_url)
data['symptom_info'] = self.symptom_spider(symptom_url)
data['inspect_info'] = self.inspect_spider(inspect_url)
data['treat_info'] = self.treat_spider(treat_url)
data['food_info'] = self.food_spider(food_url)
data['drug_info'] = self.drug_spider(drug_url)
print(page, basic_url)
self.col.insert_one(data)
except Exception as e:
print(e,page)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
需要爬取的信息包括疾病名、所属目录、症状、治疗方案等等,都可以从页面上获取。


MongoDB里面的数据也是刷新显示最新数据记录



我们随便点一个我们爬取的网页链接,点击查看网页详情:




综上内容所述,这里做一个总结

这里代码爬虫的主要功能是爬取疾病相关的信息,并将数据存储到MongoDB数据库中。代码的主要结构是一个名为MedicalSpider的类,它包含了各种方法来处理不同类型的数据采集任务。在代码的开头,导入了一些必要的库,如requests、urllib、lxml和pymongo。然后定义了一个MedicalSpider类,该类的构造函数初始化了MongoDB的连接,并指定了要使用的数据库和集合。

接下来是一系列方法,用于实现不同类型的数据采集。其中,get_html方法用于发送HTTP请求并获取网页的HTML内容。url_parser方法用于解析HTML内容,提取出需要的URL。basicinfo_spider方法用于解析疾病的基本信息,如名称、描述和所属目录。treat_spider、drug_spider和food_spider方法分别用于解析治疗信息、药物信息和食物信息。symptom_spider方法用于解析疾病的症状信息。inspect_spider方法用于解析疾病的检查信息。common_spider方法用于解析通用模块下的内容,如疾病预防和疾病起因。

在spider_main方法中,通过循环遍历页面,构造不同类型的URL,并调用相应的方法进行数据采集。采集到的数据以字典的形式存储,并插入到MongoDB数据库中。
最后,代码调用了MedicalSpider类的实例,并依次调用了inspect_crawl和spider_main方法,完成了数据的采集和存储。

通过爬取寻医问药网站的相关页面,获取疾病的基本信息、治疗信息、药物信息、食物信息、症状信息和检查信息,并将数据存储到MongoDB数据库中。

结束之后我们可以在MongoDB数据库中查看我们爬取到的疾病链接和解析出的网页内容:


贪心算法策略
贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的仅是在某种意义上的局部最优解。贪心算法不是对所有问题都能得到整体最优解,但对范围相当广泛的许多问题他能产生整体最优解或者是整体最优解的近似解。

贪婪算法(Greedy algorithm)是一种对某些求最优解问题的更简单、更迅速的设计技术。用贪婪法设计算法的特点是一步一步地进行,常以当前情况为基础根据某个优化测度作最优选择,而不考虑各种可能的整体情况,它省去了为找最优解要穷尽所有可能而必须耗费的大量时间,它采用自顶向下,以迭代的方法做出相继的贪心选择,每做一次贪心选择就将所求问题简化为一个规模更小的子问题,通过每一步贪心选择,可得到问题的一个最优解,虽然每一步上都要保证能获得局部最优解,但由此产生的全局解有时不一定是最优的,所以贪婪法不要回溯。

贪婪算法是一种改进了的分级处理方法。其核心是根据题意选取一种量度标准。然后将这多个输入排成这种量度标准所要求的顺序,按这种顺序一次输入一个量。如果这个输入和当前已构成在这种量度意义下的部分最佳解加在一起不能产生一个可行解,则不把此输入加到这部分解中。这种能够得到某种量度意义下最优解的分级处理方法称为贪婪算法。

对于一个给定的问题,往往可能有好几种量度标准。初看起来,这些量度标准似乎都是可取的,但实际上,用其中的大多数量度标准作贪婪处理所得到该量度意义下的最优解并不是问题的最优解,而是次优解。因此,选择能产生问题最优解的最优量度标准是使用贪婪算法的核心。

这里是用于中文分词的工具类,名为CutWords。它实现了最大向前匹配、最大向后匹配和双向最大匹配三种分词算法。在类的构造函数中,它首先初始化了一个词典路径,并调用load_words方法加载词典。load_words方法读取词典文件,将词语存储到一个列表中,并记录词语的最大长度。

max_forward_cut方法实现了最大向前匹配算法。它从左向右遍历待切分的中文句子,根据词典中的词语进行匹配。如果匹配成功,则将匹配的词语作为一个词切分出来。如果没有匹配成功,则按字符切分。最后,返回切分结果。

max_backward_cut方法实现了最大向后匹配算法。它从右向左遍历待切分的中文句子,根据词典中的词语进行匹配。如果匹配成功,则将匹配的词语作为一个词切分出来。如果没有匹配成功,则按字符切分。最后,将切分结果倒序返回。

max_biward_cut方法实现了双向最大匹配算法。它先调用max_forward_cut方法和max_backward_cut方法分别得到正向和逆向的切分结果。然后,根据启发式规则比较两种切分结果,决定正确的分词方法。如果正向和逆向的切分结果词数不同,则选择词数较少的那个。如果词数相同,则根据单字数量选择切分结果。

双向最大匹配法是将正向最大匹配法得到的分词结果和逆向最大匹配法得到的结果进行比较,从而决定正确的分词方法。

启发式规则:
1.如果正反向分词结果词数不同,则取分词数量较少那个;
2.如果分词结果词数相同:
a.分词结果相同,说明没有歧义,可以返回任意一个结果;
b.分词结果不同,返回其中单字较少的那个。

在最大向前匹配和最大向后匹配中,算法会从待切分的汉语句的左侧或右侧开始,每次选择当前位置开始的最长匹配字段,然后移动到下一个未匹配的位置。这个过程一直进行,直到整个句子被切分完毕。在双向最大向前匹配中,先使用最大向前匹配和最大向后匹配分别得到两组分词结果,然后根据一定的启发式规则来确定最终的分词结果。这种方法可以一定程度上解决中文分词中的歧义问题。

总体来说,最大匹配法是一种贪心算法,它每次选择当前位置的最长匹配字段,希望通过局部最优的选择达到整体的最优结果。



然后我们运行build_data.py脚本,通过collect_medical函数完成数据的收集整理工作。
再将整理好的数据重新存入数据库中。在init文件里配置数据库连接,找到当前文件所在的目录,指定连接的数据库及其下面的collection。




再利用python脚本导出爬出的医疗数据并设置格式为data.json格式文件:



处理数据后对应的图谱系统数据关键词:



将从MongoDB导出的data.json数据格式处理之后保存为medical.json和medical2.json文件,其中medical2.json为medical.json中的部分数据。
考虑到构建全部知识图谱需要很久,所以取出一部分数据(medical2.json)先进行构建图谱。

处理后结构化的数据保存到excel文件:

3)所搭建的系统框架,包括知识图谱实体类型,实体关系类型,知识图谱属性类型等。

知识图谱实体类型


一般来说,一个医疗知识图谱问答系统Schema包括以下几个部分:
实体:指代医疗领域中的具体概念或对象,如药品、疾病、症状等。
属性:指代实体的特征或描述,如药品的成分、剂型、适应症等。
关系:指代实体之间的联系或影响,如疾病与药物的治疗关系、食物的忌吃关系等。
问题:指代用户对医疗领域的信息需求,如“高血压应该吃什么药?”、“感冒有哪些常见的症状?”等。
答案:指代针对问题的回复或解释,如“高血压可以服用降压药物,如氨氯地平片、硝苯地平片等。”、“感冒常见的症状有发热、咳嗽、流鼻涕等。”等。

departments = [] #科室
diseases = [] # 疾病
drugs = [] # 药品
foods = [] # 食物
producers = [] #药品大类
symptoms = []#症状
1
2
3
4
5
6


实体关系类型


# 构建节点实体关系,共11类,medical2做出来的只有10类,因为数据量少
rels_department = []
rels_noteat = [] # 疾病-忌吃食物关系
rels_doeat = [] # 疾病-宜吃食物关系
rels_recommandeat = [] # 疾病-推荐吃食物关系
rels_commonddrug = [] # 疾病-通用药品关系
rels_recommanddrug = [] # 疾病-热门药品关系
rels_check = [] # 疾病-检查关系
rels_drug_producer = [] # 厂商-药物关系
rels_symptom = [] #疾病症状关系
rels_acompany = [] # 疾病并发关系
rels_category = [] # 疾病与科室之间的关系
1
2
3
4
5
6
7
8
9
10
11
12
知识图谱属性类型


医疗问答KBQA查询功能表


4)导入Neo4j数据库,生成图谱。
新建一个数据库:基于医疗领域的问答系统

免责申明:
本文系转载,版权归原作者所有,如若侵权请联系我们进行删除!


《数据治理行业实践白皮书》下载地址:https://fs80.cn/4w2atu
《数栈V6.0产品白皮书》下载地址:https://fs80.cn/cw0iw1
想了解或咨询更多有关袋鼠云大数据产品、行业解决方案、客户案例的朋友,浏览袋鼠云官网:https://www.dtstack.com/?src=bbs
同时,欢迎对大数据开源项目有兴趣的同学加入「袋鼠云开源框架钉钉技术群」,交流最新开源技术信息,群号码:30537511,项目地址:https://github.com/DTStack

0条评论
上一篇:Flink 使用场景
社区公告
  • 大数据领域最专业的产品&技术交流社区,专注于探讨与分享大数据领域有趣又火热的信息,专业又专注的数据人园地

最新活动更多
微信扫码获取数字化转型资料
钉钉扫码加入技术交流群