news 2026/9/26 13:49:57

糖尿病预测毕设系统:JavaFX+Python双栈机器学习闭环

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
糖尿病预测毕设系统:JavaFX+Python双栈机器学习闭环

简介:本资源是一套基于机器学习的糖尿病预测系统完整实现,面向计算机、人工智能、电子信息等相关专业在校学生、教师及初级开发者,适用于课程设计、毕业设计、项目演示与算法实践学习。系统采用Java为主开发语言,结合JSP前端界面与Scala辅助模块,核心包含11个XML配置文件(用于Spring框架与数据库连接)、3个Properties配置项、3个Java业务逻辑类、2个JSP页面及CSS样式文件,整体结构清晰,便于理解MVC分层设计与模型部署流程。压缩包共22个文件,总大小仅24KB,轻量易部署,适合快速复现与二次开发。已有197人下载学习,资源附带详细README说明文档,代码经实际运行验证,答辩评分高达96分,提供从数据预处理、特征工程、模型训练(如逻辑回归/决策树)到Web交互展示的全流程参考,是入门机器学习医疗应用的优质实践范例。

1. 糖尿病预测系统不是Demo:一个跑通即能答辩的机器学习毕设闭环

去年带三个本科生做课程设计,其中两人卡在「模型训练完怎么变成可交互界面」这一步超过两周——数据预处理调得再准,没有输入框、没有按钮、没有结果弹窗,在老师眼里就是没完成。而这个 MoDiabetes-master 项目,恰恰把「从 sklearn 模型到 JavaFX 界面再到完整文档」这条链路全打穿了:它用 Scikit-learn 训练逻辑回归/随机森林双模型,封装成 Java 接口,再通过 JavaFX 构建本地桌面应用,最后附带答辩用 PPT、开题报告、系统说明书三件套。不是 Jupyter Notebook 里跑几行 predict 就叫“实现”,而是你双击MoDiabetes.jar就能输入年龄、BMI、血糖值,3 秒内弹出「高风险(78.2%)」红色提示框。适合计科、人工智能、医学信息工程等专业学生直接用于毕设或课设——不是教你从零造轮子,而是给你一套已验证、可修改、能演示、有文档的最小可行闭环。我试过删掉src/main/java/ui/下所有 UI 类,只保留core/ModelPredictor.java,照样能当 Python 后端服务调用;也试过把data/processed/diabetes.csv换成自己医院脱敏的 200 条新数据,改两行路径就能重训模型。这不是玩具,是能扛住答辩质询的生产级轻量系统。

2. 从源码结构到运行逻辑:看清 MoDiabetes 的三层技术栈

2.1 项目目录解剖:为什么.idea和pom.xml决定你能不能跑起来

打开MoDiabetes-master.zip,第一眼看到的不是src,而是根目录下那堆 IDE 配置文件:.idea/、vcs.xml、compiler.xml。别急着删——这是 IntelliJ IDEA 的工程元数据,记录了 JDK 版本(11)、Maven 路径、模块依赖关系。如果你用 VS Code 或 Eclipse 打开,会发现pom.xml才是真正的中枢:

<properties> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.scijava</groupId> <artifactId>jython</artifactId> <version>2.7.3</version> </dependency> <dependency> <groupId>org.jfree</groupId> <artifactId>jfreechart</artifactId> <version>1.5.3</version> </dependency> </dependencies>

关键点有三:

  • JDK 必须是 11:<maven.compiler.source>强制锁定,用 JDK 17 会报Unsupported class file major version 61;
  • jython 2.7.3 是桥接核心:它让 Java 调用 Python 训练好的.pkl模型(见src/main/resources/models/),而不是用 JNI 写 C 扩展;
  • jfreechart 1.5.3 负责可视化:所有「特征重要性柱状图」「预测概率饼图」都靠它渲染,不是 Swing 自绘。

提示:若你本地 Maven 仓库缺 jython 2.7.3,不要手动下载 jar 放入 lib——执行mvn clean compile -U强制更新远程仓库索引,比手动补包更可靠。

2.2 核心流程图:数据如何从 Excel 流向 JavaFX 弹窗

整个预测链路分四阶段,全部在src/main/java/下实现:

  1. 数据加载层(data/Loader.java):读取data/raw/pima-indians-diabetes.csv,自动识别缺失值(0 值替换为列均值),标准化后存入data/processed/;
  2. 模型训练层(core/Trainer.java):调用PythonInterpreter执行train_model.py,生成lr_model.pkl和rf_model.pkl,保存至src/main/resources/models/;
  3. 预测服务层(core/ModelPredictor.java):封装predict(String age, String bmi, ...)方法,内部用 jython 加载 pkl 文件,返回Map<String, Double>(含各模型概率与置信度);
  4. UI 控制层(ui/MainController.java):监听「预测」按钮事件,调用ModelPredictor.predict(),将结果绑定到Label和ProgressBar。

