ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于Python与机器学习的急性心肌梗死死亡风险预测实战

基于Python与机器学习的急性心肌梗死死亡风险预测实战 简介本资源面向高校学生与开发者提供一套基于Python与机器学习的急性心肌梗死死亡风险预测完整项目适用于毕业设计、课程设计及项目开发练手。项目以MIMIC数据库数据为训练集围绕重症监护场景下的死亡风险建模涵盖数据预处理、特征工程与多种集成学习模型的训练流程可作为医疗数据挖掘方向的入门参考。压缩包共14个文件约5.7MB包含4个Python脚本、3个CSV数据文件、2个SQL查询脚本以及xlsx、txt、md说明文档与开源协议文件脚本与数据分离便于按模块阅读和二次开发。目前已有236人学习下载。读者可获得可运行的源码、数据读取与预处理脚本、模型训练代码及配套说明文档并在此基础上替换数据集或调整特征延伸出肾衰竭等其他疾病的风险预测实验积累从数据清洗到模型评估的完整实践思路。1. 急性心肌梗死死亡风险预测从一份临床数据到能跑通的模型急性心肌梗死的院内死亡风险预测是机器学习在临床场景里少有的「特征清晰、标签明确、样本量够用」的落地方向。拿到一份包含年龄、肌钙蛋白、Killip 分级、射血分数、心率、血压等字段的表格数据用 Python 加机器学习做二分类把死亡风险分成高危和低危这件事本身并不玄学。真正难的是数据里缺失值怎么填、类别不平衡怎么处理、95% 的准确率到底是怎么来的、换一批数据还能不能稳住。这篇笔记就围绕「基于 Python 机器学习的急性心肌梗死死亡风险预测」这个题目把数据预处理、特征工程、模型选型、训练调参、评估验证、踩坑排查整条链路讲清楚适合做毕业设计、课程设计或者想快速搭一个可复现基线的人。读完你能自己跑出一套完整流程也能判断别人报的 95% 到底靠不靠谱。2. 数据准备与特征工程把临床表格变成模型能吃的矩阵2.1 先搞清楚字段含义再动手拿到数据第一件事不是pd.read_csv然后model.fit而是逐列确认含义。急性心肌梗死预测里常见的字段大致分几类人口学年龄、性别、生命体征收缩压、舒张压、心率、呼吸频率、实验室指标肌钙蛋白、肌酸激酶、血糖、肌酐、白细胞、心功能左室射血分数 LVEF、Killip 分级、病史高血压、糖尿病、吸烟、既往心梗、以及结局标签院内死亡 0/1。这里有个血泪经验Killip 分级是 I 到 IV 的有序类别直接当连续数值喂进去模型会误以为 IV 是 I 的 4 倍实际它只是等级。正确做法是做 one-hot 或者保留序关系用有序编码。同理性别、吸烟这类二值字段保持 0/1 即可不要做标准化。import pandas as pd import numpy as np df pd.read_csv(ami_data.csv) # 确认标签分布先看正负样本比例 print(df[death].value_counts(normalizeTrue)) # 有序类别Killip 分级保留顺序用映射而不是 one-hot killip_map {I: 1, II: 2, III: 3, IV: 4} df[killip] df[killip].map(killip_map) # 无序类别性别、吸烟等做 one-hot df pd.get_dummies(df, columns[sex, smoke], drop_firstTrue)逻辑说明value_counts(normalizeTrue)先看标签比例这一步决定了后面要不要做重采样。Killip 用映射保留序关系是因为它本身有轻重之分one-hot 会丢掉这个信息。drop_firstTrue避免虚拟变量陷阱防止多重共线性。参数说明normalizeTrue输出比例而非计数drop_firstTrue在二分类字段上其实只保留一列对性别这种二值字段效果就是 0/1 编码。2.2 缺失值处理别一上来就均值填充临床数据缺失是常态肌钙蛋白、LVEF 这些关键指标缺失率可能到 10% 到 30%。直接fillna(df.mean())是最省事也最容易翻车的做法因为它会抹掉缺失本身的含义——很多时候「没测某项指标」本身就是病情轻或重的信号。我一般分三步走缺失率超过 40% 的字段直接考虑丢弃除非它是核心指标缺失率 5% 到 40% 的用多重插补或者 KNN 插补缺失率低于 5% 的用中位数填充并加一个缺失指示列。from sklearn.impute import KNNImputer # 先标记缺失保留缺失模式信息 for col in [troponin, lvef, creatinine]: df[f{col}_missing] df[col].isna().astype(int) # 对数值列做 KNN 插补n_neighbors 取 5 是常用起点 num_cols [age, sbp, dbp, heart_rate, troponin, lvef, creatinine] imputer KNNImputer(n_neighbors5) df[num_cols] imputer.fit_transform(df[num_cols])逻辑说明先加_missing指示列让模型知道这个值原本是缺失的这在临床预测里经常能提升效果。KNN 插补用样本间相似度找邻居比均值填充更贴近个体情况。参数说明n_neighbors5是经验值样本量小可以降到 3样本量大可以升到 7 到 10。注意 KNNImputer 必须在训练集上 fit再 transform 测试集否则会数据泄漏。2.3 类别不平衡95% 准确率的陷阱就在这里急性心肌梗死院内死亡率通常不高正样本死亡可能只占 5% 到 15%。如果数据里 90% 是存活模型全预测存活就能拿 90% 准确率这种「准确率」毫无意义。所以看到别人报 95% 准确率第一反应应该是问正负样本比例多少用的什么评估指标处理不平衡常见三种做法SMOTE 过采样、类别权重调整、阈值移动。我一般优先用class_weightbalanced因为它不改数据分布只是让损失函数对少数类更敏感工程上最稳。from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import train_test_split X df.drop(columns[death]) y df[death] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, stratifyy, random_state42 ) clf RandomForestClassifier( n_estimators300, max_depth8, class_weightbalanced, random_state42 ) clf.fit(X_train, y_train)逻辑说明stratifyy保证训练集和测试集的正负比例一致避免随机切分导致某一侧正样本过少。class_weightbalanced自动按类别频率反比给权重少数类权重更高。参数说明n_estimators300是树数量太少欠拟合太多训练慢且收益递减max_depth8控制树深防止过拟合临床数据特征不多时 6 到 10 比较合适。3. 模型选型与训练为什么树模型是这类表格数据的首选3.1 逻辑回归、随机森林、XGBoost 怎么选表格型临床数据特征维度通常几十到上百样本量几千到几万这种场景下梯度提升树XGBoost、LightGBM和随机森林往往比深度学习更稳。逻辑回归可解释性最强系数直接对应优势比适合写论文时讲清楚每个因素的方向但拟合非线性关系能力弱。我的选型顺序是先跑逻辑回归做基线看 AUC 大概什么水平再上随机森林看特征重要性最后用 XGBoost 或 LightGBM 冲性能。如果三者差距不大就选可解释性最好的那个临床场景里医生更信能解释的模型。from sklearn.linear_model import LogisticRegression from sklearn.preprocessing import StandardScaler from sklearn.pipeline import Pipeline from sklearn.metrics import roc_auc_score # 逻辑回归必须标准化树模型不需要 lr_pipe Pipeline([ (scaler, StandardScaler()), (clf, LogisticRegression(class_weightbalanced, max_iter1000)) ]) lr_pipe.fit(X_train, y_train) lr_prob lr_pipe.predict_proba(X_test)[:, 1] print(LR AUC:, roc_auc_score(y_test, lr_prob))逻辑说明逻辑回归对特征尺度敏感所以用 Pipeline 把标准化和模型绑在一起避免测试集泄漏。predict_proba取第 1 列是正类概率AUC 用概率算而不是硬标签。参数说明max_iter1000防止不收敛警告class_weightbalanced同样处理不平衡。AUC 是评估不平衡数据最常用的指标0.5 是随机0.8 以上算不错0.9 以上要警惕泄漏。3.2 XGBoost 训练与早停XGBoost 在表格数据上通常能比随机森林再高一点但参数多容易过拟合。关键是设好eval_set和early_stopping_rounds让它在验证集 AUC 不再提升时自动停。import xgboost as xgb dtrain xgb.DMatrix(X_train, labely_train) dtest xgb.DMatrix(X_test, labely_test) params { objective: binary:logistic, eval_metric: auc, max_depth: 5, eta: 0.05, subsample: 0.8, colsample_bytree: 0.8, scale_pos_weight: (y_train 0).sum() / (y_train 1).sum() } model xgb.train( params, dtrain, num_boost_round1000, evals[(dtest, test)], early_stopping_rounds50, verbose_eval100 )逻辑说明scale_pos_weight是不平衡数据的 XGBoost 版处理方式等于负样本数除以正样本数。早停用测试集做验证在这里只是演示严格做法应该从训练集再切一个验证集测试集只在最后用一次。参数说明max_depth5比随机森林浅因为 boosting 是串行叠加深树容易过拟合eta0.05学习率小一点配合更多轮数更稳subsample和colsample_bytree都是 0.8增加随机性防过拟合。3.3 交叉验证单次切分的 95% 不可信单次 train_test_split 得到的准确率波动很大换一个 random_state 可能从 95% 掉到 88%。要报可信的数字必须做交叉验证最好分层 K 折。from sklearn.model_selection import StratifiedKFold, cross_val_score cv StratifiedKFold(n_splits5, shuffleTrue, random_state42) scores cross_val_score(clf, X, y, cvcv, scoringroc_auc) print(AUC mean: %.4f, std: %.4f % (scores.mean(), scores.std()))逻辑说明StratifiedKFold保证每折正负比例一致scoringroc_auc用 AUC 而不是准确率。输出均值和标准差标准差大说明模型不稳定需要检查数据或调参。参数说明n_splits5是常用折数样本少可以到 10样本多 5 折够用。shuffleTrue打乱顺序避免数据本身有序带来的偏差。4. 评估与可解释性准确率之外必须看的指标4.1 混淆矩阵、召回率、F1 怎么读临床死亡风险预测里漏报一个高危病人假阴性的代价远大于误报一个低危病人假阳性。所以召回率Recall比准确率重要得多。一个模型准确率 95% 但召回率只有 40%意味着六成死亡病例被漏掉这种模型不能上线。from sklearn.metrics import classification_report, confusion_matrix y_pred clf.predict(X_test) print(confusion_matrix(y_test, y_pred)) print(classification_report(y_test, y_pred, digits4))逻辑说明混淆矩阵四个格子分别是真阴、假阳、假阴、真阳。分类报告给出每类的精确率、召回率、F1。重点看死亡类标签 1的召回率。参数说明digits4控制小数位。如果召回率低可以调低预测阈值比如把 0.5 改成 0.3牺牲精确率换召回率。4.2 特征重要性与 SHAP论文和答辩里几乎一定会被问「哪些因素最重要」。随机森林和 XGBoost 都能输出特征重要性但内置重要性对高基数特征有偏。SHAP 更可靠能给出每个样本每个特征的贡献方向。import shap explainer shap.TreeExplainer(model) shap_values explainer.shap_values(dtest) shap.summary_plot(shap_values, X_test)逻辑说明TreeExplainer对树模型有精确快速实现。summary_plot画出每个特征对预测的贡献分布点的颜色表示特征值高低位置表示推动预测往正类还是负类。参数说明SHAP 计算量随样本数增长样本大时可以抽样几百条算。shap_values对二分类返回的是正类贡献。5. 避坑与排查那些让 95% 变成 70% 的细节5.1 数据泄漏准确率虚高的头号原因现象交叉验证 AUC 0.98换一批数据掉到 0.7。原因标准化、插补、特征选择在切分之前对整个数据集做了测试集信息泄漏进训练。解决所有预处理放进 Pipeline只在训练集 fit。5.2 标签定义不一致现象模型效果忽好忽坏同一份代码两次结果差很多。原因死亡标签有的按院内死亡有的按 30 天死亡混在一起。解决确认标签口径统一必要时重新定义。5.3 缺失指示列被标准化现象加了_missing列后效果反而变差。原因0/1 指示列被 StandardScaler 标准化成负数失去含义。解决指示列不参与标准化或者用树模型跳过标准化。5.4 阈值默认 0.5现象召回率低漏掉大量高危。原因默认 0.5 阈值不适合不平衡数据。解决在验证集上画 PR 曲线选召回率满足要求的阈值。5.5 过拟合到训练集现象训练集准确率 99%测试集 85%。原因树太深、特征太多、样本太少。解决限制max_depth加min_samples_leaf用早停减少特征。6. 把模型跑稳的一个技巧分层抽样加固定随机种子最后说一个我踩过坑之后养成的习惯所有涉及随机的环节都固定random_state并且用分层抽样。这不是为了好看是为了可复现。答辩时老师让你再跑一遍结果对不上解释起来很被动。具体做法是把random_state42贯穿 train_test_split、模型初始化、交叉验证的 shuffle。同时用StratifiedKFold而不是普通 KFold保证每折正负比例一致。如果样本量允许做 10 折比 5 折更稳但训练时间翻倍自己权衡。还有一个细节保存模型时用joblib而不是pickle对 sklearn 模型兼容性更好加载速度快。import joblib joblib.dump(clf, ami_death_model.pkl) loaded joblib.load(ami_death_model.pkl) print(loaded.predict_proba(X_test[:5]))逻辑说明joblib.dump序列化模型load反序列化。保存后重新加载预测确认结果一致这是上线前必做的一步。参数说明文件后缀.pkl是惯例不是强制。大模型可以加compress3压缩但加载会慢一点。验证模型是否真的稳我的做法是换三个不同的random_state各跑一遍交叉验证看 AUC 均值波动是否在 0.02 以内。如果波动超过 0.05说明数据或流程有问题别急着报结果。这个习惯帮我挡掉过好几次「看起来很美」的假象。希望帮到你。本文还有配套的精品资源点击获取
返回列表