简介:本资源是一套完整的基于知识图谱的医疗问答系统毕业设计源码,面向计算机、软件工程及医学信息工程等专业的本科生与课程设计学习者,解决传统医疗咨询响应慢、专业性弱、知识关联浅等问题。系统采用Django构建前后端,MySQL 5.7存储用户账号等结构化数据,Neo4j图形数据库承载疾病、症状、药品、科室等实体及其语义关系,实现精准语义检索与推理问答。压缩包共1207个文件,含33个核心Python后端模块、225个JS交互脚本、176个PNG界面资源、101个CSS样式文件、41个HTML模板页,以及Neo4j原生存储文件(如neostore.*、counts.db、propertystore.db.arrays等),完整复现知识图谱导入、图查询接口、用户认证与问答前端流程,包体大小为186.79MB。已有108人下载学习,配套说明文档详述环境配置(Python 3.6.8 + PyCharm + Navicat11)、数据库初始化、Neo4j图谱加载步骤及LW(论文)框架,可直接用于毕设答辩与二次开发。
1. 项目概述:一个能“听懂”医学问题的智能助手
最近几年,无论是毕业设计还是实际项目开发,基于知识图谱的智能问答系统都是一个非常热门的方向。特别是结合医疗这个垂直领域,它既有明确的应用价值,又有足够的技术深度来支撑一个完整的毕业设计。我手头这个项目,就是一个典型的、可以直接拿来参考甚至复现的“基于知识图谱的医疗问答系统”,它用 Python 的 Django 框架搭建了完整的前后端,后端用 MySQL 存储结构化数据,并整合了知识图谱的核心思想来实现问答逻辑。
简单来说,这个系统就像一个初步具备医学知识的“智能导诊员”。用户不用去记忆复杂的医学名词分类或者科室划分,他可以直接用自然语言提问,比如“我最近老是头晕、乏力,可能是什么原因?”或者“糖尿病的早期症状有哪些?”。系统会尝试理解这个问题,然后从它背后的“知识库”——也就是我们构建的医疗知识图谱——中,找到最相关、最准确的答案片段,并以清晰、结构化的方式呈现给用户。这背后涉及的核心技术,就是如何把散乱的医疗知识(疾病、症状、药品、检查等)组织成一张相互关联的“网”(知识图谱),以及如何让计算机理解用户模糊的自然语言问题,并在这张网上进行精准的“导航”和“检索”。
这个项目非常适合计算机、软件工程、人工智能相关专业的同学作为毕业设计选题。它技术栈完整(Python+Django+MySQL+前端),涵盖了 Web 开发、数据库设计、自然语言处理(NLP)基础应用等多个知识点,而且最终的成果是一个有界面、能交互的完整系统,答辩时演示效果会非常好。对于有经验的开发者来说,这个项目的架构和实现思路,也为构建其他垂直领域的问答系统(如法律、金融、教育)提供了很好的模板。
2. 核心架构与设计思路拆解
要理解这个系统是怎么工作的,我们不能只看代码,得先理清它的“大脑”是如何思考的。一个基于知识图谱的问答系统,其核心设计思路可以概括为“知识结构化”和“问题理解与匹配”两大阶段。
2.1 为什么选择“知识图谱+关系型数据库”的混合模式?
纯粹的知识图谱引擎(如 Neo4j)擅长处理复杂的、网状的关系查询,但对于一个毕业设计或中小型项目来说,引入 Neo4j 会增加环境部署、数据迁移和学习成本。而这个项目采用了一种非常务实且高效的方案:用 MySQL 关系型数据库来“模拟”和存储知识图谱的三元组结构。
什么是三元组?它就是知识图谱的基本单位,通常表示为(实体1,关系,实体2)。例如,(糖尿病,常见症状,多饮)、(阿司匹林,用于治疗,头痛)。在 MySQL 中,我们可以设计这样几张核心表:
- 实体表 (entity):存储所有实体,如疾病、症状、药品、检查项目等。表字段可能包括:实体ID、实体名称、实体类型(用于区分是疾病还是症状)、描述等。
- 关系表 (relation):存储所有定义好的关系类型,如“症状”、“病因”、“治疗方法”、“禁忌”等。表字段包括:关系ID、关系名称。
- 三元组表 (triple):这是最核心的表,用于记录具体的知识。表字段包括:三元组ID、头实体ID(对应 entity 表)、关系ID(对应 relation 表)、尾实体ID(对应 entity 表)。通过外键关联,我们就用关系数据库的表结构,清晰地记录下了“糖尿病-常见症状-多饮”这条知识。
注意:这种设计的好处是结构清晰、查询灵活(通过 SQL 的 JOIN 操作可以实现多跳查询),并且与 Django 的 ORM(对象关系映射)模型结合得天衣无缝,开发效率很高。它本质上构建了一个“属性图”。对于医疗领域初期相对规范的关系(疾病-症状,药品-适应症)来说,完全够用。
2.2 系统核心工作流程解析
当用户在前端页面输入一个问题后,后端 Django 应用会按以下流程处理:
- 问题接收与预处理:Django 的视图(View)接收到前端 Ajax 提交的问题文本。
- 关键信息抽取(NLP 核心):这是问答系统的“理解”环节。系统需要对用户问题进行分词、词性标注和命名实体识别(NER)。例如,对于问题“糖尿病应该吃什么药?”,需要识别出“糖尿病”是疾病实体,“药”是目标实体类型。在毕业设计层面,这里通常不会用复杂的深度学习模型,而是采用基于词典和规则的方法。我们预先构建一个医疗实体词典(从医学百科、公开数据集中抽取),然后通过字符串匹配或简单的算法(如 AC 自动机)来快速识别问题中的疾病、症状等关键词。
- 意图识别与查询构建:识别出实体后,还需要判断用户的意图。是问症状?问病因?还是问药品?这可以通过分析问题中的关键词(“有什么症状”、“是什么原因”、“怎么治疗”、“吃什么药”)结合规则来判断。例如,问题中有“症状”,意图就是“查询疾病症状”。然后,系统根据识别出的实体和意图,动态拼接出对应的 SQL 查询语句。比如,意图是“查询疾病症状”,实体是“糖尿病”,那么生成的 SQL 就是去
triple表中查找头实体=“糖尿病” 且 关系=“常见症状”的所有尾实体。 - 知识检索与答案生成:执行构建好的 SQL 查询,从 MySQL 中检索出相关的实体(即答案)。例如,查出“多饮、多尿、体重下降”等。然后,将这些零散的答案片段,组织成一句或一段通顺的自然语言回复。例如,“糖尿病的常见症状包括:多饮、多尿、体重下降等。”
- 结果返回与前端展示:Django 将组织好的答案以 JSON 格式返回给前端,前端 JavaScript 再将其渲染到页面的答案区域。
整个流程,就是一个将“非结构化自然语言问题”转化为“结构化数据库查询”,再将“结构化查询结果”转化为“自然语言答案”的过程。
3. 关键技术细节与实现要点
理解了宏观流程,我们深入到几个关键的技术实现细节,这些地方往往是决定项目成败和深度的关键。
3.1 医疗知识图谱的数据构建与处理
一个问答系统,知识库的质量决定了它的天花板。对于毕业设计,我们通常无法获得高质量的标注数据,因此需要采用“半自动化构建+人工校验”的方式。
数据来源:
- 公开结构化数据:如“疾病百科”类网站的数据,通常包含疾病名称、别名、症状、病因、治疗方法等字段,相对规整,易于解析入库。
- 非结构化文本爬取:从权威医学网站(如 Mayo Clinic、国内正规医学平台)爬取疾病介绍文章。但这部分数据是纯文本,需要进一步的信息抽取。
- 现有知识库:可以尝试使用像“CN-DBpedia”、“Zhishi.me”等开放的中文通用知识图谱中与医疗相关的子集,但需要清洗和领域化。
信息抽取与知识融合: 对于从非结构化文本中抽取知识,在毕业设计场景下,可以采用基于依存句法分析的规则方法。例如,从句子“糖尿病的主要症状是多饮和多尿”中,我们可以分析出“糖尿病”是主语,“症状”是谓语,“多饮和多尿”是宾语。通过预设的规则模板(如“<疾病>的[主要|常见]症状是<症状列表>”),就可以抽取出(糖尿病,症状,多饮)和(糖尿病,症状,多尿)两个三元组。 抽取出知识后,会遇到“一词多义”和“多词一义”问题(如“高血压”和“高血压病”指同一疾病)。这里需要建立一个同义词表,在存入数据库前进行实体链接,将表述不同的同一实体归一化到标准实体名上。
3.2 基于规则与词典的简易 NLP 模块实现
如前所述,复杂的深度学习 NER 模型部署成本高。在 Django 项目中,我们可以实现一个轻量级的nlp_processor.py模块。
# nlp_processor.py 示例 import jieba import jieba.posseg as pseg import re class MedicalNLP: def __init__(self): # 加载自定义医疗词典 jieba.load_userdict('data/medical_entities.dict') self.disease_dict = self.load_dict('data/disease.txt') # 疾病词典 self.symptom_dict = self.load_dict('data/symptom.txt') # 症状词典 self.drug_dict = self.load_dict('data/drug.txt') # 药品词典 # 意图关键词映射 self.intent_keywords = { 'symptom': ['症状', '表现', '征兆', '有什么感觉'], 'cause': ['原因', '病因', '为什么', '怎么会'], 'treatment': ['治疗', '怎么治', '怎么办', '疗法'], 'drug': ['药', '药品', '吃什么药', '用药'], 'prevention': ['预防', '怎么防止', '避免'] } def load_dict(self, filepath): with open(filepath, 'r', encoding='utf-8') as f: return set([line.strip() for line in f]) def extract_entities(self, question): """抽取问题中的医疗实体""" words = pseg.cut(question) entities = {'disease': [], 'symptom': [], 'drug': []} for word, flag in words: if word in self.disease_dict: entities['disease'].append(word) elif word in self.symptom_dict: entities['symptom'].append(word) elif word in self.drug_dict: entities['drug'].append(word) return entities def recognize_intent(self, question): """识别用户意图""" for intent, keywords in self.intent_keywords.items(): for kw in keywords: if kw in question: return intent return 'other' # 默认意图 def parse_question(self, question): """解析问题,返回实体和意图""" entities = self.extract_entities(question) intent = self.recognize_intent(question) return { 'entities': entities, 'intent': intent, 'original_question': question }这个类提供了最基础的功能:利用 Jieba 加载医疗词典进行分词和实体识别,通过关键词匹配判断意图。在实际使用时,需要在medical_entities.dict和各个.txt文件中预先整理好医疗实体词库。
3.3 Django 模型层与问答逻辑的核心设计
Django 的模型(Model)是与数据库交互的桥梁。以下是核心数据模型的一种设计方式:
# models.py from django.db import models class Entity(models.Model): """实体表,如疾病、症状、药品等""" ENTITY_TYPES = ( ('disease', '疾病'), ('symptom', '症状'), ('drug', '药品'), ('check', '检查'), ('department', '科室'), ) name = models.CharField(max_length=100, unique=True, verbose_name='实体名称') entity_type = models.CharField(max_length=20, choices=ENTITY_TYPES, verbose_name='实体类型') desc = models.TextField(blank=True, verbose_name='描述') # 可以添加更多字段,如别名、图片链接等 def __str__(self): return self.name class Relation(models.Model): """关系类型表""" name = models.CharField(max_length=50, unique=True, verbose_name='关系名称') desc = models.CharField(max_length=200, blank=True, verbose_name='关系描述') # 例如:'has_symptom'(有症状), 'common_drug'(常用药), 'belongs_to'(属于科室) def __str__(self): return self.name class Triple(models.Model): """三元组表,存储具体知识""" head = models.ForeignKey(Entity, on_delete=models.CASCADE, related_name='as_head') relation = models.ForeignKey(Relation, on_delete=models.CASCADE) tail = models.ForeignKey(Entity, on_delete=models.CASCADE, related_name='as_tail') # 可添加置信度、来源等字段 created_time = models.DateTimeField(auto_now_add=True) class Meta: unique_together = ('head', 'relation', 'tail') # 防止重复知识 def __str__(self): return f'{self.head.name} - {self.relation.name} - {self.tail.name}'基于这个模型,问答逻辑(视图层)的核心函数可能如下:
# views.py (部分) from django.http import JsonResponse from .models import Entity, Triple, Relation from .nlp_processor import MedicalNLP nlp_processor = MedicalNLP() def answer_question(request): if request.method == 'POST': question = request.POST.get('question', '').strip() if not question: return JsonResponse({'code': 400, 'msg': '问题不能为空'}) # 1. NLP解析 parsed_result = nlp_processor.parse_question(question) entities = parsed_result['entities'] intent = parsed_result['intent'] # 2. 根据意图和实体构建查询 answer_entities = [] if intent == 'symptom' and entities.get('disease'): disease_name = entities['disease'][0] try: disease_entity = Entity.objects.get(name=disease_name, entity_type='disease') # 查找该疾病的所有症状关系三元组 symptom_relation = Relation.objects.get(name='has_symptom') # 假设存在此关系 triples = Triple.objects.filter(head=disease_entity, relation=symptom_relation) answer_entities = [triple.tail.name for triple in triples] except (Entity.DoesNotExist, Relation.DoesNotExist): pass # 3. 生成答案文本 if answer_entities: if intent == 'symptom': answer_text = f"{disease_name}的常见症状包括:{'、'.join(answer_entities)}。" # ... 其他意图的答案模板 else: answer_text = "抱歉,我暂时没有找到这个问题的答案。" return JsonResponse({ 'code': 200, 'data': { 'question': question, 'answer': answer_text, 'entities': answer_entities, 'intent': intent } }) return JsonResponse({'code': 405, 'msg': '方法不允许'})4. 完整前后端实现与部署要点
一个毕业设计项目,除了核心逻辑,完整的前后端交互和部署也是评分重点。
4.1 前端界面设计与交互实现
前端不需要太复杂,一个简洁的问答界面即可。可以使用 Bootstrap 快速搭建。核心是一个输入框、一个提问按钮和一个答案展示区域。
<!-- question.html 核心部分 --> <div class="container mt-5"> <h2 class="text-center mb-4">医疗知识智能问答系统</h2> <div class="row justify-content-center"> <div class="col-md-8"> <div class="input-group mb-3"> <input type="text" class="form-control" id="questionInput" placeholder="请输入您的医疗问题,例如:糖尿病的症状有哪些?"> <button class="btn btn-primary" type="button" id="askBtn">提问</button> </div> <div id="answerArea" class="mt-4"> <!-- 答案将动态加载到这里 --> </div> </div> </div> </div> <script> $(document).ready(function(){ $('#askBtn').click(function(){ var question = $('#questionInput').val(); if(!question) { alert('请输入问题!'); return; } // 显示加载中 $('#answerArea').html('<div class="text-center"><div class="spinner-border" role="status"></div> 思考中...</div>'); $.ajax({ url: '/api/answer/', // Django后端API地址 type: 'POST', data: { 'question': question, 'csrfmiddlewaretoken': '{{ csrf_token }}' }, success: function(response){ if(response.code === 200){ var data = response.data; var html = `<div class="card"> <div class="card-header"><strong>Q:</strong> ${data.question}</div> <div class="card-body"> <p class="card-text"><strong>A:</strong> ${data.answer}</p> <small class="text-muted">识别意图:${data.intent}</small> </div> </div>`; $('#answerArea').html(html); } else { $('#answerArea').html(`<div class="alert alert-danger">${response.msg}</div>`); } }, error: function(){ $('#answerArea').html('<div class="alert alert-danger">网络请求失败,请稍后重试。</div>'); } }); }); // 支持回车键提问 $('#questionInput').keypress(function(e){ if(e.which == 13){ $('#askBtn').click(); } }); }); </script>4.2 数据库初始化与数据导入流程
项目拿到手后,数据库很可能是空的。你需要一个数据初始化脚本。
创建数据库和迁移:
python manage.py makemigrations python manage.py migrate编写数据导入脚本(
import_data.py): 这个脚本负责从你准备好的数据文件(如medical_data.json或medical_data.csv)中读取知识,并调用 Django 的 ORM 创建Entity和Triple记录。import os, django os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'your_project.settings') django.setup() from kg_medical.models import Entity, Relation, Triple import json def import_from_json(filepath): with open(filepath, 'r', encoding='utf-8') as f: data = json.load(f) for item in data: # 假设数据格式:{"head": "糖尿病", "relation": "has_symptom", "tail": "多饮"} head_entity, _ = Entity.objects.get_or_create(name=item['head'], defaults={'entity_type': 'disease'}) tail_entity, _ = Entity.objects.get_or_create(name=item['tail'], defaults={'entity_type': 'symptom'}) relation_obj, _ = Relation.objects.get_or_create(name=item['relation']) Triple.objects.get_or_create(head=head_entity, relation=relation_obj, tail=tail_entity) print('数据导入完成!')运行
python import_data.py即可将知识灌入数据库。
4.3 项目部署与运行指南
对于毕业设计演示,本地运行即可。但了解部署流程是加分项。
- 环境准备:确保安装 Python 3.7+,使用
pip install -r requirements.txt安装依赖(Django, mysqlclient, jieba 等)。 - 数据库配置:在
settings.py中正确配置 MySQL 数据库连接信息。DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'medical_kg', # 数据库名 'USER': 'your_username', 'PASSWORD': 'your_password', 'HOST': 'localhost', 'PORT': '3306', } } - 运行开发服务器:
然后在浏览器访问python manage.py runserver 0.0.0.0:8000http://localhost:8000即可。 - 生产环境部署(可选):可以使用 Nginx + Gunicorn 来部署 Django 应用,并使用 Supervisor 管理进程。但这对于毕业设计答辩通常不是必须的。
5. 常见问题排查与项目深度优化方向
在实际运行和开发这个项目的过程中,你肯定会遇到一些坑。这里我总结几个常见问题和对应的解决思路。
5.1 开发与运行中的典型问题速查表
| 问题现象 | 可能原因 | 排查与解决思路 |
|---|---|---|
运行python manage.py runserver报错ModuleNotFoundError: No module named 'MySQLdb' | 未安装 MySQL 的 Python 连接驱动。 | 安装mysqlclient包:pip install mysqlclient。在 Windows 上如果安装失败,可以到 这里 下载对应版本的.whl文件离线安装。 |
访问页面出现DisallowedHost错误 | Django 的ALLOWED_HOSTS配置限制了可访问的主机。 | 在settings.py中,将ALLOWED_HOSTS = []修改为ALLOWED_HOSTS = ['*'](仅限开发环境)或添加具体的域名/IP。 |
| 前端 Ajax 请求返回 403 Forbidden 错误 | 缺少 CSRF(跨站请求伪造)令牌。 | 确保 POST 请求中包含了 CSRF token。如上文前端代码所示,通过模板变量{{ csrf_token }}获取并随数据提交。或者在视图上使用@csrf_exempt装饰器(不推荐用于生产环境)。 |
| 问答系统识别不出实体或意图 | 1. 用户问题中的词汇不在自定义词典里。 2. 意图关键词匹配规则不完善。 | 1.扩充词典:持续收集医疗实体,加入disease.txt,symptom.txt等文件,并确保jieba.load_userdict加载了最新词典。2.优化规则:分析未匹配的问题案例,补充新的意图关键词或采用更灵活的匹配方式(如正则表达式)。 |
| 数据库查询速度慢,页面响应迟缓 | 1. 数据量增大后,缺乏索引。 2. 问答逻辑中存在循环查询或低效查询。 | 1.添加数据库索引:为Entity表的name字段,Triple表的head,relation,tail外键字段添加索引,可以极大提升 JOIN 查询速度。2.优化查询:使用 Django ORM 的 select_related或prefetch_related来减少数据库查询次数。避免在循环内进行数据库查询。 |
5.2 从毕业设计到更优系统的优化建议
如果你想把这个项目做得更出彩,或者为后续深入研究做准备,可以考虑以下几个优化方向:
引入更先进的 NLP 模型:
- 实体识别升级:将基于词典的 NER 替换为基于预训练模型(如 BERT、RoBERTa)的微调模型。你可以使用
transformers库,在少量标注的医疗文本上微调一个 NER 模型,识别准确率会大幅提升。 - 意图识别升级:将关键词匹配升级为文本分类模型。把用户问题分类到更精细的意图类别(如“查询疾病症状”、“查询药品副作用”、“查询检查项目”等)。可以用 FastText 或简单的 BERT 分类模型来实现。
- 实体识别升级:将基于词典的 NER 替换为基于预训练模型(如 BERT、RoBERTa)的微调模型。你可以使用
知识图谱查询的复杂化: 当前系统主要处理单跳查询(如疾病->症状)。可以扩展支持多跳推理查询,例如“治疗糖尿病的药物有哪些副作用?”。这需要系统能先找到“治疗糖尿病的药物”(第一跳),再找到这些药物的“副作用”(第二跳)。这需要对查询逻辑进行更复杂的设计,可能涉及图数据库查询语言(如 Cypher)或更复杂的 SQL 联表查询。
答案生成的友好性与多样性: 目前的答案生成是基于简单模板的。可以引入更灵活的模板,或者尝试使用文本生成技术,让答案更自然。例如,除了列出症状,还可以补充一句“如果出现以上多种症状,建议及时就医检查。”
前端体验优化:
- 历史问答记录:使用浏览器本地存储(LocalStorage)或后端会话,保存用户的提问历史。
- 相关问题推荐:在返回答案的同时,根据当前问题中的实体,从知识图谱中推荐几个相关问题(如“糖尿病如何预防?”、“糖尿病饮食注意什么?”),提升交互性。
- 可视化知识图谱:使用 ECharts 或 D3.js 库,将查询结果涉及的实体和关系以图谱形式可视化展示出来,非常直观,答辩时是亮点。
引入检索增强生成(RAG)思想: 这是当前结合大语言模型(LLM)的热门方向。你可以将系统改造为:先用现有的知识图谱检索出最相关的若干条知识(三元组),然后将这些知识作为“参考依据”和用户问题一起,提交给一个开源的、本地部署的小规模 LLM(如 ChatGLM-6B、Qwen-7B),让 LLM 来组织生成最终答案。这样既能保证答案的准确性(来源于可靠知识库),又能提升答案的自然度和完整性。这需要一定的本地 GPU 算力或使用云上 API。
这个项目提供了一个坚实的起点。它的价值在于清晰地展示了从数据到知识、从知识到智能应用的完整链路。无论是为了通过毕业答辩,还是作为进入 AI 或知识图谱领域的敲门砖,亲手实现并深入理解其中的每一个环节,都会让你受益匪浅。在实际操作中,最大的挑战往往不是编写代码,而是如何获取和清洗高质量的数据,以及如何设计出覆盖大部分用户问法的规则。多测试、多分析 bad case(回答错误或无法回答的案例),是迭代优化系统的最佳途径。
本文还有配套的精品资源,点击获取