资讯动态

中风风险数据分析与可视化:从Django到深度学习的完整实践

发布时间:2026/10/14 5:42:04 来源:尧图企业网站定制
有朋友拿着“中风人群的数据分析与可视化”这个题目来找我的时候第一反应是这题不太好做。数据在哪、分析用什么方法、Django到底要写到什么程度、深度学习模型放进去是加分还是添乱——这些问题在动手前全是未知数。等项目真正做完我反而觉得这类题目比很多看着高大上的选题更值得选它强迫你把数据分析、算法模型和Web系统完整地串成一条链路而不是停留在“训练一个模型然后保存结果”的演示层面。这篇文章就把我梳理这个项目的完整思路、技术选型、踩坑记录和答辩要注意的点都展开说说。适合正在做医工交叉方向毕设、或者想拿一个“有业务含义、有技术深度、有界面呈现”的综合型项目的人来说应该能帮你少走不少弯路。1. 中风风险数据这种综合型选题到底在做什么1.1 医学背景中风数据不是一堆数字而是一组风险信号中风在医学上叫脑卒中是脑血管被堵住或者破裂导致的脑组织损伤。它不是一个单一原因造成的疾病而是多种危险因素共同作用的结果。公开的脑卒中风险数据集里常见的字段比如年龄、高血压、心脏病史、平均血糖、BMI、吸烟状态每一列背后都有明确的医学解释。我做这个项目的时候第一件事不是急着调模型而是先把这些字段和医学含义对了一遍。高血压是中风最重要的可控危险因素之一长期血压高会损伤血管壁血糖异常说明可能存在糖尿病或者糖耐量异常这类人群血管病变风险显著上升肥胖BMI偏高往往伴随着血脂异常和血压升高。年龄则是不可干预因素数据里60岁以上人群的中风记录比例明显更高这一点和医学常识完全吻合。理解了字段含义之后分析的思路就清楚了不是让模型去“诊断”某个人有没有中风而是通过历史统计数据找到“什么样的人群风险更高”。整条分析链路围绕风险分层、因素关联、指标交互来展开这样后期做可视化和Web展示时每张图表都能讲出业务含义而不是干巴巴地堆数据。1.2 技术骨架一个“有区分度”的毕设通常分四层纯做管理系统、纯做算法调参这类题目答辩时很容易被一轮轮追问打穿。我的经验是一个有区分度的综合分析型项目至少要在项目里完整铺开四层技术栈数据层用Pandas做数据读取、清洗、缺失值处理、类别编码。这一层决定了后续模型的天花板。可视分析层用Matplotlib、Seaborn做探索性分析刻画不同因素与中风的关系。模型层用Scikit-learn跑基线模型再用Keras搭建深度学习网络做分类预测。系统层用Django把数据统计结果和模型推理包装成Web接口前端用ECharts呈现可视化大屏。这个选题的聪明之处在于每一层都有可以量化的产出。数据层有清洗前后的对比可视分析层有图表模型层有AUC和混淆矩阵系统层有在线风险预测交互。答辩的时候不管老师从哪一层切入你都有的讲。反过来如果只做其中一两层往往会出现技术栈太单薄或者展示不落地的问题。2. 数据清洗与特征工程模型性能从数据准备开始2.1 数据集的字段与医学含义对照项目使用的是公开研究用的脱敏脑卒中风险数据大概包含五千多条记录每条记录对应一个匿名个体的健康指标和是否中风的标签。字段不算多但每个字段都需要明确它的类型和处理方式。我整理了一个对照表后面做特征工程时一直用这个表来统一思路字段类型医学含义处理方式gender类别性别二值编码age数值年龄归一化 分箱hypertension二值是否高血压0/1映射heart_disease二值是否曾患心脏病0/1映射ever_married二值婚姻状态0/1映射work_type类别工作类型OneHotResidence_type二值城市/农村0/1映射avg_glucose_level数值平均血糖水平归一化bmi数值身体质量指数填充 归一化smoking_status类别吸烟状态OneHotstroke二值是否发生中风标签目标变量这份字段清单基本就是把中风危险因素的常见维度都覆盖了一遍。字段不多反而有好处特征工程的工作量可控而且每个特征都能讲到医学依据答辩时不用心虚。2.2 缺失值、类别编码和不平衡样本的处理链路数据质量直接决定模型效果这个项目里最典型的问题有三个。第一个是缺失值。bmi字段有少量缺失在数据集中占比大概一两成左右直接删除会丢掉很多样本所以我选择用中位数填充。为什么不用均值因为BMI的分布有一些右偏极端值会把均值拉高中位数对偏态分布更稳健。吸烟状态字段缺失比例更高直接用众数填充会丢掉“缺失本身也是一种信息”的含义更稳的做法是单独增加一个“unknown”类别保留缺失情况供模型学习。第二个是类别编码。二值变量像高血压、心脏病史直接映射成0和1就行。但工作类型、吸烟状态这类多类别变量不能贪图省事用LabelEncoder因为它会给类别强行加上一个不存在的顺序关系。这里应该用OneHot编码把每个类别展开成独立的0/1特征向量。第三个是类别不平衡。数据集中中风样本占比只有5%左右如果直接拿原始数据训练模型会倾向于把所有样本都预测为“未中风”因为这样准确率也能到95%。项目里我同时用了两种手段一是SMOTE过采样合成部分少数类样本二是在训练时给少数类设置更高的class_weight让模型对错分中风样本的惩罚更大。两个手段可以配合使用后面调模型时会看到召回率有明显变化。2.3 特征衍生与相关性洞察原始特征经过清洗编码之后还可以做一步特征衍生让后续可视化和模型都能吃得更透。我对年龄做了分箱处理小于40岁算青年组40到59岁算中年组60岁及以上算老年组。对BMI也按标准划分了偏瘦、正常、超重、肥胖四个区间。分箱的好处有两个一是连续变量变成有业务含义的区间做图表更直观二是能构造交叉统计比如看“高血压×年龄组”下中风比例的变化这种二维交叉在展示阶段特别出效果。相关性分析我看了两个层面。特征层面的热力图显示age和avg_glucose_level与中风标签的相关性相对更强业务层面的分组统计则发现血糖值偏高的群体中风比例显著上升老年组的风险也明显高于中青年。这些结论都可以作为答辩时的业务洞察整条项目逻辑从数据到结论是自洽的。3. 深度学习建模先跑基线再来调优3.1 为什么先跑逻辑回归和随机森林再上神经网络不少人拿到数据集就急着搭深度学习模型实际上这是一个容易踩坑的操作。五千条样本、十几个特征这个规模下深度学习并不是天生占优的。我建议先跑两个基线模型——逻辑回归和随机森林拿到一组数字作为参照再判断神经网络带来的增益值不值得。我当时跑出来的代表性结果大概是这样的模型准确率精确率召回率AUC逻辑回归0.940.320.180.78随机森林0.970.450.240.80多层感知机MLP0.960.520.470.85注意看准确率上随机森林甚至更高但一旦把视角转到召回率和AUCMLP的优势就显现出来了。医学筛查场景最怕漏掉真正的高风险人群模型哪怕把一些正常人误判为高风险后续还有医生复核但如果是真正的高风险病人被漏过问题就大了。所以在这个业务场景下召回率比准确率重要得多。选择MLP的理由不是因为它“叫深度学习”而是因为它能自动学习特征之间的交叉组合。逻辑回归只能做线性加权随机森林能捕捉一些非线性但解释性较弱MLP通过隐藏层的非线性激活可以把年龄和血糖、血糖和BMI之间的交互模式一起学出来。配合类别平衡策略后召回率的提升是三个模型里最明显的。3.2 网络结构与训练策略设计既然是深度学习算法项目网络结构这部分得讲清楚。项目里用的是经典多层感知机结构三层隐藏层逐步压缩特征维度from tensorflow.keras import Sequential from tensorflow.keras.layers import Dense, Dropout from tensorflow.keras.optimizers import Adam model Sequential([ Dense(64, activationrelu, input_shape(input_dim,)), Dropout(0.3), Dense(32, activationrelu), Dropout(0.3), Dense(16, activationrelu), Dense(1, activationsigmoid) ]) model.compile( optimizerAdam(learning_rate1e-3), lossbinary_crossentropy, metrics[AUC, Recall] )为什么是三层而不是更深的网络因为样本量就摆在那层数过多很容易把训练集背下来导致验证集表现差。64→32→16的逐层压缩思路是把输入特征慢慢抽象成更高层的表示。每个隐藏层后面接了Dropout比例设为0.3训练时随机丢掉一部分神经元的输出等于在隐式地做模型集成这是MLP防止过拟合最有效的手段之一。训练时设置早停机制监控验证集损失连续十个epoch没有下降就停止训练最大训练轮次限制在100。这样既能保证模型充分收敛又不会在平台期白白浪费时间。最终保存的是一个H5格式的模型文件后面Django系统直接加载这个文件做在线推理。3.3 评估指标这个项目的“分数”应该怎么打我见过很多同学在论文里写“模型准确率达到95%”这其实是个危险表述。在正负样本比接近1:19的数据集里准确率是一个高度误导性的指标——模型把所有样本都预测为阴性准确率照样有95%。答辩老师大概率会抓住这一点追问到时候就很难收场。所以我展示模型效果时只用三个核心指标召回率、AUC、F1分数。召回率说明“真实中风的人里模型抓到了多少”AUC说明“模型把高风险和低风险人群区分开的能力”F1分数则综合考虑了精确率和召回率。测试集上的混淆矩阵也一定要画出来真实中风样本有多少被正确识别、多少被漏掉一眼就能看清。我的测试集里原始不平衡训练时模型几乎只抓到少数几个中风样本加入SMOTE和class_weight之后召回率从个位数百分比升到了40%以上AUC稳定在0.85左右。这个提升过程本身就是很好的答辩素材。还要检查过拟合的痕迹训练集的AUC如果明显高于验证集就说明模型背题了这时优先调大Dropout比例或者减少隐藏层宽度而不是继续加epoch。4. Django落地把模型和数据变成看得见的系统4.1 工程项目结构与模块划分建模只是项目的一半另一半是把模型结果和统计图表装进Django系统里。项目结构我按职责拆成了四个模块避免所有逻辑堆在views.py里变成一坨不可维护的代码stroke_project/ ├── manage.py ├── config/ │ ├── settings.py │ └── urls.py ├── analysis/ │ ├── views.py │ ├── urls.py │ ├── services.py │ └── data_processor.py ├── prediction/ │ ├── views.py │ ├── urls.py │ └── model_api.py ├── static/ │ ├── css/ │ └── js/ ├── templates/ │ ├── dashboard.html │ └── predict.html └── models/ └── stroke_model.h5analysis模块负责所有统计图表的数据接口比如总体概览、筛选条件过滤后的统计数据prediction模块专门负责模型加载和预测推理data_processor.py里存放特征编码器和归一化器它们也要独立保存成pickle文件和模型一起供Django调用。这样拆分之后每一层都是独立的改前端不影响模型换模型也不影响统计接口。4.2 可视化大屏设计每张图表都要有落点前端可视化我选了ECharts而不是后端Matplotlib生成图片原因是ECharts天然支持交互筛选联动效果好而且中文资料多遇到问题容易找到解决方案。本地探索阶段可以用Seaborn和Plotly但最终呈现在Django页面上的一定是浏览器端的交互图表。大屏上我放了八个模块每张图都对应一个可讲述的分析结论总览卡片总样本量、中风人数、患病率、性别分布年龄分箱中风率柱状图展示风险随年龄上升的趋势性别×高血压分组堆叠柱状图展示不同性别、不同血压状态下中风率的差异血糖区间与中风率折线图展示血糖升高带来的风险变化BMI分组箱线图观察中风险和非中风人群的体型差异特征重要性排序条形图让模型的黑盒结论可解释ROC曲线和AUC直观呈现模型区分能力混淆矩阵热力图回应“模型到底抓到了多少人”这个问题页面逻辑是页面加载时先请求一个总览接口拿到数据后渲染各图表。筛选器部分放了年龄段、性别、高血压三个下拉框用户选择条件后前端带着参数重新请求筛选统计接口返回结果后图表联动更新。这种交互虽然实现起来不复杂但演示效果非常加分答辩现场操作一段就能让老师直观感受到系统是“活”的。4.3 模型推理接口实时风险评估怎么做在线预测是另一个核心亮点用户在前端表单输入自己的年龄、血糖、BMI、是否高血压等信息后端调用已训练好的模型实时返回中风风险概率。模型加载这块有个细节值得单独说不要在每次请求里都调用load_model。模型文件加载一次需要几百毫秒如果每个用户请求都加载一次并发稍微一高接口就废了。正确做法是模块启动时加载一次并缓存到全局变量from tensorflow.keras.models import load_model _model None def get_model(): global _model if _model is None: _model load_model(models/stroke_model.h5) return _model预测接口设计成POST请求接收前端传来的JSON数据。后端拿到参数后先做格式校验和类型转换然后复用训练时保存的编码器和归一化器把输入数据转换成和训练阶段完全一致的特征向量再调用模型的predict方法输出概率值。返回结果包含三个字段风险概率、风险等级、提示语。{ risk_probability: 0.73, risk_level: 中高风险, suggestion: 建议结合体检报告进一步评估 }这里最容易翻车的地方是特征顺序。训练时特征向量的排列顺序必须和生产环境推理时完全一致否则模型拿到的是一堆错位的数据结果毫无意义。我的解决方式是把编码器和归一化器对象连同特征列顺序列表一起保存为pickle文件推理时先读取列顺序再按顺序构造输入向量从根上避免手动拼字段出错。5. 答辩与复盘容易被追问的几个关键点5.1 数据来源和合规性怎么说答辩时“数据哪来的”基本是必问题。正确的说法是项目使用的是公开的脱敏研究数据集字段中不包含姓名、身份证号、联系方式等任何个人身份信息每一条记录都是匿名的健康指标和标签整个项目仅用于学术研究和系统演示不涉及真实患者的隐私数据也不构成任何医学建议。要特别注意不要随口说数据来自某家医院、某个科室。如果是公开数据就说公开数据如果有合作才说合作医疗数据的授权链条在答辩现场经不起追问编造数据来源是高风险操作。5.2 模型可解释性黑盒模型怎么向医学人员交代导师可能会追问一个很实际的问题神经网络是黑盒你怎么向临床医生解释这个模型为什么给出高风险判断这个问题如果答不上来前面模型做得再好也会打折扣。我做了两个层面的工作。第一是特征重要性排序利用置换重要性或者集成树模型的特征重要性给每个特征打一个分数排序结果清晰显示年龄、平均血糖、BMI排在前三这和医学常识保持一致。第二是SHAP值分析挑出预测为高风险的样本逐样本展示每个特征对其风险概率的贡献方向正面特质和反面特质一目了然。这两项工作做完之后黑盒说服力的问题基本就化解了模型内部虽然复杂但输入和输出之间的整体逻辑仍然可以用特征归因来解释。5.3 项目的局限性和扩展方向主动讲局限性和扩展方向是答辩加分的姿态。我总结了这个项目可以坦诚接受的几个不足样本量只有几千条模型泛化能力有限换一个地区的数据集可能效果会波动。数据是横截面快照缺乏时间维度比如一个人的血压在半年内的变化趋势没有纳入。特征不完整缺少血脂、家族史、既往中风史等对评估也很重要的指标。扩展方向可以从三个角度切入一是接入更大规模的多中心数据集让模型覆盖更多人群二是把静态预测升级为时序预测比如记录多次随访数据后预测未来一段时间的中风风险三是在Django系统中加入更多交互维度例如让用户调整不同指标的取值观察风险概率的实时变化趋势。这几个方向既符合医学研究趋势也体现你对项目延展性有思考。做这类医工交叉项目我最大的体会是最有价值的不是跑出来多高的AUC而是把整条链路从头到尾走通了。数据清洗时踩过的编码顺序坑、训练模型时反复调类别权重的过程、Django里模型加载缓存的优化这些环节单独拎出来都不算难但组合在一起就是一个完整的工程思维训练。最后再分享一条实操建议如果时间紧张先跑通最小闭环——数据读进来、模型训练完、页面能打开——然后再去叠加筛选联动、SHAP解释这些花活。不少项目死在“想一次做完整”上分阶段交付反而更容易收尾。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