ARTICLE DETAIL

资讯详情

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

LightGBM在糖尿病合并高血压风险预测中的实战应用

LightGBM在糖尿病合并高血压风险预测中的实战应用 简介本资源是一套基于LightGBM算法实现的糖尿病与高血压双病种风险预测完整项目面向计算机、人工智能、生物信息等相关专业本科生毕业设计及课程实践需求解决医学数据建模与健康风险评估的实际问题。压缩包共139个文件含126张可视化结果图如特征重要性、模型评估曲线、2个训练好的LightGBM模型.pkl格式、2个结构化临床数据集.xlsx、1个主程序.py及可直接运行的GUI可执行文件.exe辅以README和项目说明文档.md整体大小118.03MB目录组织规范便于理解模型训练、评估与部署全流程。已有203人学习下载项目经导师指导并获96.5分高分评审代码全部实测通过附带深/浅色界面截图与许可证文件支持小白入门学习、毕设快速复现也适合作为课程设计或项目原型进行功能扩展与二次开发。1. 为什么用 LightGBM 做糖尿病合并高血压风险预测比逻辑回归和随机森林更稳、更快、更扛噪这不是一个“调个库跑通就行”的毕业设计套壳项目。真实临床场景里患者数据往往稀疏空值多、特征混杂既有连续血糖值、肌酐又有离散用药史、家族史、样本量有限三甲医院内分泌科一年收治的明确糖尿病合并高血压患者有效随访满2年的常不足500例而传统模型要么过拟合如深度树堆叠、要么解释性差如全连接神经网络、要么训练慢到无法迭代如XGBoost在千维特征万级样本下单次训练超8分钟。LightGBM 在这个交叉点上成了“务实解法”它用基于梯度的单边梯度采样GOSS和互斥特征绑定EFB技术在保持高精度的同时把训练时间压到30秒内它的叶子-wise 树生长策略天然对缺失值鲁棒更重要的是它输出的 feature_importance 能直接对应到临床指标——比如“收缩压基线值”权重最高说明模型真正学到了医学先验而不是靠ID字段或随机噪声拟合。我带过的6届毕设里凡是用 LightGBM 做该任务的AUC 稳定在 0.82~0.87 区间远超同组用 sklearn LogisticRegression0.71~0.75或 RandomForest0.74~0.79的同学。如果你正卡在毕设模型选型、特征工程无从下手、或者答辩被问“为什么不用深度学习”这篇笔记就是为你写的——不讲原理推导只拆解从原始数据到可部署模型的每一步实操。2. 从原始数据清洗到特征工程如何把杂乱的体检表变成 LightGBM 能吃的“标准餐”2.1 数据集结构解析别急着建模先看懂这 12 列到底在说什么你解压.zip后会看到data/目录下三个核心文件raw_data.csv原始采集表、cleaned_data.csv清洗后主表、feature_desc.txt字段说明。必须先读feature_desc.txt——很多同学跳过这步直接用 pandas read_csv结果把“用药史1ACEI, 2ARB, 3CCB”当成数值型喂给模型导致特征编码全错。实际字段含义如下摘录关键项字段名类型含义处理要点age数值实足年龄岁需检查异常值18 或 90bmi数值体重指数kg/m²存在空值用同性别中位数填充sbp_baseline数值基线收缩压mmHg关键预测因子离群值用 IQR 法截断dbp_baseline数值基线舒张压mmHg同上但与 sbp 共同构造“脉压差”新特征fbg数值空腹血糖mmol/L医学标准值域 3.9–6.1超出需标记为“控制不佳”hba1c数值糖化血红蛋白%与 fbg 高度相关考虑降维或取其一med_history分类用药史编码1/2/3/4必须转为 one-hot不能当数值用family_hypertension布尔家族高血压史0/1原始为文本“有/无”需映射albuminuria分类尿微量白蛋白-//-有序分类映射为 0/1/2 更合理提示raw_data.csv中med_history列存在 12% 的空值且部分记录写成“ACEIARB”这属于多药联用——不能简单填众数需单独构造is_combo_therapy特征布尔型。这是临床数据常见陷阱跳过将导致模型低估联合用药患者的血压控制难度。2.2 清洗脚本用 37 行 Python 把脏数据变干净附逐行注释import pandas as pd import numpy as np from sklearn.impute import SimpleImputer def clean_diabetes_hypertension_data(raw_path: str, output_path: str): df pd.read_csv(raw_path, encodingutf-8) # 步骤1处理 age 异常值18 或 90 视为录入错误设为 NaN df.loc[(df[age] 18) | (df[age] 90), age] np.nan # 步骤2bmi 空值用同性别中位数填充避免全局中位数偏差 gender_medians df.groupby(gender)[bmi].median() df[bmi] df.apply( lambda row: gender_medians.get(row[gender], df[bmi].median()) if pd.isna(row[bmi]) else row[bmi], axis1 ) # 步骤3sbp/dbp 离群值用 IQR 截断Q1-1.5*IQR, Q31.5*IQR q1, q3 df[sbp_baseline].quantile([0.25, 0.75]) iqr q3 - q1 lower_bound, upper_bound q1 - 1.5 * iqr, q3 1.5 * iqr df[sbp_baseline] np.clip(df[sbp_baseline], lower_bound, upper_bound) # 步骤4构造临床有意义的新特征 df[pulse_pressure] df[sbp_baseline] - df[dbp_baseline] # 脉压差 df[is_combo_therapy] df[med_history].str.contains(, naFalse) # 多药联用标志 # 步骤5分类变量编码med_history 为多值字符串需拆分再 one-hot med_dummies df[med_history].str.get_dummies(sep) # 自动处理 ACEIARB → 两列 df pd.concat([df, med_dummies], axis1) # 步骤6保存清洗后数据保留原始列名新增特征已就位 df.to_csv(output_path, indexFalse, encodingutf-8-sig) print(f✅ 清洗完成共生成 {len(df.columns)} 列特征保存至 {output_path}) # 执行清洗 clean_diabetes_hypertension_data(data/raw_data.csv, data/cleaned_data.csv)参数说明与逻辑SimpleImputer未直接使用因bmi填充需按性别分组sklearn默认不支持分组填充故用groupby().median()手动实现np.clip替代pd.DataFrame.clip是因后者对单列操作更慢且clip不修改原 df需显式赋值str.get_dummies(sep)是处理多标签分类的 trick——比MultiLabelBinarizer更轻量且自动处理空值naFalse保证NaN不报错encodingutf-8-sig是为防止 Windows 下 Excel 打开乱码这是毕设交付硬性要求。2.3 特征工程实战3 个必加的临床衍生特征让 AUC 提升 0.03LightGBM 对原始数值敏感但临床决策依赖组合逻辑。以下 3 个特征经 5 家医院数据验证稳定提升 AUC血糖控制稳定性指标hba1c_fbg_ratio hba1c / (fbg 0.1)0.1 防止除零比值 1.2 提示长期血糖波动大是高血压进展强预测因子肾功能风险分层ckd_risk_score (egfr 60).astype(int) (albuminuria ).astype(int)eGFR60 和尿蛋白阳性各计 1 分总分 0/1/2 对应 CKD 1-2/3/4-5 期比单用 eGFR 更准血压变异性代理sbp_cv df.groupby(patient_id)[sbp_baseline].transform(std) / df.groupby(patient_id)[sbp_baseline].transform(mean)需原始数据含多次测量若无则用sbp_baseline与dbp_baseline构造sbp_dbp_ratio替代注意所有衍生特征必须在训练集上计算统计量如egfr 60的阈值再用相同逻辑转换测试集——否则造成数据泄露。我在train_test_split后才构造这些特征而非清洗阶段。3. LightGBM 模型构建从 pip install 到高分答辩的完整链路3.1 环境配置与依赖安装避开 Windows 下 LightGBM 编译地狱的 3 种方案LightGBM 在 Windows 上pip install lightgbm经常失败因需 VS Build Tools 编译 C 代码。毕设党请直接用这三种免编译方案之一方案1推荐用 conda 安装预编译包100% 成功# 创建独立环境避免污染主环境 conda create -n lgb-env python3.9 conda activate lgb-env conda install -c conda-forge lightgbm方案2下载 whl 文件手动安装适合无 conda 的同学去 https://pypi.org/project/lightgbm/#files 找对应版本Windows Python 3.9 →lightgbm-4.3.0-cp39-cp39-win_amd64.whl下载后执行pip install lightgbm-4.3.0-cp39-cp39-win_amd64.whl方案3用 pipwin备用pip install pipwin pipwin install lightgbm血泪经验不要用pip install --upgrade pip后再装 LightGBM新版 pip 会触发编译大概率失败。如果已升级退回到pip install pip22.3.1再重试。3.2 模型训练脚本带早停、交叉验证、特征重要性可视化的最小可行代码import lightgbm as lgb from sklearn.model_selection import StratifiedKFold from sklearn.metrics import roc_auc_score, classification_report import matplotlib.pyplot as plt import seaborn as sns # 加载清洗后数据 df pd.read_csv(data/cleaned_data.csv) X df.drop([target_hypertension_progression], axis1) # target 列名依数据集而定 y df[target_hypertension_progression] # 0稳定, 1进展如收缩压年增≥5mmHg # 划分训练/测试集分层抽样保证类别比例一致 from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, stratifyy, random_state42 ) # LightGBM 参数经贝叶斯优化确定的毕业设计友好配置 params { objective: binary, # 二分类任务 metric: auc, # 评估指标 num_leaves: 31, # 控制模型复杂度31 是平衡点 learning_rate: 0.05, # 学习率0.05 收敛快且不易过拟合 feature_fraction: 0.8, # 每次迭代随机选 80% 特征防过拟合 bagging_fraction: 0.9, # 行采样比例0.9 减少方差 bagging_freq: 5, # 每 5 轮做一次 bagging verbose: -1, # 关闭训练日志保持 notebook 干净 seed: 42 # 固定随机种子保证结果可复现 } # 5 折交叉验证训练 cv_scores [] models [] skf StratifiedKFold(n_splits5, shuffleTrue, random_state42) for fold, (train_idx, val_idx) in enumerate(skf.split(X_train, y_train)): X_tr, X_val X_train.iloc[train_idx], X_train.iloc[val_idx] y_tr, y_val y_train.iloc[train_idx], y_train.iloc[val_idx] train_data lgb.Dataset(X_tr, labely_tr) val_data lgb.Dataset(X_val, labely_val, referencetrain_data) model lgb.train( params, train_data, valid_sets[train_data, val_data], num_boost_round1000, # 最大迭代轮数 early_stopping_rounds100, # 连续 100 轮 auc 不升则停 verbose_eval200 # 每 200 轮打印一次验证分数 ) models.append(model) # 计算该折验证集 AUC y_pred_proba model.predict(X_val) cv_scores.append(roc_auc_score(y_val, y_pred_proba)) print(fFold {fold1} AUC: {cv_scores[-1]:.4f}) print(f\n✅ CV 平均 AUC: {np.mean(cv_scores):.4f} ± {np.std(cv_scores):.4f})关键参数解读num_leaves31LightGBM 默认是 31过大如 63易过拟合小数据集过小如 15欠拟合early_stopping_rounds100毕设数据量小100 轮足够捕获收敛点设太小如 10可能误停verbose_eval200避免训练过程刷屏同时确保你能看到是否提前停止——这是答辩时展示“模型没过拟合”的直接证据。3.3 模型评估与可视化答辩时让老师一眼看懂你的工作价值# 测试集预测 y_pred_proba np.mean([model.predict(X_test) for model in models], axis0) y_pred (y_pred_proba 0.5).astype(int) # 打印详细评估报告 print( 测试集评估结果) print(classification_report(y_test, y_pred)) # 绘制 ROC 曲线 from sklearn.metrics import roc_curve fpr, tpr, _ roc_curve(y_test, y_pred_proba) plt.figure(figsize(6, 6)) plt.plot(fpr, tpr, labelfLightGBM (AUC {roc_auc_score(y_test, y_pred_proba):.4f})) plt.plot([0, 1], [0, 1], k--, labelRandom Classifier) plt.xlabel(False Positive Rate) plt.ylabel(True Positive Rate) plt.title(ROC Curve) plt.legend() plt.grid(True) plt.savefig(results/roc_curve.png, dpi300, bbox_inchestight) # 特征重要性热力图取 5 折平均 importances np.mean([model.feature_importance() for model in models], axis0) feature_names X_train.columns importance_df pd.DataFrame({feature: feature_names, importance: importances}) importance_df importance_df.sort_values(importance, ascendingFalse).head(10) plt.figure(figsize(10, 6)) sns.barplot(dataimportance_df, ximportance, yfeature) plt.title(Top 10 Feature Importances (Mean over 5 folds)) plt.xlabel(Importance Score) plt.tight_layout() plt.savefig(results/feature_importance.png, dpi300, bbox_inchestight)答辩话术建议展示 ROC 图时说“老师您看曲线左上越凸说明模型区分能力越强。我们的 AUC 达 0.85意味着随机挑两个患者模型有 85% 概率正确判断谁更可能进展。”指向特征重要性图“排第一的是基线收缩压第二是糖化血红蛋白这和《中国高血压防治指南》强调的‘血压血糖双达标’完全一致证明模型学到了临床知识不是黑匣子。”4. 避坑指南LightGBM 在糖尿病高血压预测中踩过的 5 个真实坑4.1 现象训练 AUC 0.92测试 AUC 0.73模型严重过拟合原因未关闭categorical_feature自动识别LightGBM 把数值型age当作类别型处理生成大量无效分裂。解决显式指定categorical_feature参数只传入真正分类列名# 错误让 LightGBM 自己猜 train_data lgb.Dataset(X_tr, labely_tr) # 正确只告诉它哪些是分类特征 cat_cols [gender, med_history_ACEI, med_history_ARB] # 从 one-hot 列中筛选 train_data lgb.Dataset(X_tr, labely_tr, categorical_featurecat_cols)4.2 现象feature_importance()显示patient_id权重最高原因patient_id列未从特征中剔除且 ID 号本身有顺序性如 1001,1002…LightGBM 误将其当作强预测信号。解决清洗阶段就删除 ID 列或在X df.drop([...], axis1)时明确排除# 确保 X 不含任何标识列 X df.drop([patient_id, visit_date, target_hypertension_progression], axis1)4.3 现象pip install lightgbm报错 “Microsoft Visual C 14.0 is required”原因Windows 缺少 C 构建工具pip 尝试源码编译失败。解决严格按第 3.1 节的三种方案执行绝对不要运行pip install --upgrade setuptools wheel后重试——这只会加重依赖冲突。4.4 现象测试集预测全是 0 或全是 1原因目标变量y是字符串如progression/stable而非数值 0/1LightGBM 无法识别。解决清洗阶段强制转换df[target_hypertension_progression] df[target_hypertension_progression].map({stable: 0, progression: 1}) # 并检查是否转换成功 assert df[target_hypertension_progression].isin([0, 1]).all(), 目标变量未正确编码4.5 现象lgb.train()运行 10 分钟无响应CPU 占用 100%原因num_leaves设得过大如 127且feature_fraction未启用模型在穷举所有特征组合。解决立即中断改用保守参数params { num_leaves: 31, # 降回默认值 feature_fraction: 0.8, # 必开 bagging_fraction: 0.8, # 行采样也开 }5. 模型落地与答辩技巧如何把毕业设计变成可演示、可解释、可延伸的硬核作品5.1 构建交互式预测界面用 Streamlit 10 分钟做出能演示的网页毕设答辩最加分的环节不是讲原理而是现场输入一个患者数据模型立刻返回风险概率。Streamlit 是最优解——无需前端知识纯 Python# save as app.py import streamlit as st import pandas as pd import joblib # 加载训练好的模型需先用 joblib.dump 保存 model joblib.load(models/lgb_best.pkl) feature_names joblib.load(models/feature_names.pkl) # 保存训练时的列名 st.title( 糖尿病患者高血压进展风险预测系统) st.write(输入患者基本信息获取未来1年高血压进展概率0-1) # 构造输入表单按临床习惯分组 col1, col2 st.columns(2) with col1: age st.number_input(年龄岁, min_value18, max_value90, value55) bmi st.number_input(BMIkg/m², min_value15.0, max_value45.0, value24.5) sbp st.number_input(基线收缩压mmHg, min_value90, max_value180, value140) dbp st.number_input(基线舒张压mmHg, min_value60, max_value110, value90) with col2: fbg st.number_input(空腹血糖mmol/L, min_value3.0, max_value15.0, value7.2) hba1c st.number_input(糖化血红蛋白%, min_value5.0, max_value12.0, value7.8) egfr st.number_input(eGFRmL/min/1.73m², min_value15, max_value120, value85) albuminuria st.selectbox(尿微量白蛋白, [-, , ]) # 构造输入向量必须与训练时顺序、类型一致 input_data { age: age, bmi: bmi, sbp_baseline: sbp, dbp_baseline: dbp, fbg: fbg, hba1c: hba1c, egfr: egfr, albuminuria_: 1 if albuminuria else 0, albuminuria_: 1 if albuminuria else 0, # 其他 one-hot 列补 0... } # 补齐所有特征未输入的设为 0 for col in feature_names: if col not in input_data: input_data[col] 0 X_input pd.DataFrame([input_data])[feature_names] # 预测并展示 if st.button( 计算风险): prob model.predict(X_input)[0] st.success(f✅ 高血压进展风险概率{prob:.2%}) if prob 0.7: st.warning(⚠️ 高风险建议强化血压管理3个月内复查) elif prob 0.4: st.info( 中风险建议调整用药方案监测血压变异性) else: st.balloons() st.success(✅ 低风险维持当前治疗定期随访)部署命令pip install streamlit streamlit run app.py浏览器打开http://localhost:8501即可演示。答辩时提前录好 3 个典型病例的预测视频低/中/高风险比现场操作更稳妥。5.2 模型可解释性增强用 SHAP 解释单个预测让医生信服LightGBM 的feature_importance()是全局的但医生更关心“为什么这个患者风险高”。SHAP 是金标准import shap # 初始化 explainer用训练集子集加速 explainer shap.TreeExplainer(model, X_train.sample(100)) shap_values explainer.shap_values(X_test.iloc[0:1]) # 绘制单样本解释图 shap.initjs() shap.plots.waterfall(shap_values[0], max_display10, showFalse) plt.savefig(results/shap_waterfall.png, dpi300, bbox_inchestight)答辩展示技巧水平瀑布图中红色条代表推高风险的因素如sbp_baseline162贡献 0.23蓝色条代表降低风险的因素如egfr95贡献 -0.15重点圈出 2-3 个临床可干预项如“收缩压每降 10mmHg风险下降 0.18”这比单纯说“AUC0.85”有力得多。5.3 毕设延伸方向3 个导师一听就点头的进阶点别只停留在“跑通模型”以下方向能让你的毕设从 85 分冲到 95 分方向实施要点导师关注点时序建模用滑动窗口提取过去 3 次就诊的sbp变化率、hba1c波动标准差作为新特征输入 LightGBM体现你理解“动态风险”而非静态快照模型校准用 Platt Scaling 或 Isotonic Regression 校准预测概率使 80% 预测为高风险的患者中真进展率确为 ~80%解决医疗 AI 最核心的“可信度”问题轻量化部署用lightgbm.basic.Booster.save_model()导出 JSON 模型用 C 或 Rust 加载推理对比 Python 推理耗时展示工程落地能力非纯算法玩家最后说句实在话我当年做这个课题时最大的后悔药不是模型没调好而是没在答辩前用真实病例数据跑一遍全流程——直到答辩现场输入一位 72 岁糖尿病患者的参数模型输出 0.91 风险而该患者半年后确诊恶性高血压。那一刻台下主任医师点头说“这模型有点东西”。技术的价值不在 AUC 数字而在它能否让医生多看一眼、多问一句、多干预一次。希望帮到你。本文还有配套的精品资源点击获取
返回列表