基于LightGBM的糖尿病高血压预测毕设全流程实战
简介这是一份面向计算机相关专业毕业设计与课程实践的完整项目包基于LightGBM框架构建糖尿病与高血压风险预测系统涵盖数据处理、模型训练、评估与可视化展示全流程适合正在做毕设或需要项目实战练习的学生参考复用。压缩包共139个文件约118.03MB主要包含Python源码、训练好的pkl模型、xlsx数据集、可执行exe程序以及126张界面截图截图可直观展示系统各模块运行效果md文档则提供项目结构与应用逻辑说明。项目经导师指导并评审通过评价96.5分代码均测试运行成功后上传读者可直接运行查看预测效果也可基于源码调整特征或算法进行二次开发作为课程设计、期末大作业或项目初期立项演示均有较强参考价值。目前已有204人学习下载适合希望快速上手LightGBM疾病预测实战的学习者。1. 为什么拿它当毕业设计从赛题到落地只差一个 LightGBM如果手里这份毕业设计叫《基于 LightGBM 的糖尿病高血压预测 python 源码 数据集 模型》那它解决的并不是一个纯算法题而是一整条“数据清洗 → 特征工程 → 模型训练 → 界面打包 → 论文展示”的链路。做过毕设的人都懂导师真正看重的不是你把准确率刷到多高而是你能不能讲清楚每个环节为什么这么做。这份资源刚好把整条链路补齐了从 hypertension.pkl、diabetes.pkl 两个现成数据集到训练好的模型再到能跑出预测结果的 .exe 程序适合计算机相关专业学生做毕设、课程设计或期末大作业也适合想从零上手 LightGBM 的初学者照着复现。我拆完第一感受是它最值钱的部分不是模型本身而是那份把“二分类风险预测”讲成完整故事的项目文档。2. 数据集与特征工程先解决“预测什么”和“喂什么”2.1 先看清两个 pkl 文件里到底装了什么拿到压缩包后先别急着跑训练脚本第一步是确认数据形态。压缩包里最核心的输入是hypertension.pkl和diabetes.pkl这两个是序列化后的 DataFrame直接 pandas.read_pickle 就能读。我一般会在项目根目录建一个explore.py把字段名列出来确认标签列到底叫什么再做后面的特征处理。import pandas as pd df_diabetes pd.read_pickle(diabetes.pkl) df_hyper pd.read_pickle(hypertension.pkl) print(diabetes shape:, df_diabetes.shape) print(diabetes columns:, df_diabetes.columns.tolist()) print(df_diabetes.head(3).to_string()) print(hypertension shape:, df_hyper.shape) print(hypertension columns:, df_hyper.columns.tolist()) print(df_hyper.head(3).to_string())这段代码的作用是把 pkl 文件里的数据先“打回原形”。pd.read_pickle读取的是二进制序列化对象列名、行数、前几行样本一打印出来就能判断这份数据是原始问卷还是已经做过清洗。常见做法是先看标签列比如label或target再看特征列里有没有身份证号、姓名这类不该进模型的字段。参数上只有一个encoding值得注意如果读取报 UnicodeDecodeError通常不是 pkl 的问题而是你环境里 pandas 版本差异可以在读取后统一df.columns [str(c) for c in df.columns]规避。2.2 缺失值、重复行和数值分布不处理干净就训不出能答辩的模型LightGBM 本身支持缺失值很多新手以为“模型能处理”就等于“不用管”这是最容易翻车的地方。LightGBM 处理缺失值是把缺失值默认分到增益最大的一侧这在样本量大的时候问题不大但毕设数据往往只有几千行缺失比例超过 20% 的字段就会变成隐形的噪声源。我建议的固定流程是先统计每列缺失比例缺失低于 1% 的列直接删除对应行缺失在 1% 到 20% 的数值列用中位数填充分类列用众数填充缺失超过 20% 的列除非它跟标签有强相关性否则直接删列。import pandas as pd import numpy as np def clean_df(df, label_col): df df.copy() missing_ratio df.isnull().mean() drop_cols missing_ratio[missing_ratio 0.20].index.tolist() df df.drop(columnsdrop_cols) for col in df.columns: if col label_col: continue if df[col].dtype in [float64, int64]: df[col] df[col].fillna(df[col].median()) else: df[col] df[col].fillna(df[col].mode()[0]) df df.drop_duplicates() return df这段函数维护了一个筛选逻辑超过 20% 缺失的列直接丢弃数值列用中位数分类列用众数。drop_duplicates()必须放在填充之后因为填充前两行可能因缺失值不同被误判为不重复填充后才会暴露出真正重复的样本。答辩时如果被问到“为什么用中位数不用均值”标准回答是均值对离群点敏感中位数稳健LightGBM 是树模型对特征尺度不敏感所以不需要标准化。2.3 特征怎么选别把十来个字段全塞进去项目正文里没有给出完整的特征清单但这类疾病风险预测数据集通常围绕年龄、性别、血压、血糖、BMI、吸烟史、家族病史等字段展开。如果原始数据只有十几列全部塞进模型也能跑但如果字段里有强相关的重复信息比如同时存在“收缩压”和“血压等级”就要处理共线性。我一般会先跑一版全特征模型然后看feature_importance把重要性接近 0 的列删掉再跑一版对比。LightGBM 的默认feature_importance基于分裂次数打印出来长这样import lightgbm as lgb importance pd.DataFrame({ feature: model.feature_name(), gain: model.feature_importance(importance_typegain) }).sort_values(gain, ascendingFalse) print(importance.head(20))importance_typegain和默认的split含义不同split是特征被用来分裂的次数gain是特征带来的总信息增益。后者更能反映真实贡献。如果某个字段在两次交叉验证里 gain 都排在倒数且删除后验证集 AUC 不降就把它拿掉。这步不是必须做但做了之后论文里能多一张“特征筛选前后对比”的表答辩老师通常吃这套。3. 训练与调参交叉验证和早停才是 LightGBM 的正确打开方式3.1 数据划分不能把 train_test_split 当默认答案很多入门教程直接train_test_split(test_size0.2)放在毕设里也能跑通但风险是随机划分可能把同一个人的多条体检记录同时分到训练集和测试集造成数据泄漏。如果数据集里有patient_id或id字段必须先按个体分群再划分。更稳妥的做法是直接上 StratifiedKFold保持每一次划分里正负样本比例跟全量一致。疾病预测场景里糖尿病和高血压的患病率通常不是 50%如果直接随机划分很可能某一折里正样本只有十几个模型训练时决策边界偏移验证集表现忽高忽低。from sklearn.model_selection import StratifiedKFold X df.drop(columns[label]) y df[label] folds StratifiedKFold(n_splits5, shuffleTrue, random_state42) for fold_idx, (trn_idx, val_idx) in enumerate(folds.split(X, y)): X_trn, X_val X.iloc[trn_idx], X.iloc[val_idx] y_trn, y_val y.iloc[trn_idx], y.iloc[val_idx] print(ffold {fold_idx}: train {len(trn_idx)}, val {len(val_idx)}, fpos_rate {y_val.mean():.3f})这段代码的核心在StratifiedKFold的shuffleTrue和random_state42shuffle 让样本顺序不再影响折的划分固定随机种子保证每次复现结果一致。如果你发现某个折的正样本比例波动超过 2%回头检查是不是没有分层或者label本身是连续值那要先做阈值二值化。答辩时最常被问的就是“你这个验证集是怎么来的”直接答五折分层交叉验证比“我随便切的”有说服力得多。3.2 先给一组确定性强的 baseline 参数LightGBM 新手最常见的误区是一上来就调n_estimators5000、learning_rate0.01结果训练几个小时后发现过拟合。我一般会让初版参数保守一点跑通后再往深处调。import lightgbm as lgb from sklearn.metrics import roc_auc_score lgb_params { objective: binary, metric: auc, learning_rate: 0.05, num_leaves: 31, max_depth: -1, min_child_samples: 20, feature_fraction: 0.8, bagging_fraction: 0.8, bagging_freq: 1, lambda_l1: 0.0, lambda_l2: 1.0, verbose: -1, seed: 42, }num_leaves31是默认值代表单棵树的叶子数上限对应模型复杂度min_child_samples20强制每个叶子至少 20 个样本是防止过拟合的第一道闸门feature_fraction0.8让每棵树只用 80% 的特征增加树的多样性。bagging_fraction0.8配合bagging_freq1做行采样两者组合起来相当于给模型加随机性泛化能力通常比纯列采样更好。verbose-1是关掉训练日志不关的话终端会被刷屏影响你观察早停信息。3.3 早停拿着验证集当“体温计”毕设数据量不大时训练轮数不是越多越好。每轮迭代完用验证集算一次 AUC连续多轮不涨就停这是 LightGBM 里最实用的技能也是答辩时可以展开讲的细节。dtrn lgb.Dataset(X_trn, labely_trn) dval lgb.Dataset(X_val, labely_val, referencedtrn) model lgb.train( lgb_params, dtrn, num_boost_round1000, valid_sets[dval], valid_names[val], callbacks[lgb.early_stopping(stopping_rounds50), lgb.log_evaluation(50)] ) val_pred model.predict(X_val, num_iterationmodel.best_iteration) print(val auc:, roc_auc_score(y_val, val_pred)) print(best iteration:, model.best_iteration)early_stopping(stopping_rounds50)表示连续 50 轮验证集 AUC 没提升就停止训练num_iterationmodel.best_iteration预测时必须指定最佳迭代次数否则 LightGBM 会默认用最后一轮效果反而变差。这里有个容易踩的坑如果log_evaluation没设置终端会疯狂刷 1000 多行日志设置成 50 代表每 50 轮打印一次干净很多。还有一点验证集必须通过referencedtrn关联到训练集否则模型不知道特征对齐关系后续做预测时容易出现列顺序错乱。3.4 参数再调不是靠玄学是靠固定变量调参只要守住一个原则一次只动一个参数。把 grid search 或者 Optuna 留到你确实需要刷分的时候毕设场景下手工调三组就够了。常见的顺序是第一轮固定其他参数调num_leaves候选值[15, 31, 63]第二轮固定最优num_leaves调min_child_samples候选值[5, 20, 50]第三轮把learning_rate从 0.05 降到 0.03同时把num_boost_round上限提高让早停决定最终轮数。我踩过的教训是num_leaves63配合小数据集时验证集 AUC 在训练前期上涨很快但到第 300 轮左右就开始过拟合训练集 AUC 一路冲到 1.0验证集却原地不动。所以调num_leaves时一定要同步观察best_iteration有没有变早。如果变早说明模型变复杂了需要同时加大min_child_samples压制复杂度。这个联动逻辑写进论文里比贴一张网格搜索的表格更有技术含量。4. 打包 .exe 与运行避坑从 .py 到可双击程序的完整链路4.1 为什么毕设一定要打包 .exe很多学生把模型训练完就交差但导师演示的时候不可能在你机器上配环境。打包成疾病风险预测程序.exe后演示环节就是双击、输入信息、看结果不需要现场装 Python也不需要解释“我这环境有问题”。这份资源里已经附带打包好的 exe但你自己改过模型或界面后还是要学会重新打包。打包工具有 PyInstaller 和 Nuitka 两类PyInstaller 更成熟踩坑经验在网上也更多。毕设场景我推荐 PyInstaller因为 LightGBM 的模型文件是 pkl 或 txt 格式PyInstaller 打包时容易漏掉动态库需要手动补。pip install pyinstaller pyinstaller --noconfirm --onefile --windowed \ --name 疾病风险预测程序 \ --add-data hypertension.pkl;. \ --add-data diabetes.pkl;. \ --add-data lightgbm_model.txt;. \ app.py这个命令里的--onefile是把所有文件塞进单个 exe代价是启动速度慢一点--windowed是去掉命令行黑窗口GUI 程序必须加。--add-data在 Windows 上用分号分隔源文件和目标目录Linux/macOS 上要用冒号这个细节能卡住一半的人。如果你用的是新版 PyInstaller--add-data前面不用写直接空格分隔即可。4.2 打包后双击没反应先看这三个位置.exe打包成功不代表直接能跑。我拆这个项目时第一次双击后什么反应都没有任务管理器里能看到进程一闪而过典型的运行时崩溃。排查顺序固定是先在终端里跑dist/疾病风险预测程序.exe把报错信息打出来然后逐个检查模型文件路径、动态库依赖、pkl 版本三个位置。cd dist .\疾病风险预测程序.exe如果在命令行里能看到ModuleNotFoundError: No module named pandas通常是 PyInstaller 的依赖分析和你的虚拟环境不一致如果在代码里的模型加载位置报FileNotFoundError问题基本出在sys._MEIPASS路径处理上。PyInstaller 打包后--add-data的文件不在 exe 旁边而是被解压到临时目录里必须用下面这套逻辑取路径。import sys import os def resource_path(relative_path): base_path getattr(sys, _MEIPASS, os.path.abspath(.)) return os.path.join(base_path, relative_path) model lgb.Booster(model_fileresource_path(lightgbm_model.txt)) df pd.read_pickle(resource_path(hypertension.pkl))这段代码是 PyInstaller 打包场景的标准解法sys._MEIPASS在打包后指向临时解压目录在源码运行时不存在所以用getattr(sys, _MEIPASS, os.path.abspath(.))兜底开发时读当前目录打包后读临时目录。论文里把这个函数贴出来再配一句“兼容开发环境和打包环境”导师会觉得你确实跑过完整链路。提示如果 exe 用--onefile打包后在别人电脑上杀毒软件报毒不要慌多半是 PyInstaller 的加壳行为触发误报。换个思路用--onedir模式打包成文件夹误报率会低很多演示时把整个文件夹拷过去即可。4.3 界面交互里的三个真实翻车点项目里有Dark screenshot.png和Light screenshot.png两张截图说明它支持深色和浅色主题切换。这种纯前端逻辑的坑不多真正的坑都在“用户输入到模型预测”这一段。第一个坑用户输入的字段类型和模型训练时不一致。比如训练时age是数值型但界面拿到的输入是字符串直接 predictions 前必须强转。第二个坑模型训练时的特征列顺序和界面组装的特征顺序不一致。LightGBM 的 Booster 对列顺序敏感训练时列顺序是[age, bmi, blood_glucose, ...]界面组装时万一少传一列预测结果会完全乱掉。第三个坑pkl 里的模型可能是 sklearn 的LGBMClassifier也可能是原生lgb.Booster两种对象的 predict 接口不一样。import pandas as pd feature_cols [age, bmi, blood_glucose, hypertension_history, smoke] def assemble_input(raw_form): row [] for col in feature_cols: val raw_form.get(col, 0) if col in [age, bmi, blood_glucose]: val float(val) else: val int(val) row.append(val) return pd.DataFrame([row], columnsfeature_cols) input_df assemble_input(form_data) pred_proba model.predict(input_df)[0]这段代码的feature_cols是一个显式白名单作用是强制保证预测时的特征顺序和训练时一致。float和int的转换必须写在进模型之前不要等着模型内部去自动识别否则传入字符串时 LightGBM 会直接抛异常或者按缺失值处理。答辩现场最容易露怯的场景就是老师随手输入一个年龄界面崩了。提前把类型转换放在assemble_input里能救回来。4.4 打包避坑清单照着逐条排除这份资源的 exe 是已经打包好的但如果你改了模型想重新打包下面三条是最真实的踩坑记录。现象一打包时提示ModuleNotFoundError: No module named lightgbm.some_inner_module。原因PyInstaller 对 LightGBM 的动态库扫描不完全常见于 Windows 上安装了多个 LightGBM 版本。解决在app.py顶部显式import lightgbm并把完整包路径手动加进--hidden-import必要时复制 lightgbm 目录下所有 DLL 到项目根目录。现象二exe 打开后点预测按钮程序卡死但 CPU 没跑满。原因不是模型推理慢是 exe 里加载的 pandas 在初始化时耗时--onefile模式还要先解压临时目录首次启动慢很正常。解决不要判断程序死了等 5 到 10 秒如果实在要提速--onedir比--onefile启动快一半以上。现象三exe 在自己电脑上正常换到教室电脑上缺 DLL。原因--onefile没带上 VC 运行库。解决打包机的 Python 尽量用官方安装包不要用精简版到目标机器上后如果报缺VCRUNTIME140.dll直接装一次 VC 运行库一劳永逸。这个坑跟代码无关但答辩现场最容易因为这个问题翻车提前在虚拟机里测一遍最保险。5. 模型验证与进阶除了 AUC 还要看什么5.1 混淆矩阵能看出阈值选错了AUC 高不代表实际可用。糖尿病和高血压预测是典型的“宁可误报也不漏报”场景模型默认的 0.5 阈值往往不是最优选择。我训练完模型后一定会在验证集上画混淆矩阵找到敏感度和特异度的平衡点。from sklearn.metrics import confusion_matrix, classification_report opt_threshold 0.4 val_binary (val_pred opt_threshold).astype(int) print(classification_report(y_val, val_binary, target_names[negative, positive])) cm confusion_matrix(y_val, val_binary) print(cm)classification_report会一次性给出精确率、召回率、F1 值比单独看 AUC 直观得多。这里的opt_threshold0.4是举例真实场景应该用验证集画出 PR 曲线或 ROC 曲线后在曲线上选点。一个简单的做法是遍历 0.3 到 0.7 间隔 0.05 找 F1 最高的阈值。答辩时如果被问到“为什么阈值是 0.4 不是 0.5”回答“我对验证集做了阈值扫描选择了在漏报率与误报率之间平衡的点”比“我随便设的”好太多。5.2 单样本预测的调试技巧断点打在哪很重要模型整体指标好看不代表单个样本的预测结果可信。我调试这类疾病风险预测程序时有个习惯从界面里拿一条真实输入单独写一段脚本打印每一层的中间结果。sample pd.DataFrame([ {age: 58, bmi: 27.5, blood_glucose: 6.8, hypertension_history: 1, smoke: 1} ]) sample sample[feature_cols] raw_prob model.predict(sample)[0] print(raw probability:, raw_prob) if hasattr(model, predict_proba): proba model.predict_proba(sample)[0] print(proba:, proba)这段代码里hasattr(model, predict_proba)是判断模型类型的技巧原生lgb.Booster没有predict_proba只有 predict返回的是正样本概率sklearn 的LGBMClassifier有predict_proba返回的是[负样本概率, 正样本概率]。两者下标含义不一样混淆之后界面显示的风险值会反着来。这个坑我见过不止一次把类型判断写进代码后面打包成 exe 也不会因为模型对象不同而显示错乱。5.3 SHAP 才是把模型“讲明白”的终极大招毕设答辩走到高阶阶段老师大概率会问一句“你凭什么相信这个模型”光靠 AUC 不够SHAP 值可以把每个特征对预测结果的贡献可视化出来。如果项目里没有现成的 SHAP 分析模块你可以自己补因为它不依赖模型结构只依赖你的训练数据。import shap explainer shap.TreeExplainer(model) shap_values explainer.shap_values(X_val) shap.summary_plot(shap_values, X_val, feature_namesfeature_cols)TreeExplainer专门针对树模型LightGBM 直接用性能很好。shap.summary_plot会画一张散点图横轴是 SHAP 值纵轴是特征颜色代表特征值高低。这张图放进论文里价值极高它能直观证明“血糖越高预测风险越高”而不是你自己嘴上说说。SHAP 安装偶尔会跟 numpy 版本冲突常见报错是AttributeError: NoneType object has no attribute split原因是 shap 版本偏老升级到 0.44 以上基本能解决。这个坑很小但足以让人在答辩前夜崩溃提前装好省心。5.4 我的一个验证习惯供你参考从那以后我每次跑完一组实验都会做三件事先把验证集预测结果导出成 CSV附上原始标签和概率值然后把特征重要性表和最关键的两个 SHAP 图存成图片最后把训练参数、数据集行数、AUC、最佳迭代轮数写成一份 Markdown 存档。下次不管是谁来问“这模型到底靠谱吗”我直接把这份档案翻出来对着数据说话而不是凭印象说“还行”。这个习惯也是从拆这份资源时学到的它自带一份《基于LightGBM的糖尿病高血压预测程序.md》把项目前因后果都写清楚了你复现完之后完全可以模仿它把自己这版的数据也补一份文档。希望帮到你。本文还有配套的精品资源点击获取