基于机器学习的肥胖预测系统:从决策树到深度学习的模型对比与Django实现
做毕设选这个题目的时候我其实有点犹豫。机器学习、决策树、深度学习、Django几个词拼在一起看似很“热门”但担心会不会只是一个技术堆砌的大杂烩。做完之后回头想这个项目真正值钱的地方不在于用了多少算法而在于它完整覆盖了一条从数据理解、特征工程、模型对比到Web系统落地的工业级流水线。你要做毕业设计、找实习作品、或者想入门数据科学又不想只停留在跑通Notebook这个项目都能给你一个非常扎实的落脚点。我能用一句话说清楚这东西是什么用机器学习模型对肥胖成因做预测分析并基于决策树、集成模型和深度学习算法做横向对比最后用Django把模型封装成可以在浏览器里直接使用的Web系统。它能做什么采集一个人的饮食、运动、遗传等特征预测肥胖等级还能解释哪些因素影响最大。适合谁计算机、数据科学、软件工程方向的同学尤其想走机器学习工程路线但手里缺一个“能演示、能答辩、能写论文”项目的朋友。1. 毕设选题不能只看热度更要看“能不能做完”很多人选“基于机器学习的XX预测系统”这类题目是为了蹭热度但真正动手才发现数据集难找、特征太乱、算法结果像玄学、Web端又不会集成。我为什么说“肥胖成因分析与对比”这个方向是相对稳妥的因为它在数据、算法、系统三个维度上都刚好卡在“有挑战但可控”的位置。1.1 选题逻辑一个题目同时覆盖数据、算法、系统的闭环肥胖成因预测本质上是一个有监督分类问题目标是根据个体特征预测其肥胖等级常见分类有 Insufficient_Weight、Normal_Weight、Overweight_Level_I、Obesity_Type_I 等。这个场景在公开数据领域有现成的、质量不错的数据集比如UCI机器学习库中的Obesity Dataset样本量不大2000多行特征维度适中16列左右既有连续变量又有类别变量还带明显的类别不平衡。这些特点恰好让它成为教学、毕设场景的“黄金数据”。更妙的是肥胖成因本身存在多因素耦合遗传家族史、饮食习惯、运动频率、作息、交通方式都会影响结果。它不是简单的线性关系所以用决策树、随机森林、甚至深度神经网络来做都有发挥空间且不同模型的表现差异能直观展示“为什么需要算法对比”。这比单纯拿鸢尾花数据集做的“假项目”要有说服力得多。1.2 系统目标和技术路线不只是“算法跑个分”我给自己定的目标是做一个能实际使用的“个人肥胖风险分析系统”而不是交一个训练好的模型文件完事。系统要能完成四件事用户录入个人基本信息和生活习惯后台调用训练好的机器学习模型实时输出肥胖等级预测通过决策树可视化或特征重要性解释“为什么是这个结果”展示不同算法决策树、随机森林、XGBoost、MLP在同一测试集上的性能对比。技术路线也很直接Python 3.8 Django 3.2 pandas scikit-learn TensorFlow/Keras 前端Bootstrap。有人会问深度学习在大数据场景里不是很吃资源吗我这里的“深度学习”并没有上大模型而是针对表格数据设计了一个MLP多层感知机参数量十万以内普通笔记本就能跑。这部分后面会细说。2. 数据处理机器学习里的“七分靠数据”不是玩笑无论你打算用决策树还是深度学习数据没洗干净后面所有模型都是自欺欺人。这个项目里的原始数据是英文编码的比如字段FAF、FCVC、CH2O第一次打开可能看得一脸懵。需要先理解每个字段的业务含义再决定怎么清洗和构造特征。2.1 字段理解与业务映射我用的UCI肥胖数据集字段大致如下字段含义类型Gender性别类别Age年龄连续Height / Weight身高(m)、体重(kg)连续family_history_with_overweight是否有肥胖家族史二分类FAVC是否频繁吃高热量食物二分类FCVC吃蔬菜的频率1-3分离散NCP正餐次数连续CAEC两餐之间是否吃东西类别CH2O每天饮水升数连续SCC是否监测卡路里摄入二分类FAF每周运动频率连续TUE每天使用电子设备时间连续CALC饮酒频率类别MTRANS常用交通方式类别NObeyesdad体重等级标签分类目标这里有个很重要的业务理解Height和Weight可以合并计算出BMI而BMI和最终的肥胖等级标签存在极强的相关关系。如果直接把BMI作为一个特征输入模型模型很容易“作弊”——几乎不用看其他特征就能靠BMI猜出肥胖等级准确率会虚高。但在实际应用中BMI本身就是肥胖等级划分标准的一部分这么做会导致数据泄露掩盖真实成因。所以我在特征工程中会选择不使用BMI或者有意保留原始身高体重让模型从身高体重、生活习惯、遗传因素的组合中学到因果关系。这一点在论文里是很好的讨论素材。2.2 清洗与特征工程实操清洗步骤不复杂但一个都不能少检查缺失值通常这个数据集没有缺失但必须验证类别变量编码二分类用LabelEncoder多分类用OneHotEncoder避免给类别强加数字大小顺序连续变量标准化决策树不要求标准化但MLP和K近邻需要所以我都做了一遍标准化并保存scaler对象供预测时复用划分训练集和测试集按80/20设定random_state42保证可复现类别不平衡处理肥胖等级中Normal_Weight和Obesity_Type_I样本偏多而Insufficient_Weight较少直接用accuracy容易被“多数类”迷惑。这里我用SMOTE做少数类过采样注意只在训练集上做不要在测试集上做否则会数据泄漏。2.3 最容易犯的错误特征顺序不一致这个坑我踩得很重。训练模型时我用pandas的DataFrame传入X_train模型记住了特征的顺序但在Django里接收前端表单数据后如果手动拼dict稍不留神字段顺序就和训练时不一致那模型预测结果就是一团乱码。解决办法是训练时把特征列表单独保存为一个变量比如feature_columns [...]用joblib保存特征列表文件在Django预测时先按这个列表创建空DataFrame再逐一填充顺序绝对不能临时拼。3. 从决策树到深度学习算法选型与对比策略这一部分是项目的核心灵魂。很多人把多个模型跑一遍比个AUC就完事但“为什么选这个模型”“怎么调参”“结果差异说明了什么”才是答辩时老师最想听的。3.1 决策树分类器可解释性拉满的基线模型肥胖成因分析最看重解释性比如“爱吃高热量食物且缺乏运动的人更可能肥胖”这种规则决策树天然能输出。我选用的CART决策树核心思想是每次选一个特征和切分点让切分后子节点的Gini系数下降最大递归下去长出一棵树。决策树有个好处不需要太多预处理类别编码后就能直接训练计算也快。但裸的决策树非常容易过拟合深度设到5的时候甚至能记住噪声。我实验里默认参数的决策树准确率约70%但决策边界很碎限制max_depth5、min_samples_split10后准确率稍有下降但泛化能力明显增强。这也能引出“树模型剪枝”的讨论。补充说明用Graphviz导出树的可视化结构在Django前端展示成图片效果比干巴巴的特征重要性柱状图好得多。3.2 随机森林与XGBoost用集成思路甩开单棵树的瓶颈单棵决策树不稳定稍微改一点样本结果就变。随机森林的思路是**“三个臭皮匠顶个诸葛亮”**训练很多棵用随机子样本、随机特征子集构建的树投票决定结果。这样方差变小能有效抑制过拟合。XGBoost则是用梯度提升的思想去“纠错”每一棵新树都在拟合前面所有树预测结果的残差。在表格数据上XGBoost往往是性价比最高的选择。我对比下来模型准确率F1加权AUC宏平均训练耗时决策树(剪枝)0.780.770.92秒级随机森林(500棵树)0.860.850.98秒级XGBoost(默认)0.890.880.99秒级MLP(两隐层)0.880.870.98分钟级这里有个值得注意的现象XGBoost和MLP表现差距不大甚至在个别类别上树模型更稳。原因在于表格数据有大量离散化、数值稀疏的特征树模型对特征变换的适应机制更好深度学习更擅长图像、语言这种稠密高维数据。但MLP的加入能在论文里引出“神经网络在结构化数据上的边界”这一深度讨论这是拿高分的关键。3.3 用Keras搭一个能跑通的MLP模型深度学习部分不用太华丽我用了一个三层MLPfrom tensorflow.keras.models import Sequential from tensorflow.keras.layers import Dense, Dropout, BatchNormalization from tensorflow.keras.callbacks import EarlyStopping model Sequential([ Dense(64, activationrelu, input_shape(X_train.shape[1],)), BatchNormalization(), Dropout(0.3), Dense(32, activationrelu), BatchNormalization(), Dropout(0.2), Dense(7, activationsoftmax) ]) model.compile(optimizeradam, losssparse_categorical_crossentropy, metrics[accuracy]) early_stop EarlyStopping(monitorval_loss, patience10, restore_best_weightsTrue) history model.fit(X_train_scaled, y_train_encoded, validation_split0.15, epochs200, batch_size32, callbacks[early_stop], verbose1)注意三个细节标准化一定要用训练集的scaler去transform测试集不能重新fit否则信息泄漏EarlyStopping早停是防止过拟合最实用的手段看val_loss而非val_accuracy类别的编码标签需要变成0到6的整数序列预测结果再映射回原来的类别名。3.4 模型对比怎么才说服力对比不能只看准确率因为样本不平衡时准确率会骗人。我最终报告里放了四类指标准确率、加权精确率、加权召回率、加权F1还有一个“宏平均AUC”。另外做了5折交叉验证报告均值±标准差避免因为固定训练测试集划分产生偏差。这样做完之后你会发现不同模型在不同肥胖等级上的表现差异很有意思比如深度学习对Obesity_Type_II的召回率可能更高而XGBoost在整体平衡性上更优。4. Django系统实现把模型从Jupyter搬到Web算法跑完只是开始。有相当多同学卡在“模型怎么让人用起来”这一步而Django正好解决了这个难题。项目里我用Django写了一个MVT架构的Web应用让用户通过表单输入数据后台调用模型返回结果和解释。4.1 系统页面与交互设计我做了一个很朴素但完整的系统包含这些页面首页项目介绍、算法对比图表总览预测页面表单包含所有输入字段每个字段带提示结果页显示预测的肥胖等级、各类别概率条形图、决策树路径解释对比页面用柱状图/折线图展示各模型评估指标。前端用了Bootstrap和ECharts图表数据由Django视图序列化为JSON传给页面。这里关键是接口设计要稳前端只管收集表单后端统一校验和封装最后返回一个JSON结构包括prediction、probability、explanation。4.2 模型加载与预测的完整流程我在ml_utils/predictor.py里封装了统一预测接口import joblib import numpy as np import pandas as pd from tensorflow.keras.models import load_model class ObesityPredictor: def __init__(self): self.feature_columns joblib.load(models/feature_columns.pkl) self.scaler joblib.load(models/scaler.pkl) self.smote_encoder joblib.load(models/label_encoder.pkl) self.xgb_model joblib.load(models/xgb_model.pkl) self.mlp_model load_model(models/mlp_model.h5) def _build_frame(self, form_dict): df pd.DataFrame([{col: form_dict.get(col, 0) for col in self.feature_columns}]) df df[self.feature_columns] return df def predict(self, form_dict): df self._build_frame(form_dict) df_scaled self.scaler.transform(df) xgb_prob self.xgb_model.predict_proba(df)[0] mlp_prob self.mlp_model.predict(df_scaled.reshape(1, -1))[0] # 这里可以融合或分别展示我选择展示两个模型结果供对比 final_class int(np.argmax(xgb_prob)) return self.smote_encoder.inverse_transform([final_class])[0]几个实操要点加载模型放在视图外层避免每次请求都重新加载在Django中我把它放到apps.py的ready()方法里或者用全局单例。Keras的HDF5模型文件可能比较大如果部署到服务器要注意内存。测试时用load_model(mlp_model.h5)没问题但生产环境建议转成TensorFlow SavedModel格式。异常处理如果前端传了非法数值比如身高写成了负数后端要捕获并返回友好提示不能直接抛500。4.3 表单校验与用户输入体验这个项目的数据字段多如果用户填错了很麻烦。我用了Django Form框架每个FloatField设置min_value和max_value对于选择类的字段用ChoiceField限制可选范围。比如FAF每周运动频率范围0-10TUE设备使用时间范围0-24。同时前端加了简单的HTML5校验但后端校验不能省因为接口是可以被直接调用的。4.4 部署层面的小建议如果只是本地演示python manage.py runserver 0.0.0.0:8000就够了。但如果要部署到云服务器或者答辩现场演示最好用gunicorn nginx。模型文件夹不要提交到Git仓库用django-environ配置路径。静态文件和数据库都要按Django正式部署的方式收集避免同学演示时图片加载不出来。5. 论文写作与答辩现场的加分细节毕设项目做完之后论文和答辩是另一场硬仗。这部分不是技术问题而是“怎么把技术价值讲清楚”的问题。5.1 论文结构怎么搭我的论文目录大体是这样绪论背景、国内外研究现状、研究内容相关技术介绍决策树、随机森林、深度学习、Django框架数据获取与预处理数据集来源、清洗、特征工程肥胖预测模型构建四个模型的原理、参数设置、训练过程模型对比与结果分析指标评估、混淆矩阵、特征重要性分析系统设计与实现需求分析、架构设计、功能模块实现总结与展望。这里有一个常见的误区不要大篇幅抄算法原理导师更关心你在项目里怎么用、怎么调、怎么选择。比如第三章写“我为什么保留身高体重而不是直接计算BMI”第四章写“决策树剪枝前后准确率的变化”这些才是有效内容。5.2 常见问题与排查技巧实录这个项目跑通不难但调试过程有几个坑值得记录sklearn版本兼容问题用joblib保存的模型如果换了一台电脑且sklearn版本不匹配加载可能报错。解决办法是固定环境写requirements.txt并注明版本号。Django加载模型后内存占用高MLP模型文件大概几百KB但TensorFlow运行时占了几个GB的内存。如果服务器内存紧张可以只在预测页面所在进程加载模型或将Keras模型转为.tflite不过这会稍微改变预测结果。预测结果全部是同一个类别一般是特征顺序错乱或标准化不对把训练特征列表打出来逐列核对看预测分布是否合理。ECharts图表中文乱码检查后端返回的JSON是否经过ensure_asciiFalse前端html是否设置meta charsetutf-8。表单输入0被当成缺失Django表单里FloatField(requiredFalse)配合clean_方法要注意0是合法值不能当空值过滤掉。决策树可视化乱码/报错Graphviz需要本机安装dot程序不只是pip install graphviz如果只是展示用也可以直接用matplotlib画树节点。5.3 答辩演示的加分操作答辩时别只对着PPT念我会建议做一个“现场预测”环节准备一份典型用户数据比如“20岁、女性、有家族史、每周运动不到1次、爱吃快餐、饮水少”当场在系统里输入展示预测结果为Overweight_Level_I并指出主要成因。这个操作比任何图表都有冲击力也直观体现了系统的实用价值。另外单独准备一张特征重要性排序图大多是FAF运动频率、FCVC蔬菜频率、CAEC餐饮习惯排前面。答辩时老师问“哪个因素影响最大”你能直接回答并解释这和医学常识是吻合的项目就真正立住了。最后再说一个实际项目里的小技巧训练模型时别只用一个random_state。我一开始固定为42后面改成2025后结果波动心态差点崩了。后来学乖了无论是划分数据集还是模型初始化都设好随机种子同时跑几个不同的随机状态看均值和方差。这样既稳得住实验结果又能让答辩老师觉得你懂“可复现性”这件事。这个项目的后续还可以继续扩展比如把年龄、性别分别做分层分析或者采集真实问卷数据做增量训练加上权限管理做成多用户系统发展空间很足。如果你也正在为毕业设计挠头这个题目真的值得认真做一回。