简介基于机器学习的糖尿病预测系统是一份面向计算机相关专业学生的课程设计高分项目涵盖计科、数据科学与人工智能等方向尤其适合正在做课设、大作业的学生以及需要实战演练的入门开发者。项目代码完整、功能已验证采用Maven工程结构包含Java与Scala源码、JSP交互页面、XML配置、属性文件及说明文档等覆盖数据读取、特征处理、模型训练到前端展示的完整链路便于读者系统理解机器学习预测系统的工程实现。资源共46个文件压缩包仅55KB轻量易部署解压重命名为英文路径后即可稳定运行内附说明.txt与IDE工程配置方便快速定位代码结构。目前已有360人学习具有较高的学习借鉴价值基础较好的读者可基于该项目二次开发扩展不同预测功能或更换数据集也可作为毕业设计与初期立项演示的参考模板。1. 基于机器学习的糖尿病预测系统课程设计的热门选题为什么翻车率不低基于机器学习的糖尿病预测系统是机器学习课程设计里最经典也最容易被低估的选题Pima数据集公开、特征只有8个、模型怎么也能跑出70%以上的准确率看起来两三天就能交差。可真动手你会发现翻车点全藏在细节里——0值当不当缺失处理、标准化在切分前还是切分后、准确率挺高但糖尿病漏诊率也高得吓人任何一个都够你折腾一晚上。这篇写给正在做机器学习课程设计、或者想找个实战项目练手的读者先花最小代价把数据、特征、模型跑通再把评估指标和系统界面做扎实最后落到答辩现场能讲清楚。我按做这类项目时最有用的顺序来数据处理、模型调参、系统封装、常见问题最后说怎么拿高分。2. 糖尿病预测的数据与特征工程8个特征、0值陷阱与切分顺序2.1 数据集与特征Pima糖尿病数据集为什么是课程设计标配在做基于机器学习的糖尿病预测系统之前先认识一下手头的数据。课程设计里最常见的原料是Pima Indians Diabetes Dataset768条样本、二分类标签Outcome1表示确诊糖尿病特征共8个。这个数据集之所以成为标配是因为它大小适中一行代码就能载入训练时间以秒计足够你把“数据处理是什么”这个问题完整走一遍不会被性能瓶颈打断思路。特征名含义取值特点Pregnancies怀孕次数整数0到17Glucose2小时口服葡萄糖耐量测试血糖正常应大于0常见0值缺失BloodPressure舒张压(mmHg)正常应大于0常见0值缺失SkinThickness皮褶厚度(mm)正常应大于0常见0值缺失Insulin2小时血清胰岛素(μU/mL)正常应大于0缺失极多BMI体重指数(kg/m²)正常应在12到600为缺失DiabetesPedigreeFunction糖尿病家族遗传函数0到2.5越小风险越低Age年龄21到81这个表在答辩PPT里放一页老师马上知道你认真看过数据。需要特别注意的是Glucose、BloodPressure、SkinThickness、Insulin、BMI这五列它们的0值在医学上不可能是真实测量值——一个成年人的BMI不可能是0舒张压也不可能是0。这些0是缺失标记是数据处理环节最先要处理的对象不是数值。2.2 0值处理与中位数填充先看describe()再动手拿到数据的第一步我一般先跑df.describe()看每一列的最小值。如果发现特征最小值是0而医学上0不合理先标记为缺失再做填充。填充策略上常见做法是用中位数不用均值Pima数据集里有极端值比如Insulin的分布尾巴很长均值会被拉偏中位数更稳。import pandas as pd import numpy as np df pd.read_csv(data/diabetes.csv) # 这些字段的0值在医学上不合理视为缺失值 missing_cols [Glucose, BloodPressure, SkinThickness, Insulin, BMI] for col in missing_cols: df[col] df[col].replace(0, np.nan) # 用中位数填充而不是均值避免被极端值拉偏 df df.fillna(df.median()) # 检查填充后的分布 print(df.describe())逻辑说明replace(0, np.nan)先把不合理0变成缺失fillna(df.median())用每一列的中位数回填。参数上Insulin列的缺失比例接近一半用中位数填充是最保守的做法如果你有余力可以按“是否糖尿病”分组分别计算中位数来填也就是组内填充效果会更好一点代价是代码多几行。填完之后再用df.describe()确认最小值不再是0这一步做好了后面模型才不至于学到“BMI0的人风险更低”这种假规律。2.3 切分与标准化顺序scaler只能fit训练集数据处理的下一个关键决定是先切分再标准化。很多翻车项目都是先把整个数据集StandardScaler一遍再train_test_split这其实已经造成数据泄露——测试集的均值和方差参与了训练数据的变换等于测试集信息提前进了模型。正确顺序是先把X和y切成四份然后在训练集上fit_transform在测试集上只transform。from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler X df.drop(Outcome, axis1) y df[Outcome] # 先切分再标准化stratify保证训练/测试里正负样本比例一致 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, stratifyy, random_state42 ) scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test) # 注意这里不能fit注意先切分再标准化scaler只能fit训练集。这个顺序错了再好的模型也白搭。逻辑说明test_size0.2是课程设计最常用的比例768条样本切下来训练集614条、测试集154条足够稳定stratifyy让正负样本在两个集合里的比例和原始数据一致对类别不平衡的数据尤其重要random_state42固定随机种子保证你每次跑结果一致答辩现场重跑也能复现。scaler.fit_transform(X_train)只学习训练集的均值和方差X_test_scaled用同一套参数去变换这才算干净。这一段是“机器学习中的数据处理是什么”最直接的答案数据处理的本质就是让数据进入模型之前先解决缺失、量纲、泄漏这三件事。很多课程设计分数拉不开问题不在模型而在这三步有没有做对。3. 模型训练与参数调优逻辑回归、随机森林与XGBoost的取舍3.1 模型选型课程设计视角下的三选一在完整的机器学习应用流程里训练模型只是中间一步前面的数据清洗和后面的系统封装同样决定成败。到了选模型这一步课程设计的评分逻辑一般是你讲不讲得清原理、指标全不全、系统能不能跑。所以我的常见做法是主力模型控制在两个以内第三个作为加分项。模型训练速度可解释性调参成本课程设计适配度逻辑回归秒级强系数直接讲低必做基线随机森林秒级中特征重要性可用中主力推荐XGBoost秒级中弱高加分项慎当主力逻辑回归作为基线原因是系数可以直接解读某个特征每变化一个单位对数几率变化多少答辩时一句话讲明白。随机森林作为主力是因为它对非线性关系更友好而且自带feature_importances_能回答“模型凭什么这么判”。XGBoost精度上限高但768条样本下提升有限调参却要花掉大量时间性价比在课程设计里并不高。一句话不要同时堆十个模型算法选择从来不是越多越好评分老师只关心你“为什么选它”。3.2 训练与评估准确率会骗人看AUC和召回率训练代码我一般固定成型跑完打两份报告classification_report和roc_auc_score。以下代码覆盖基线和主力两个模型from sklearn.linear_model import LogisticRegression from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import classification_report, roc_auc_score, confusion_matrix # 基线逻辑回归max_iter调大避免不收敛class_weight处理类别不平衡 lr LogisticRegression(max_iter1000, class_weightbalanced, random_state42) lr.fit(X_train_scaled, y_train) # 主力随机森林限制深度防止过拟合leaf最小样本数保证泛化 rf RandomForestClassifier( n_estimators200, max_depth5, min_samples_leaf3, class_weightbalanced, random_state42 ) rf.fit(X_train_scaled, y_train) for name, model in [(LogisticRegression, lr), (RandomForest, rf)]: y_pred model.predict(X_test_scaled) y_proba model.predict_proba(X_test_scaled)[:, 1] print(f--- {name} ---) print(classification_report(y_test, y_pred)) print(fAUC: {roc_auc_score(y_test, y_proba):.4f}) print(confusion_matrix(y_test, y_pred))参数说明max_iter1000是逻辑回归的收敛上限Pima数据量小但特征经过标准化后有负数迭代次数默认100不够稳定class_weightbalanced让少数类样本在损失里获得更高权重缓解类别不平衡n_estimators200表示200棵树对这个数据量已经够用再大收益递减max_depth5防止树深了过拟合Pima特征只有8个深度5足以表达交互关系min_samples_leaf3要求叶子节点至少3个样本进一步平滑预测。这几个参数是课程设计常见做法不是最优搜索的结果如果你时间够可以用GridSearchCV在周边小范围再搜一遍。评估指标上准确率在糖尿病预测里会骗人当多数类比例高时模型只需要无脑判多数类就能拿到高准确率。课程设计报告里必须出现的是classification_report里的recall敏感度和roc_auc_score。在医学场景漏诊有糖尿病但没预测出来比误诊代价高得多所以recall是核心指标AUC则衡量模型排序能力不受阈值影响两个模型对比时AUC比accuracy可靠。3.3 特征重要性把模型从黑匣子变成可解释随机森林训练完之后顺手打印特征重要性。这个动作时间成本几乎为零但答辩价值极高import pandas as pd importance pd.Series(rf.feature_importances_, indexX.columns) print(importance.sort_values(ascendingFalse))逻辑说明feature_importances_是随机森林对每个特征在所有树中减少不纯度的平均贡献输出后你通常会发现Glucose、BMI、Age稳居前几名。答辩时可以讲“我的模型显示血糖和BMI贡献最大这和临床认知一致说明模型学到的是合理规律。”这句话比你解释十行代码都有说服力。反过来如果某个医学上明确重要的特征排名垫底说明前面的数据预处理有问题回去查0值处理和填充方式——特征重要性也是帮你自查数据的工具。机器学习模型不是纯黑匣子这是处理过的数据加上可解释工具之后的结果。4. 把模型封装成可运行的预测系统命令行脚本、Flask接口与源代码组织4.1 源代码组织一个课程设计项目最少需要哪些文件很多同学训练完模型就算完工但课程设计要的是“系统”。最少可运行的项目结构我一般这样组织diabetes-predict/ ├── data/ │ └── diabetes.csv ├── train.py # 训练脚本输出模型文件 ├── predict.py # 命令行预测脚本 ├── app.py # Flask Web 接口 ├── requirements.txt # 依赖清单 └── README.md # 运行说明train.py里跑完第3章的完整流程最后把模型和scaler保存下来predict.py负责加载模型从命令行拿8个特征值输出预测结果和概率app.py是Web接口给前端或演示页面调用。requirements.txt固定依赖库名称README写三步装依赖、训练、启动预测。源代码管理这件事在课程设计里就是“老师拿到手能不能在两分钟内跑起来”。结构里少一个README可能直接导致老师放弃运行你的代码。4.2 模型保存与加载joblib比pickle更省心模型训练完必须落盘下次直接用。常见做法是用joblib它对numpy数组的序列化比pickle更高效随机森林这种含大量数组结构的模型用joblib最合适import joblib # 保存模型和scaler必须一起保存预测时特征要先走同样变换 joblib.dump(rf, diabetes_model.joblib) joblib.dump(scaler, diabetes_scaler.joblib) # 加载 rf_loaded joblib.load(diabetes_model.joblib) scaler_loaded joblib.load(diabetes_scaler.joblib)逻辑说明为什么scaler也要保存因为训练时特征做了标准化预测时输入的新样本也必须用同一套均值和方差变换否则特征分布不一致预测结果不可信。很多项目只保存模型不保存scaler换环境预测时直接拿原始特征往里灌输出自然不对。这个细节课程设计里极度常见属于一踩一个准的经典坑。4.3 命令行预测脚本predict.py命令行版本是最小的可用系统适合答辩现场演示。写一个predict.pyimport sys import joblib import numpy as np model joblib.load(diabetes_model.joblib) scaler joblib.load(diabetes_scaler.joblib) # 命令行参数按特征顺序传入例如 # python predict.py 6 148 72 35 0 33.6 0.627 50 raw np.array([float(x) for x in sys.argv[1:9]]).reshape(1, -1) scaled scaler.transform(raw) prob model.predict_proba(scaled)[0][1] pred int(prob 0.5) print(f预测结果{患糖尿病 if pred else 未患糖尿病}) print(f患病风险概率{prob:.2%})逻辑说明sys.argv[1:9]取8个输入特征顺序必须和训练时的特征列一致否则数据就错位了。输出里我故意保留prob而不是只输出0/1因为风险概率本身就有临床意义概率0.49和0.9虽然都会被阈值判成两类但对医生和用户来说含义完全不同。课程设计里能输出概率说明你理解了模型输出层到底在给什么。4.4 Flask接口与Web界面把模型变成能交互的系统命令行能跑之后Flask接口就顺理成章。一个最小可用的预测接口代码量不超过30行from flask import Flask, request, jsonify import joblib import numpy as np app Flask(__name__) model joblib.load(diabetes_model.joblib) scaler joblib.load(diabetes_scaler.joblib) FEATURES [Pregnancies, Glucose, BloodPressure, SkinThickness, Insulin, BMI, DiabetesPedigreeFunction, Age] app.route(/predict, methods[POST]) def predict(): data request.get_json() # 按特征顺序组装输入字段缺失时兜底为0避免接口崩溃 values [float(data.get(f, 0)) for f in FEATURES] scaled scaler.transform(np.array(values).reshape(1, -1)) prob float(model.predict_proba(scaled)[0][1]) return jsonify({probability: round(prob, 4), prediction: int(prob 0.5)}) if __name__ __main__: app.run(host0.0.0.0, port5000)逻辑说明data.get(f, 0)是请求体里缺字段时的兜底演示时前端少传一个字段接口不会直接500这个细节在抽查时好感度很高。host0.0.0.0允许局域网访问答辩时如果前后端分两台设备也能联调。启动后可以用一行curl做冒烟测试curl -X POST http://127.0.0.1:5000/predict \ -H Content-Type: application/json \ -d {Glucose:148,BMI:27.3,Age:50}5. 糖尿病预测系统常见问题与排查五个一踩就废的坑5.1 数据泄露训练集准确率99%换数据就翻车现象训练集准确率跑到99.8%测试集结果也不差答辩时老师拿一条新数据来预测结果明显不合理。原因最常见的是在train_test_split之前就对全量数据做了标准化或者用全量数据做过特征选择和缺失值统计。scaler在全体数据上fit过测试集的分布信息已经被训练过程看到这叫数据泄露。它让指标虚高但模型实际没见过干净的测试分布一换数据就现原形。解决严格按第2章的流程先切分再标准化scaler只fit训练集测试集只transform。如果你用了特征选择、PCA这类步骤把它们包进sklearn.pipeline.Pipeline里和切分放在同一个流程中执行不要手写顺序。5.2 类别不平衡准确率74%但漏诊率过半现象accuracy约74%看起来能交差但打印classification_report发现Outcome1的recall只有50%出头也就是说一半真患者被漏掉了。原因Pima数据里正负样本比例约1.8:1对机器学习来讲不算严重但模型天然偏向多数类如果直接用原始类分布训练阈值默认在0.5少数类的召回率就会偏低。课程设计里只拿accuracy当唯一指标这个问题根本不会被发现。解决训练时加class_weightbalanced让少数类样本权重更高报告里同时给出AUC、recall和混淆矩阵不要只贴accuracy。如果数据不平衡更严重比如1:10再考虑SMOTE过采样但Pima这个量级class_weight加AUC报告已经足够。5.3 0值陷阱BMI0居然进了模型现象直接打开CSV就训练逻辑回归的系数里BMI的权重是负数——BMI越高越安全这在医学上完全说反了。原因Pima数据集的Glucose、BloodPressure、SkinThickness、Insulin、BMI五列0是缺失标记不是真实值。成年人的BMI不可能为0模型把“缺失”当“真实的0”学自然会学到假规律。解决建模前先df.describe()看最小值把不合理0先替换成NaN再用中位数填充。注意别用均值Insulin列有长尾极端值均值填充会把整体分布拉偏。填完再打印一次describe()确认最小值恢复正常。这一步是整个数据处理里最容易被忽略但影响最大的一环。5.4 模型文件打不开版本不一致的玄学现象自己电脑上joblib.load一切正常换到老师电脑或答辩机器上报ModuleNotFoundError或者EOFError模型文件完全废掉。原因joblib和pickle序列化保存的是“类的完整路径”比如sklearn.ensemble._forest.RandomForestClassifier。scikit-learn版本不一致、Python版本不一致、甚至numpy版本不一致反序列化都会失败。这不算代码bug但答辩现场就是翻车现场。解决项目里必须带requirements.txt写明依赖库名称和版本范围答辩前在另一台干净机器上完整跑一遍README步骤。由于这个数据集训练只要几秒最保险的做法是答辩机现场跑train.py重新训练模型文件根本没有机会打不开。5.5 黑匣子输出只给“是/否”答辩被问住现象页面只显示“患糖尿病/未患糖尿病”老师追问“你的判断依据是什么”回答不上来。原因预测系统的输出只暴露了二分类结果把概率、特征取值、特征重要性全部丢在后台。模型本身有predict_proba这个接口但很多同学觉得输出0/1就够了结果把关键的可解释信息全丢了。解决界面上同时展示患病风险概率概率本身就是最有价值的输出更进一步把当前样本的Glucose、BMI等特征值连同特征重要性排名一起展示比如“主要风险因素血糖148、BMI 27.3、年龄50”。答辩时讲这句比解释任何代码都有效。我习惯把所有这类项目的输出都改成“概率主要风险因素”短期多写几行代码长期答辩体验完全不同。6. 让课程设计从“能用”变“高分”评估可视化、报告组织与答辩准备6.1 报告里放这几张图比贴十页代码管用做基于机器学习的糖尿病预测系统课程设计最后得分往往不在代码量而在呈现。我的习惯是固定输出三张图ROC曲线、混淆矩阵热力图、特征重要性条形图。ROC曲线能直观展示AUC和阈值取舍import matplotlib.pyplot as plt from sklearn.metrics import roc_curve, auc fpr, tpr, _ roc_curve(y_test, rf.predict_proba(X_test_scaled)[:, 1]) plt.plot(fpr, tpr, labelfAUC {auc(fpr, tpr):.3f}) plt.plot([0, 1], [0, 1], k--) plt.xlabel(False Positive Rate) plt.ylabel(True Positive Rate) plt.legend() plt.savefig(roc_curve.png, dpi150)混淆矩阵热力图用seaborn的heatmap一页能说明灵敏度、特异度特征重要性图把前面的结果可视化。这三张图放进报告或PPT“实验结果”一节评分老师立刻知道你完整走完了评估流程。6.2 交作业前在干净目录里跑一遍全流程我做过最亏的事是交了.py文件没交运行说明老师打不开直接不给分。现在我的习惯是交付前重新建一个空目录把项目文件复制进去按README从头执行——pip install -r requirements.txtpython train.pypython app.py。跑不通就改直到干净环境一次通过。README里写清楚数据从哪来、每个脚本干什么、启动后访问哪个地址。这个操作成本十分钟但对课程设计来说价值远大于模型再调一轮参。答辩时先讲数据处理的两个坑再讲模型指标最后演示系统顺序对了一个入门级项目也能讲出实战感。我的经验是把系统演示留到回答完两个追问之后再收尾效果往往最好。希望帮到你。本文还有配套的精品资源点击获取