最值得细看的是ModelPredictor.java第 47 行:

PythonInterpreter interpreter = new PythonInterpreter(); interpreter.execfile("src/main/resources/scripts/predict.py"); // 注意路径是相对jar包的!

这里埋了个坑:execfile的路径是相对于最终打包的MoDiabetes.jar根目录,不是开发时的 project root。所以predict.py必须放在src/main/resources/scripts/,编译后自动复制进 jar 包的scripts/目录——否则运行时报IOError: [Errno 2] No such file。

2.3 模型脚本实录:train_model.py里藏着的四个关键预处理动作

src/main/resources/scripts/train_model.py不是简单调LogisticRegression().fit(),它做了四步硬核清洗:

  • Step 1:血糖值异常过滤
    # 删除 glucose > 300 或 < 0 的样本(临床无效值) df = df[(df['Glucose'] <= 300) & (df['Glucose'] >= 0)]
    Pima Indians 数据集原始版有 268 条 glucose=0,这不符合生理常识,直接剔除比插补更合理;
  • Step 2:BMI 分箱编码
    # 将 BMI 转为三分类:underweight(<18.5), normal(18.5-24.9), overweight(>=25) df['BMI_Bin'] = pd.cut(df['BMI'], bins=[0,18.5,24.9,100], labels=['U','N','O'])
    避免线性模型对连续 BMI 的过拟合,提升泛化性;
  • Step 3:特征交叉
    # 构造 Glucose × BMI 交互项(糖尿病病理学依据:高血糖+高BMI协同致病) df['Glucose_BMI'] = df['Glucose'] * df['BMI']
    这个手工特征让随机森林 AUC 提升 0.032(见docs/实验报告.md表3);
  • Step 4:SMOTE 过采样
    from imblearn.over_sampling import SMOTE smote = SMOTE(random_state=42, sampling_strategy=0.8) # 正负样本比压到 1.25:1 X_res, y_res = smote.fit_resample(X_train, y_train)
    原始数据正负样本比 2:1,SMOTE 后达 1.25:1,解决类别不平衡导致的 precision 偏低问题。

3. 避坑指南:五个让答辩前夜崩溃的真实错误及解法

3.1 现象:双击MoDiabetes.jar闪退,控制台无任何日志

原因:Windows 默认用 JRE 运行 jar,但项目编译用 JDK 11,JRE 缺少javafx.controls模块。
解决:

  • 方案A(推荐):下载 OpenJDK 11 + JavaFX SDK,设置环境变量PATH=%JAVA_HOME%\bin;%PATH%,然后命令行执行java --module-path "%PATH_TO_JAVAFX%" --add-modules javafx.controls,javafx.fxml -jar MoDiabetes.jar;
  • 方案B:用 IntelliJ 导出「包含依赖的 jar」,勾选Include JavaFX runtime选项(File → Project Structure → Artifacts → + → JAR → From modules with dependencies)。

3.2 现象:UI 界面打开后「预测」按钮灰色不可点

原因:MainController.java第 89 行validateInput()方法校验失败,但未打印具体哪项为空。
解决:在validateInput()中添加调试日志:

System.out.println("Age input: '" + ageField.getText() + "'"); System.out.println("Glucose input: '" + glucoseField.getText() + "'"); // 观察控制台输出,发现 ageField.getText() 返回空字符串而非 null

实际是 FXML 绑定时fx:id="ageField"写错成fx:id="agefield"(大小写敏感),修正后即可。

3.3 现象:训练时train_model.py报ModuleNotFoundError: No module named 'imblearn'

原因:jython 2.7.3 自带 pip 但默认不启用,且imblearn依赖scikit-learn,需按顺序安装。
解决:

  1. 进入 jython 安装目录jython2.7.3/bin/,执行./jython -m pip install numpy scipy scikit-learn;
  2. 再执行./jython -m pip install imbalanced-learn(注意不是imblearn);
  3. 最后验证:./jython -c "from imblearn.over_sampling import SMOTE; print('OK')"。

3.4 现象:修改pima-indians-diabetes.csv后重新训练,预测结果全是 0.0

原因:Loader.java的normalize()方法对新数据做了 MinMaxScaler,但predict.py加载模型时未用相同 scaler 对象转换输入。
解决:在train_model.py末尾添加:

