毕设做推荐系统,十个选题里八个是电影,两个是电商。但真正拿高分的,是懂得把算法落到具体场景、解决真实问题的项目。今天这篇就聊聊我用Django+K-means完成的一套校园美食推荐系统——从选题思路、算法原理、代码实现到部署答辩,把整个实战过程完整拆出来。标题里那些“完整源码+LW+部署说明+演示视频”都不是噱头,而是做这个项目必须交付的完整闭环。我会把每一步的思考逻辑、关键代码、踩坑记录都写清楚,正在为毕设发愁的同学可以直接参考。
1. 选题与整体架构:为什么是Django+K-means
1.1 选题背后的逻辑拆解
先说选题。校园美食推荐系统这个题目,听起来没有“基于深度学习的XXX”那么高大上,但仔细分析会发现它非常契合毕设的评审标准。毕设评审看重三点:有明确的应用场景、有核心算法支撑、有完整的工程实现。校园美食推荐恰好满足全部条件——场景具体(学生吃饭)、算法明确(聚类推荐)、工程完整(Web系统)。而且数据量不需要几百万条,几千条用户行为数据就能把效果展示清楚,这对毕设来说反而是优势。
更关键的是,这个选题避开了“大而空”的陷阱。很多人喜欢选那种听起来特别复杂的题目,结果做出来只能演示一个半成品页面,算法部分全靠截图,答辩时被老师一问就露馅。校园美食推荐系统的体量控制得很好:Django负责完整的Web框架,K-means负责推荐算法的核心逻辑,两者结合既能展示工程能力,又能体现算法功底,难度曲线对本科生和硕士研究生都很友好。
1.2 Django在毕设项目中的实用性分析
Django为什么适合做毕设?我个人的体会是Python生态降低了开发和调试的负担,而Django自带ORM、Admin后台、模板引擎、用户认证这些功能,省去大量重复造轮子的工作。对比一下,如果用Java的Spring Boot,光是环境配置和依赖管理就能磨掉你一周的耐心;用Flask虽然更轻量,但用户系统、Admin管理、数据库迁移这些都得自己搭,毕设周期根本拖不起。
Django还有一个隐藏优势——它的MTV模式(Model-Template-View)非常适合体现在论文的技术架构图里。你可以在论文里画一张结构分明的架构图,评委一看就能理解整个系统的数据流转。用Django自带的后台管理,你甚至不写一行代码就能管理用户和菜品数据,对快速开发来说非常友好。
1.3 核心架构与数据流
整个系统的架构分三层。最底层是数据层,用SQLite数据库存储用户、菜品、评分、行为日志等数据(如果要求必须用MySQL,换一下DATABASES配置就行)。中间是业务层,Django的View处理请求,调用K-means聚类模块和推荐引擎生成推荐列表。最上层是展示层,用Django模板系统渲染页面,配合ECharts做可视化图表展示聚类结果。
数据流向是这样的:用户在前端页面浏览菜品、打分、点击查看详情,这些行为记录到数据库;系统定期触发(或用户点击推荐按钮时手动触发)K-means聚类任务,把所有用户按口味偏好聚类;当一个用户进入推荐页面时,系统先判断该用户属于哪个分类(或根据历史行为计算离哪个聚类中心最近),然后提取同类用户的共同偏好,生成推荐菜品列表。
这套设计让推荐过程清晰可解释:不是把K-means当作黑盒子,而是每个环节都有明确输入输出。写论文的时候可以画一张完整的数据流图,从行为采集到聚类计算到推荐生成,每一步都有理有据。
2. K-means算法原理与推荐场景结合
2.1 K-means核心机制回顾
K-means的工作原理用一句话概括:把n个点分成k个簇,让每个点离自己所在簇的中心最近。算法流程四步走——随机选k个初始中心 → 计算每个点到各中心的距离并归属最近的簇 → 重新计算每个簇的质心 → 重复迭代直到质心变化小于阈值。
这里要理解K-means本质解决的问题:无监督聚类,也就是在没有标签的情况下,按照特征把用户天然分成几类。用在美食推荐里,就是根据用户的评分行为、口味偏好特征、消费时间特征等,把学生群体划分成“重口味党”“清淡养生派”“甜品爱好者”等不同族群。
2.2 特征工程:推荐系统的关键一步
K-means能不能发挥效果,特征工程占七成。毕设里我设计了这样几个特征维度:
- 菜品口味特征:辣、甜、咸、清淡、油腻,每种菜品一个五维向量,比如麻辣香锅就是(5, 1, 3, 2, 4)
- 用户消费习惯:早中晚三餐的消费频率、平均消费金额
- 用户评分行为:对哪些口味的菜品打分高,对哪些品类点击次数多
- 时间偏好:倾向于饭点前还是饭后购买,夜宵频率等
原始数据不好直接拿来聚类,必须做归一化处理。用户间特征量纲不一致,比如A用户一个月消费20次,B用户消费5次,直接用原始值计算欧氏距离,消费次数会主导聚类结果。我用的方法是Min-Max归一化,把所有特征压缩到0-1区间。
2.3 K值选择:肘部法则与轮廓系数
K值怎么确定?毕设答辩最容易被追问的点就是这里。两个常用方法:肘部法则(Elbow Method)和轮廓系数(Silhouette Coefficient)。肘部法则的原理是:随着K增大,簇内误差平方和(SSE)会下降,但下降速度在某一点后变缓,这个“肘部”就是最优K值。
轮廓系数的取值范围是-1到1,越接近1说明聚类效果越好。我当时用Python脚本把K从2跑到10,记录SSE和轮廓系数,画了一张折线图放在论文里。数据结果:K=3时SSE下降开始变缓,K=3时轮廓系数最高,所以最终选定3个簇。这一步的截图放在论文里很加分,向老师展示了你是真的调过参、比较过结果的。
代码参考:
from sklearn.cluster import KMeans import matplotlib.pyplot as plt # X是归一化后的特征矩阵 sse = [] sil_scores = [] K_range = range(2, 11) for k in K_range: km = KMeans(n_clusters=k, init='k-means++', random_state=42) km.fit(X) sse.append(km.inertia_) sil_scores.append(silhouette_score(X, km.labels_)) # 绘制肘部法则图 plt.plot(K_range, sse, 'bo-') plt.xlabel('K') plt.ylabel('SSE') plt.title('Elbow Method') plt.show()实际跑下来,k=3的时候SSE曲线已经有了明显拐点,轮廓系数大概在0.62左右。如果你跑出来的效果不够清晰,可以检查特征是否归一化,或者适当增加数据量。
2.4 聚类结果如何生成推荐
聚类只是手段,推荐才是目的。我的推荐策略分两种情况:
用户已有历史行为数据时:把用户特征向量算出来,用欧氏距离找最近的聚类中心,确定用户属于哪一类;然后取该类簇内Top10最热门的菜品,再根据用户历史未消费记录过滤,优先推荐同类用户评分高但该用户还没吃过的菜。
新用户冷启动时:系统刚上线,用户没有历史记录,这时直接推荐全校学生整体评分最高的几道菜,同时推荐当前所在校区附近的热销菜品——这就是“热门推荐”兜底策略。
Django后台代码里增加一个推荐逻辑模块:
def get_recommendations(user_id): user = User.objects.get(id=user_id) user_vector = build_user_vector(user) if user_vector is None: # 冷启动:返回热门菜品 return Dish.objects.order_by('-avg_rating')[:10] kmeans_model = load_model() cluster_label = kmeans_model.predict([user_vector])[0] # 获取同簇用户 similar_users = get_users_in_cluster(cluster_label) # 获取这些用户高分但当前用户未消费的菜品 candidate_dishes = get_high_rated_dishes_exclude_user(similar_users, user) return candidate_dishes[:10]3. 系统核心模块设计与实现
3.1 数据库表结构与ORM模型
数据表设计是系统的基础,我总共设计了六张核心表:
- User用户表:Django自带User模型扩展,增加口味偏好字段
- Dish菜品表:菜品名称、图片、价格、校区、窗口编号、评分
- Rating评分表:用户ID、菜品ID、评分(1-5分)、评论内容、时间
- BehaviorLog行为日志表:浏览、点击、收藏、购买等行为记录
- ClusterResult聚类结果表:用户ID、簇标签、距簇心距离
- RecommendCache推荐缓存表:用户ID、推荐菜品列表、生成时间
Django ORM定义模型,迁移数据库,这套组合拳下来省去了手写SQL的繁琐,而且数据库结构可以直接导入论文附录,评审老师看着很规范。
关键的M2M关系和索引要在设计时规划好,否则数据量一大就慢。比如Rating表建议给(user_id, dish_id)加联合索引,BehaviorLog表给(user_id, action_type)加索引,Django的meta类里设置db_index=True。
3.2 数据采集:BLOCK无法爬虫环境下的数据集方案
写毕设最怕什么?没有数据。起初我想爬学校点评系统的数据,但爬虫很容易触发反爬,而且涉及合规风险。后来换了个更稳的方案:用一个Python脚本自己生成模拟数据。
我设计了一个数据生成器,用吃饭口味把“目标用户画像”分成了四类——重口辣党、清淡养生派、甜口爱好者、性价比干饭人;然后按照每类画像生成6000条用户行为记录,包括评分、点击、消费时间等字段;再手工标注50道真实存在的校园菜品数据。这样造出来的数据用于算法验证,既能保证聚类效果看得见,又不涉及隐私风险。
生成数据的代码逻辑很简单:
import random import pandas as pd specialties = ['重口辣党', '清淡养生派', '甜口爱好者', '性价比干饭人'] # 根据画像生成用户特征 def gen_user_vector(specialty): if specialty == '重口辣党': return [random.uniform(0.7, 1.0), random.uniform(0, 0.3), random.uniform(0.3, 0.5), ...] elif specialty == '清淡养生派': ... # 其余画像类似这套模拟数据方案不仅解决了数据来源问题,还因为“标签已知”方便做聚类效果验证:如果聚类出来的分类与预设画像高度重合,就说明算法实现正确,少了很多调试的麻烦事。
3.3 推荐引擎核心代码实现
推荐引擎是核心模块,Django中我用独立的recommend.py模块封装了所有推荐逻辑。主要函数包括数据预处理函数、K-means训练函数、推荐生成函数。注意一个设计细节:聚类模型不是每次请求都重新训练,而是定时(比如每天凌晨)训练一次,训练完保存模型文件,推荐时直接加载。
# recommend.py from sklearn.cluster import KMeans import joblib from .models import Rating, UserProfile, Dish MODEL_PATH = 'models/user_kmeans.pkl' def train_cluster_model(): df = build_feature_matrix() # 从数据库读取数据并构建特征矩阵 kmeans = KMeans(n_clusters=3, init='k-means++', random_state=42) kmeans.fit(df) joblib.dump(kmeans, MODEL_PATH) return kmeans def predict_cluster(user): kmeans = joblib.load(MODEL_PATH) vec = single_user_feature_vector(user) return kmeans.predict([vec])[0]这里分享一个重要技巧:k-means++初始化点,避免随机初始质心影响聚类稳定性。第一次跑的时候我发现两次聚类结果不一致,用户归类在变化,后来换了k-means++之后结果就很稳定了。
3.4 可视化模块:为论文加分的关键
推荐系统除了推荐列表,还必须有可视化展示。我在“数据分析”页面放了几个图表:聚类散点图(用PCA降维到二维展示三簇分布)、各簇用户画像雷达图、菜品评分分布柱状图、推荐命中率趋势图。
前端用ECharts,后端用Django的JsonResponse把数据传给前端JS。ECharts在毕设里非常好用,不用花太多精力就能做出很专业的图表。比如雷达图,能直观看出“重口辣党”在辣这一项评分远高于平均,光这一点放到论文里就是很好的说明材料。
4. 项目完整落地:从代码到LW再到答辩演示
4.1 环境配置与部署完整流程
项目配套的部署说明,关键步骤要写得清清楚楚。整个流程分环境准备、依赖安装、数据库初始化、静态文件收集、启动服务五步。
我用的是Python 3.9 + Django 3.2,数据库先用SQLite,后期若视频里要求换MySQL,改settings.py的DATABASES配置并执行makemigrations/migrate就行。依赖清单放在requirements.txt里,部署时一键pip install -r requirements.txt。
# 部署核心步骤 python -m venv venv venv\Scripts\activate # Windows激活虚拟环境 pip install -r requirements.txt python manage.py makemigrations python manage.py migrate python manage.py collectstatic python manage.py runserver 0.0.0.0:8000这里要提醒一个新人最容易踩的坑,static静态文件配置。Django默认开发环境下能读取APP下的static目录,但部署时用collectstatic把所有静态文件集中后,Nginx配置和STATIC_ROOT设置对应好,不然页面样式全部丢失。
如果要求部署到服务器,docker-compose配置也搞一份,Django用gunicorn跑,Nginx做反向代理,MySQL加volume,一套下来演示的时候直接开浏览器访问IP,不给评委任何挑刺的空间。
4.2 LW文档的写作思路与避坑指南
毕设论文写作有非常成熟的套路可循。我按院校要求的模板,一篇LW大概是这样的骨架——摘要、Abstract、绪论、技术和算法介绍、需求分析、系统设计、系统实现、测试、总结、参考文献、致谢。
写LW时最高效的顺序是:先搭完整骨架框架,再填充图和表,最后补文字。重点发力在算法描述部分,把数学公式、伪代码、流程图画好;K-means的原理推导写细致,特征工程的设计逻辑写清楚,推荐流程用一张时序图说明。代码不整段贴,核心算法的关键代码贴20行左右加注释就行,突出原创性。
查重是很多人的痛点。技巧在这里——图纸公式大概率不算文字重复;对比经典实验的数据表格替换成自己跑的结果;技术介绍不要大段摘抄,用自己的逻辑“串”起来;代码不要贴公共库的标准调用方法,贴自己改动过的核心算法部分。
4.3 演示视频录制与答辩技巧
演示视频要遵循“先完整走流程、后看效果”的节奏。录屏工具用OBS或EV录屏,分辨率调到1920x1080,窗口大小不变,声音清晰。视频结构大概分四步:开头展示项目运行环境与启动过程;演示用户注册登录与基础功能;进入数据分析页面前展示聚类效果图;最后操作推荐模块,输入不同口味的用户特征,展示推荐结果差异化。
答辩PPT不超过10页,放一句话点睛的题目,“基于聚类分析的校园餐饮智能推荐策略研究”这种标题比原题更能体现思考。第一页摘要,第二页技术栈全景,第三页K-means原理图,后面每页功能模块配界面截图。答辩时评委常问的就是“为什么选K-means”“数据从哪来的”“K值怎么定的”“推荐效果如何验证”这四个问题,在这四页PPT上提前埋好答案。
经验之谈:答辩演示时不要演示登陆注册那种标配页面,直接切重点。评委在意的是你有没有独立完成算法实现的能力,不是看你会不会设计个登录框。演示推荐功能时,切两个账号——一个账号刚注册有冷启动热门推荐、一个老账号聚类后推荐更个性化,对比效果立竿见影。
4.4 常见问题与排查技巧实录
毕设期间踩的坑,整理出来给大家排雷。
Django版本兼容问题。我一开始用Django最新版(当时是4.x),结果第三方库和教程的代码对不上,报错一堆。后来装回3.2 LTS稳定版本,问题一次清零。这是新人的经典坑,版本选择永远选稳定版,不要用最新的。
静态文件加载不出来的问题。现象是页面有HTML结构但CSS和图片全丢了,排查办法:浏览器按F12看Network里静态文件的响应状态,500代码基本是路径问题,404代码基本是配置问题。最笨但有效的方法是把STATIC_URL改成绝对路径试试。
K-means聚类结果为空簇的问题。某次测试K=5时,有一个簇的成员数为0,说明初始质心选得太远。解决方案就是用k-means++初始化,并检查特征归一化。若还有空簇,减少K值或增加样本量。
用户系统密码加密的问题。第一次做的时候想省事,直接在数据库存了明文密码,后来发现Django自带auth系统已经集成了PBKDF2加密,根本不需要自己处理,用auth模块自带的User模型就好了。
5. 项目扩展空间与你的下一步动作
校园美食推荐系统做完只是起点。这个框架可以带出来的扩展方向很多:换成基于协同过滤的算法做对比实验;把K-means和DBSCAN的结果对比;数据可视化部分换成大屏展示;用Django Channels把推荐结果实时推送到前端用户页面。这些扩展方向在论文总结和展望部分写上,能体现思考深度,答辩时既能堵住老师“以后怎么改进”的追问,也能给后续迭代留下空间。
如果时间允许,我建议把“WebSocket实时推送推荐结果”作为亮点功能做一个demo——用户行为一变,前端立刻刷新推荐列表,这在演示时是很抓人眼球的效果。实现路径其实不复杂:Django Channels加WebSocket层,前端JS的WebSocket监听端口变化,推荐结果从Redis拉取。把这套实现写进论文的“系统实现”部分,立刻和普通增删改查毕设拉开了差距。
整体看,这套项目的工程量在毕设中属于“中等偏上”的位置,既不会让你累到脱发,又不会让你香答辩时觉得拿不出手。K-means算法本身不复杂,真正投入时间的是数据整理、特征工程、多轮调试和论文组织。但这些工作的每一项都是能写进简历、敢于在面试时讲清楚的实战能力。
最后说一点真情实感。一开始做这项目时,我也觉得推荐系统溢价太严重,仿佛人工智能都能轻松搞定。但真正把每个模块一个个做起来,才意识到毕业设计和企业项目的差距在“完整”而不是“炫技”。一个数据能跑、代码能运行、逻辑自洽的项目,比一个PPT里塞满高大上词汇但跑不出结果的项目值钱得多。这套Django+K-means的校园美食推荐系统,适合第一次完整做项目的学生,也适合快速搭建一个可演示系统的开发者,希望这份实战记录能帮到你。