Python与Django实现糖尿病预测系统:从数据处理到Web部署全流程

发布时间:2026/9/18 1:39:24
Python与Django实现糖尿病预测系统:从数据处理到Web部署全流程 简介一份基于Python和Django框架的糖尿病预测系统设计论文文档面向计算机、软件工程等专业学生完成毕业设计也适合需要从零构建健康预测Web系统的开发者作为参考。内容以西南财经大学学士学位论文为模板按绪论、相关技术、系统需求分析、数据预处理与特征选择、模型构建与训练、系统实现、系统测试与性能分析等章节展开完整展示了数据收集、清洗、标准化、缺失值处理、异常值检测、特征选择策略以及逻辑回归、决策树等模型训练与评估过程并详细说明了Django框架下用户注册登录、数据输入、预测结果展示等功能的界面与数据库设计以及系统稳定性与安全性分析。资源为1个docx文件压缩包约31KB体积小巧但包含完整的目录结构、摘要、章节正文与参考文献便于直接阅读、复制和二次编辑。已有243人学习下载可以作为毕业设计选题、论文结构安排、实验设计与系统开发的重要参考。1. 糖尿病预测系统的技术全景Python 建模、Django 落地的完整闭环一个体检中心拿到几千行体检指标想从血糖、BMI、年龄这些字段里筛出高风险人群于是找人开发“糖尿病预测系统”。这类系统在实际交付中形态非常固定数据清洗用 pandas模型训练用 scikit-learn模型序列化用 pickle 或 joblib外层套一个 Django 项目处理表单请求、渲染预测结果、记录历史数据。算法本身并不复杂真正的难点在于把“原始表格”变成“可解释的预测服务”再把预测服务嵌入 MTV 架构的 Web 页面里。这篇文章按“数据 → 模型 → Web → 部署”的顺序把每个环节的命令、参数和边界讲清楚适合已经会 Python 基础语法、想独立完成一个完整项目的开发者也适合刚入门 Django 但不确定模型怎么接进视图的读者。2. 先用 pandas 和 scikit-learn 处理糖尿病数据清洗、标准化与特征选择2.1 加载数据集并处理常见的 0 值缺失糖尿病预测最常用的公开数据集包含 8 个特征字段Pregnancies、Glucose、BloodPressure、SkinThickness、Insulin、BMI、DiabetesPedigreeFunction、Age以及目标字段 Outcome。这份数据最典型的坑是 Glucose、BloodPressure、SkinThickness、Insulin、BMI 这五列用 0 表示缺失值而 0 在生理指标上根本不合法。先用 pandas 检查缺失分布import pandas as pd df pd.read_csv(diabetes.csv) print(df.shape) print(df.isnull().sum()) print((df[[Glucose, BloodPressure, SkinThickness, Insulin, BMI]] 0).sum())逻辑说明isnull()查的是 NaN但这份数据里 0 才是脏数据所以第二行用 0统计非法值。若 Insulin 列有接近一半的 0不能简单删行否则样本量损失太大。常见做法是把这些 0 替换成对应列的中位数for col in [Glucose, BloodPressure, SkinThickness, Insulin, BMI]: median_val df.loc[df[col] ! 0, col].median() df[col] df[col].replace(0, median_val)参数说明loc[df[col] ! 0, col].median()先排除 0 再算中位数避免把脏数据纳入统计replace(0, median_val)只替换该列的 0 值。选中位数而不是均值是因为体检指标常右偏均值会被极端高值拉高中位数更稳健。2.2 特征分布与相关性分析处理完缺失后先看各特征与 Outcome 的关系再做筛选。计算皮尔逊相关系数是一个快速有效的起点8 个特征里通常 Glucose 与 Outcome 的相关性最高约 0.47而 SkinThickness、Pregnancies 等相对较弱。用以下代码做初步判断corr df.corr()[Outcome].sort_values(ascendingFalse) print(corr)逻辑说明df.corr()计算全部数值列的相关系数矩阵只取Outcome这一列并降序排列。注意相关系数只反映线性关系不排除弱相关特征在组合后的价值所以这一步只是辅助不是唯一的取舍依据。脏数据导致的 0 值在特征分析中的影响容易被忽视。比如某行 Glucose 为 0相关系数会被显著拉低严重时会把本来重要的特征误判成无关特征。这也是为什么我在处理顺序上坚持先清洗再做相关性分析。若发现某列 0 值占比过高比如 Insulin 超过 40%可以考虑将该列转为二值化特征是否异常而不是强行用中位数填补后参与线性模型计算。2.3 划分训练集与测试集StandardScaler 的 fit 和 transform数据准备完毕划分训练集与测试集。这里要特别注意StandardScaler 必须在训练集上 fit再用同一个 scaler transform 测试集不允许对全量数据 fit 后再划分。from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler feature_cols [Pregnancies, Glucose, BloodPressure, SkinThickness, Insulin, BMI, DiabetesPedigreeFunction, Age] X df[feature_cols] y df[Outcome] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test)参数说明test_size0.2表示 20% 数据留作测试random_state42固定随机种子保证每次运行划分一致便于复现stratifyy让训练集和测试集中 0/1 两类样本的比例与原数据集一致这在正负样本不均衡时尤其关键。若去掉stratify可能某个子集里全是阴性样本训练出的模型会严重偏向多数类。最后一行scaler.transform(X_test)不做 fit只用训练集学到的均值与方差做标准化这是防止数据泄漏的标准做法。3. 训练糖尿病预测模型逻辑回归还是随机森林要解释性还是要精度3.1 逻辑回归的系数可以直接解释风险因素对于医疗辅助判断类系统逻辑回归是首选基线模型因为它给出的是每个特征的权重系数能直接回答“血糖每升高一个标准差风险概率怎么变”这类问题。from sklearn.linear_model import LogisticRegression model LogisticRegression(max_iter1000, C1.0, class_weightbalanced) model.fit(X_train_scaled, y_train) coef_df pd.DataFrame({ feature: feature_cols, coef: model.coef_[0] }).sort_values(coef, ascendingFalse) print(coef_df)逻辑说明model.coef_[0]的形状是(1, n_features)因为二分类逻辑回归只训练一组权重。系数为正表示该特征值越大预测为糖尿病的概率越高系数绝对值越大影响力越强。输出里通常 Glucose 和 BMI 的系数最大这个结果要能和医学常识对上否则要回头查数据问题。max_iter1000是梯度下降最大迭代次数默认 100 在特征量纲不一致且样本量偏大时可能不收敛会出现警告C1.0是正则化强度的倒数值越小正则化越强模型越不容易过拟合调参时可以尝试 0.1、1.0、10 再配合交叉验证选择。class_weightbalanced根据类别频率自动调整权重缓解负样本多于正样本带来的偏向预测问题。3.2 随机森林与 XGBoost 的取舍逻辑回归的问题在于它假设特征与 log-odds 呈线性关系而现实中血糖、BMI 与糖尿病风险之间可能存在阈值效应。随机森林能捕捉非线性关系且对特征尺度不敏感即使忘记做标准化也能训练。用随机森林做对比from sklearn.ensemble import RandomForestClassifier rf RandomForestClassifier( n_estimators300, max_depth6, min_samples_leaf5, random_state42, class_weightbalanced ) rf.fit(X_train, y_train) importance pd.DataFrame({ feature: feature_cols, importance: rf.feature_importances_ }).sort_values(importance, ascendingFalse) print(importance)参数说明n_estimators300是决策树数量不是越大越好300 之后精度提升趋缓但推理耗时线性增长max_depth6限制每棵树深度防止单棵树记住噪声min_samples_leaf5强制叶子节点至少 5 个样本是控制过拟合最有效的参数。随机森林不需要把X_train做标准化后再扔进去因为树模型只做特征值切分不受量纲影响但决策边界不如逻辑回归平滑也不容易给出连续的概率解释。实际项目中的选择标准我一般是如果客户要求医生能看懂判断依据走逻辑回归如果只要求 AUC 尽可能高走随机森林或 XGBoost。也可以两个都训练把 AUC 和可解释性放在同一张表里对比。特征数只有 8 个时随机森林的调参空间有限不要太指望 n_estimators 从 100 调到 500 会带来质的提升。3.3 评估指标看 recall、precision 和 ROC-AUC糖尿病预测属于典型的“少数类更重要”问题漏掉一个高风险病人比误报一个低风险病人的代价高得多所以不能只看 accuracy。用 classification_report 和 roc_auc_score 评估from sklearn.metrics import classification_report, roc_auc_score, confusion_matrix y_pred rf.predict(X_test) y_proba rf.predict_proba(X_test)[:, 1] print(classification_report(y_test, y_pred, target_names[阴性, 阳性])) print(AUC:, roc_auc_score(y_test, y_proba)) print(confusion_matrix(y_test, y_pred))逻辑说明predict_proba(X_test)[:, 1]取“阳性”类别的概率ROC-AUC 基于这个概率计算不依赖阈值AUC 0.85 以上在公开糖尿病数据集上算合格水平。classification_report 里重点看阳性类的 recall目标是把 recall 提到 0.8 以上哪怕 precision 降到 0.7 也可以接受。confusion_matrix的行是真实类别列是预测类别要读清楚到底是哪一类被误判得多。3.4 用 joblib 打包模型和标准化器避免部署时重复 fit模型训练完成后建议用 sklearn Pipeline 把标准化和模型绑在一起再序列化导出这样 Django 端只需要加载一个文件from sklearn.pipeline import Pipeline import joblib pipe Pipeline([ (scaler, scaler), (clf, rf) ]) pipe.fit(X_train, y_train) joblib.dump(pipe, diabetes_pipeline.pkl)逻辑说明Pipeline 的 fit 会先执行 scaler.fit_transform再执行 rf.fitpredict 时自动做 transform。这样在 Django 视图里只需要joblib.load一次不会出现“训练时标准化了、预测时忘了标准化”这类低级错误。diabetes_pipeline.pkl就是后续 Web 系统与模型之间的唯一接口。4. 用 Django 把模型包成 Web 服务MTV 分层、表单提交与结果展示4.1 创建 Django 项目和 predictor 应用先建虚拟环境并安装依赖避免污染系统 Python。Windows 下用venv\Scripts\activateLinux 或 macOS 用source venv/bin/activate。python -m venv venv venv\Scripts\activate # Windows 环境 pip install django joblib scikit-learn pandas numpy django-admin startproject diabetes_site cd diabetes_site python manage.py startapp predictor逻辑说明django-admin startproject创建的是整个站点配置startapp创建的是业务模块。把预测相关的 views、models、模板都放进 predictor 应用里保持“一个应用只做一件事”的分层习惯。生产环境中 Django 只负责 Web 层不负责训练模型模型文件通过joblib.load读入内存训练脚本独立维护。4.2 settings.py 注册应用并配置模板与静态资源打开diabetes_site/settings.py在INSTALLED_APPS列表中加入predictorINSTALLED_APPS [ django.contrib.admin, django.contrib.auth, django.contrib.contenttypes, django.contrib.sessions, django.contrib.messages, django.contrib.staticfiles, predictor, ]逻辑说明Django 通过INSTALLED_APPS发现应用下的 models、admin、migrations 等模块。新版 Django 的模板目录默认按应用自动发现即predictor/templates/predictor/无需手动改 TEMPLATES 配置这也意味着模板文件必须放在应用目录内的templates/predictor/子目录下不能直接放在templates/根目录。4.3 定义糖尿病预测记录的 Model模型历史记录是这类系统常见的需求既方便自测时验证结果也能给医生留一个回看的入口。在predictor/models.py中from django.db import models class DiabetesRecord(models.Model): pregnancies models.FloatField(verbose_name怀孕次数) glucose models.FloatField(verbose_name血糖) blood_pressure models.FloatField(verbose_name血压) skin_thickness models.FloatField(verbose_name皮褶厚度) insulin models.FloatField(verbose_name胰岛素) bmi models.FloatField(verbose_nameBMI) diabetes_pedigree models.FloatField(verbose_name糖尿病家族史系数) age models.IntegerField(verbose_name年龄) risk_label models.CharField(max_length10, verbose_name风险等级) risk_prob models.FloatField(verbose_name风险概率) created_at models.DateTimeField(auto_now_addTrue, verbose_name预测时间) class Meta: ordering [-created_at]逻辑说明字段类型与训练数据的特征一一对应FloatField存连续值age是整数用IntegerField。risk_label存的是“高风险”或“低风险”这类文字risk_prob存模型输出的概率值二分类里 0.5 作为默认阈值但阈值本身应该在上线前根据 recall/precision 的权衡确认。ordering [-created_at]让后台列表按时间倒序显示查询时无需额外写排序逻辑。然后执行迁移让 Django 在数据库里建出这张表python manage.py makemigrations predictor python manage.py migrate参数说明makemigrations根据 models.py 的变化生成迁移文件migrate把迁移应用到数据库。若添加字段后忘记迁移调用接口时会报no such column错误这是 Django 初学者最常踩的坑。4.4 视图函数加载模型文件、组装特征向量、调用 predict_proba核心逻辑都在predictor/views.py里。一个关键设计是模型文件在模块加载时只 load 一次而不是每次请求都joblib.load。import joblib import numpy as np from django.shortcuts import render from .models import DiabetesRecord pipe joblib.load(predictor/diabetes_pipeline.pkl) FEATURE_ORDER [ Pregnancies, Glucose, BloodPressure, SkinThickness, Insulin, BMI, DiabetesPedigreeFunction, Age ] FORM_FIELD_MAP { pregnancies: Pregnancies, glucose: Glucose, blood_pressure: BloodPressure, skin_thickness: SkinThickness, insulin: Insulin, bmi: BMI, diabetes_pedigree: DiabetesPedigreeFunction, age: Age, } def predict_view(request): context {} if request.method POST: try: raw_values [float(request.POST.get(key, 0)) for key in FORM_FIELD_MAP] features np.array([raw_values], dtypefloat) proba pipe.predict_proba(features)[0, 1] label 高风险 if proba 0.5 else 低风险 DiabetesRecord.objects.create( pregnanciesraw_values[0], glucoseraw_values[1], blood_pressureraw_values[2], skin_thicknessraw_values[3], insulinraw_values[4], bmiraw_values[5], diabetes_pedigreeraw_values[6], ageint(raw_values[7]), risk_labellabel, risk_probround(float(proba), 4), ) context[result] { label: label, probability: round(float(proba) * 100, 2), } except ValueError: context[error] 输入必须是数值请检查血糖、BMI 等字段的格式 return render(request, predictor/index.html, context)逻辑说明FORM_FIELD_MAP的键是前端表单控件的name值是训练数据的特征列名两者不能混淆。predict_proba(features)[0, 1]返回的是形状为(1, 2)的二维数组的第二个元素即阳性概率。raw_values的顺序必须与训练时的FEATURE_ORDER完全一致否则特征错位会导致预测结果完全失真。try/except ValueError用于拦截用户输入非数字字符避免 500 错误。这里没有使用 Django Form 类是为了让逻辑更直白项目复杂后建议改用 ModelForm 字段校验。模型的加载路径有个坑joblib.load使用相对路径时实际搜索的是进程的当前工作目录而不是 Django 项目的根目录。如果用python manage.py runserver启动通常没问题但如果用 waitress 或 gunicorn 从其他目录启动可能报文件不存在。我一般用绝对路径比如BASE_DIR / predictor / diabetes_pipeline.pkl其中BASE_DIR从 settings 导入。4.5 模板渲染表单输入、POST 提交与结果回显模板文件放在predictor/templates/predictor/index.html核心是一个表单和结果区域form methodpost action{% url predict %} {% csrf_token %} label怀孕次数 input typetext namepregnancies/label label血糖 input typetext nameglucose/label labelBMI input typetext namebmi/label label年龄 input typetext nameage/label button typesubmit开始预测/button /form {% if result %} div classresult p预测结果{{ result.label }}/p p风险概率{{ result.probability }}%/p /div {% elif error %} p classerror{{ error }}/p {% endif %}参数说明{% csrf_token %}是 Django 防跨站请求伪造的必填项省略会报 403action{% url predict %}采用 URL 反向解析比硬编码/predict/更便于维护。注意一个容易被忽略的细节这里没有引入任何 JavaScript请求是同步的 POST用户点击“开始预测”后会刷新整个页面。如果要做异步体验可以用 fetch 往同一个接口发 POSTDjango 端把响应改成 JsonResponse但这不属于标题里“预测系统”的核心要求通常放在第一阶段之后的优化列表里。4.6 配置 URL 路由在diabetes_site/urls.py中把请求映射到视图from django.contrib import admin from django.urls import path from predictor.views import predict_view urlpatterns [ path(admin/, admin.site.urls), path(, predict_view, namepredict), ]逻辑说明空路径表示首页直接显示预测表单namepredict供模板里的{% url predict %}解析。URL 里没有出现/predict/这在单一功能系统里足够但若后续要加“历史记录”页面建议拆成多个 path。5. 部署时验证模型文件路径用 waitress 替代 runserver 并记录预测日志5.1 绝对路径加载模型文件避免目录切换导致加载失败views.py里的joblib.load路径在开发环境没问题但部署时工作目录可能不同。更稳妥的写法是from pathlib import Path from django.conf import settings MODEL_PATH Path(settings.BASE_DIR) / predictor / diabetes_pipeline.pkl pipe joblib.load(MODEL_PATH)逻辑说明settings.BASE_DIR是 Django 项目根目录的绝对路径不管从哪个目录启动服务Path拼接出的文件路径都是同一个。把模型文件名写成一个变量排在视图函数之外模块导入时只执行一次 load请求处理不重复读文件。部署后若报Model file not found先检查这个路径是否真实存在不要急着怀疑 Django 配置。5.2 用 waitress 替代 runserver单机并发足够runserver是开发服务器性能差且有安全限制。Windows 环境我常用 waitress它支持多线程、无需额外配置底层是 WSGI 服务器pip install waitress新建start_server.pyfrom waitress import serve from diabetes_site.wsgi import application serve(application, host0.0.0.0, port8000, threads8)参数说明threads8表示同时处理 8 个请求这台机器只给几个人内部使用时 8 足够若部署在 Linux 上也可以用gunicorn diabetes_site.wsgi:application -w 4 -b 0.0.0.0:8000-w 4是 worker 进程数通常取 CPU 核心数加 1。上线后可以用下面的命令验证服务是否正常curl -X POST http://127.0.0.1:8000/ \ -d pregnancies2glucose148blood_pressure72skin_thickness35insulin0bmi33.6diabetes_pedigree0.627age50参数说明-X POST指定请求方法-d传表单参数参数名与FORM_FIELD_MAP的键一一对应。正常响应里应包含预测结果高风险这样的文本同时数据库新增一条记录。若返回 500查看终端堆栈日志大部分是字段类型转换失败或模型特征数量不匹配。5.3 一个进阶技巧预测日志直接进 Admin 后台DiabetesRecord虽然没有为每条记录设置显式主键但 Django 会自动创建id字段。在predictor/admin.py里注册from django.contrib import admin from .models import DiabetesRecord admin.register(DiabetesRecord) class DiabetesRecordAdmin(admin.ModelAdmin): list_display [created_at, glucose, bmi, age, risk_label, risk_prob] list_filter [risk_label, created_at]逻辑说明注册后可在/admin/页面查看和筛选每次预测的历史记录这对自测模型效果很有帮助。可以把真实预测结果存下来等积累到一定量后重新评估线上数据的分布是否与训练集一致。这个表的另一个用途是做阈值调优把risk_prob从 0.5 下调到 0.4观察哪些原本被判为低风险的样本翻转为高风险辅助临床判断哪些阈值更符合实际场景。预测系统交付后维护工作大多集中在这张表上而不是模型代码本身。本文还有配套的精品资源点击获取