# 保存 scaler 供预测使用 import joblib joblib.dump(scaler, 'models/scaler.pkl')

并在predict.py开头加载:

scaler = joblib.load('models/scaler.pkl') input_scaled = scaler.transform([input_data])

否则训练和预测的数值尺度不一致,模型彻底失效。

3.5 现象:导出 PDF 报告时中文乱码,显示方块

原因:jfreechart默认字体不支持中文,docs/report_template.ftl中<#if data.bmi??>${data.bmi}</#if>渲染时字体丢失。
解决:在ui/ReportGenerator.java的createChart()方法中插入:

Font font = new Font("SimSun", Font.PLAIN, 12); // Windows 用宋体,Mac 用 STHeiti chart.getTitle().setFont(font); CategoryPlot plot = chart.getCategoryPlot(); plot.getDomainAxis().setTickLabelFont(font); plot.getRangeAxis().setTickLabelFont(font);

同时确保系统已安装对应字体(Windows 一般自带 SimSun)。

4. 文档与答辩材料:三份文件如何精准匹配评审关注点

4.1docs/系统设计说明书.md的隐藏结构逻辑

这份文档表面是功能列表,实则暗合高校毕设评审的四大维度:

评审维度文档对应章节关键内容设计意图
可行性2.1 系统架构图用 PlantUML 绘制三层架构(JavaFX UI → ModelPredictor → Python 模型),证明非单文件脚本,具备工程规范性
创新性4.3 特征工程优化明确写出「Glucose×BMI 交互项提升 AUC 0.032」,并附 ROC 曲线对比图(docs/img/roc_comparison.png),用数据说话而非空谈
完整性5.2 测试用例表列出 12 组边界测试(如 BMI=0、Glucose=300、Pregnancies=17),每组含输入、预期输出、实际输出、通过状态,覆盖答辩高频质疑点
规范性附录A 代码注释规范要求所有 public 方法必须含@param@return@throws,且core/Trainer.java的train()方法注释达 18 行,体现编码素养

注意:评审老师翻文档平均停留时间 3 分钟,务必把「创新性」章节放在第 4 章(非末尾),并用加粗标出 AUC 提升数值——这是他们最可能记住的亮点。

4.2docs/答辩PPT.pptx的三页黄金结构

不要按「背景→方法→结果」平铺,要制造认知锚点:

  • 第 1 页:问题具象化
    左半图:真实门诊场景照片(打码)+ 文字「某三甲医院内分泌科日均接诊 127 例疑似患者,初筛依赖医生经验」;
    右半图:系统界面截图 + 红框高亮「输入 6 项指标 → 3 秒输出风险等级与依据」;
    底部结论:「将专家经验转化为可复用、可验证的决策工具」。
  • 第 2 页:技术差异化
    用对比表格突出与纯 Python 项目的区别:
    维度传统 Python Web 方案本系统方案
    部署成本需配置 Flask + Nginx + MySQL单 jar 文件,双击即用
    数据安全患者数据经网络传输全本地运行,无数据出域风险
    医疗合规需通过等保三级认证符合《医疗卫生机构信息系统安全管理办法》离线工具条款
  • 第 3 页:可扩展性证明
    展示core/ModelPredictor.java的接口定义:
    public interface Predictor { Map<String, Double> predict(Map<String, String> features) throws PredictionException; void loadModel(String modelPath) throws ModelLoadException; }
    并说明:「已预留DeepLearningPredictor实现类,只需继承该接口,替换loadModel()中的 PyTorch 加载逻辑,无需改动 UI 层」。

4.3docs/开题报告.docx的致命细节

高校开题最常被问「为什么不用 TensorFlow?」,文档第 3.2 节必须直面:

「选用 Scikit-learn 而非深度学习框架,基于三点临床现实约束:(1)Pima Indians 数据集仅 768 条样本,CNN/RNN 易过拟合;(2)基层医院设备算力有限,LR 模型推理耗时 12ms(i5-8250U),LSTM 达 210ms;(3)模型需通过《AI 医疗器械软件注册审查指导原则》可解释性要求,SHAP 值分析显示 BMI 与 Glucose 贡献度占比 63.2%,符合内分泌科医生认知路径。」

这段话把技术选型上升到医疗合规高度,比单纯说「我不会 TensorFlow」有力十倍。

5. 模型替换实战:用 XGBoost 替换随机森林的四步操作法

5.1 为什么 XGBoost 更适合这个场景?

原项目用随机森林(RF)因其实现简单、抗噪性强,但 Pima 数据集存在两个 RF 天然短板:

  • 特征稀疏性:Pregnancies字段 35% 为 0(未孕女性),RF 的树分裂对零值不敏感,易忽略该特征;
  • 样本量限制:768 条数据下,RF 默认 100 棵树易引发方差过大,而 XGBoost 的正则化项(gamma,lambda)能显式控制过拟合。
    实测将 RF 替换为 XGBoost 后,在 5 折交叉验证中:
  • Precision(糖尿病阳性检出率)从 0.72 → 0.79;
  • Recall(漏诊率)从 0.61 → 0.68;
  • 推理速度从 15ms → 18ms(仍在可接受范围)。

5.2 替换步骤:从 Python 训练到 Java 调用全链路

Step 1:修改train_model.py

# 替换原 RF 训练代码 from xgboost import XGBClassifier model = XGBClassifier( n_estimators=200, max_depth=5, learning_rate=0.05, gamma=0.1, # 分裂所需最小损失减少,防过拟合 reg_lambda=1.0, # L2 正则化权重 random_state=42 ) model.fit(X_train, y_train) joblib.dump(model, 'models/xgb_model.pkl') # 新增模型保存

Step 2:同步更新predict.py

# 在 predict() 函数开头添加 import joblib xgb_model = joblib.load('models/xgb_model.pkl') # 替换原预测逻辑 def predict(input_data): # input_data 是 list of float,如 [6,148,72,...] proba = xgb_model.predict_proba([input_data])[0] return {'xgb_positive_prob': proba[1], 'xgb_negative_prob': proba[0]}

Step 3:Java 层适配ModelPredictor.java

// 新增 getXGBPrediction() 方法 public Map<String, Double> getXGBPrediction(Map<String, String> features) { // 构造 input_data list,同原逻辑 List<Double> inputData = buildInputList(features); // 调用 jython 执行 predict.py PythonInterpreter interpreter = new PythonInterpreter(); interpreter.set("input_data", inputData); interpreter.execfile("src/main/resources/scripts/predict.py"); // 获取返回值(需在 predict.py 中 print JSON 字符串) PyObject result = interpreter.eval("json.dumps(result_dict)"); return parseJsonResult(result.toString()); }

Step 4:UI 层切换模型开关
在MainController.java的onPredictClick()中:

// 添加 CheckBox:xgbToggle if (xgbToggle.isSelected()) { result = predictor.getXGBPrediction(featureMap); resultLabel.setText(String.format("XGBoost 风险:%d%%", (int)(result.get("xgb_positive_prob") * 100))); } else { result = predictor.getPrediction(featureMap); // 原 RF 逻辑 }

5.3 验证 XGBoost 是否真正生效?

不能只看控制台输出,必须做三重验证:

  1. 日志验证:在predict.py末尾加print("XGBoost model loaded, n_trees:", xgb_model.n_estimators),运行时观察控制台是否输出XGBoost model loaded, n_trees: 200;
  2. 数值验证:用固定输入[6,148,72,35,0,33.6,0.627,50],分别运行原 RF 和新 XGBoost,对比positive_prob输出值(RF 应为 0.821,XGBoost 应为 0.853);
  3. 可视化验证:运行docs/scripts/plot_feature_importance.py,生成xgb_feature_importance.png,确认Glucose_BMI交互项重要性排名升至第 2(RF 中排第 4)。

6. 从毕设到落地:我把答辩系统改造成科室日常工具的三个技巧

6.1 把 JavaFX 界面变成「护士友好型」的三处微调

答辩系统默认面向开发者,但真正在医院用的是护士——她们需要的是「零学习成本」。我在附属医院信息科实测后做了这些改造:

  • 输入字段重排序:原顺序是Pregnancies → Glucose → BloodPressure → SkinThickness → Insulin → BMI → DiabetesPedigree → Age,但护士录入习惯是「先填基础信息(年龄、BMI),再填检测值(血糖、血压)」。调整 FXML 中VBox子节点顺序,把ageField和bmiField提到最前;
  • 单位自动补全:在ageField失去焦点时,自动追加「岁」字:
    ageField.focusedProperty().addListener((obs, old, isFocused) -> { if (!isFocused && !ageField.getText().endsWith("岁")) { ageField.setText(ageField.getText() + "岁"); } });
  • 一键清空按钮:在界面右下角加clearBtn,点击执行:
    clearBtn.setOnAction(e -> { ageField.clear(); glucoseField.clear(); // 逐个清空 resultLabel.setText(""); resultBar.setProgress(0); // 关键:重置所有输入框背景色为白色(原红色错误态残留) ageField.setStyle("-fx-background-color: white;"); });

6.2 用jython调用本地 Python 环境的终极方案

原项目用 jython 2.7.3 是为跨平台,但它对numpy的兼容性差(尤其 Windows 上pip install numpy常失败)。我的替代方案是:让 Java 直接调用系统 Python。修改ModelPredictor.java:

public Map<String, Double> predictWithSystemPython(Map<String, String> features) { try { // 构造命令:python predict_local.py --age 6 --glucose 148 ... ProcessBuilder pb = new ProcessBuilder("python", "src/main/resources/scripts/predict_local.py", "--age", features.get("age"), "--glucose", features.get("glucose")); pb.redirectErrorStream(true); Process process = pb.start(); BufferedReader reader = new BufferedReader( new InputStreamReader(process.getInputStream())); String line; StringBuilder output = new StringBuilder(); while ((line = reader.readLine()) != null) { output.append(line); } process.waitFor(); // 解析 Python 输出的 JSON 字符串 return parseJsonResult(output.toString()); } catch (Exception e) { throw new RuntimeException("Python subprocess failed", e); } }

对应predict_local.py用argparse解析参数,用sklearn原生加载模型——这样就能用你 Anaconda 里装好的完整科学计算栈,不再受 jython 限制。

6.3 文档生成自动化:用 FreeMarker 动态填充实验报告

docs/实验报告.md里大量重复数据(如准确率、AUC 值),每次改模型都要手动更新。我用 FreeMarker 模板report_template.ftl实现自动生成:

## 实验结果 | 模型 | 准确率 | AUC | 推理耗时(ms) | |------|--------|-----|--------------| | <#if data.model == "xgboost">XGBoost<#else>RandomForest</#if> | ${data.accuracy?string["0.00%"]} | ${data.auc?string["0.000"]} | ${data.latency} | ## 特征重要性 <#list data.feature_importance as item> - ${item.name}: ${item.weight?string["0.000"]} </#list>

然后写GenerateReport.java:

Configuration cfg = new Configuration(Configuration.VERSION_2_3_31); cfg.setClassForTemplateLoading(ReportGenerator.class, "/templates/"); Template template = cfg.getTemplate("report_template.ftl"); Map<String, Object> data = new HashMap<>(); data.put("model", "xgboost"); data.put("accuracy", 0.792); data.put("auc", 0.853); // ... 填充所有字段 Writer out = new FileWriter("docs/实验报告_自动生成.md"); template.process(data, out); out.close();

现在每次跑完新实验,双击GenerateReport.jar就生成最新报告,再也不怕答辩前改模型忘更新文档。

从那以后我每次给学生改毕设,都强制走一遍「删掉所有 UI,只留 core 包跑通预测」——这能瞬间暴露模型层缺陷;再走一遍「用系统 Python 替代 jython」——这能绕过 80% 的环境问题;最后一定生成一份实验报告_自动生成.md——因为评审老师永远只信白纸黑字的数据,不信你口头说的「效果更好」。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/26 13:49:52

机器学习检测恶意代码:基于smali opcode与3-gram的静态检测流水线解析

简介&#xff1a;这是一套面向恶意代码检测的机器学习源码项目&#xff0c;项目聚焦Android应用smali指令序列&#xff0c;通过提取3-gram操作码特征&#xff0c;并利用TF与TF-IDF两种加权方式构建高区分度特征集&#xff0c;随后训练二分类模型并输出预测结果、TPR/FPR指标及R…

作者头像 李华
网站建设 2026/9/26 13:49:00

Wand-Enhancer开源补丁:Windows游戏辅助工具注入与拦截技术解析

1. 从标题说起&#xff1a;这个工具到底解决什么问题Wand 这个名字&#xff0c;在游戏辅助和系统增强这个圈子里其实不算陌生。它本质上是一类运行在 Windows 平台上的辅助工具&#xff0c;核心能力是给用户提供游戏内的数值调整、界面增强、快捷操作等功能。而 WeMod 则是另一…

作者头像 李华
网站建设 2026/9/26 13:46:31

语音验证码接口接入实战:从鉴权签名到返回码与回调避坑全指南

语音验证码接口文档看一遍容易&#xff0c;用起来全是坑。接口地址拼错一个斜杠&#xff0c;请求参数漏了被叫号码&#xff0c;返回码100006在线上挂半天排查不出来——这些我全遇到过。以前做客服系统加语音通知模块&#xff0c;我把好几家云通信平台的语音验证码接口反复调了…

作者头像 李华