尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于协同过滤算法的美食推荐系统设计与实现

基于协同过滤算法的美食推荐系统设计与实现 简介这是一份面向计算机专业本科生的毕业设计与课程作业参考资源聚焦基于Python实现的美食推荐系统核心解决冷启动、用户偏好建模与个性化精准推荐问题。资源包含完整可运行系统含前后端、配套论文与答辩PPT覆盖用户画像构建、多源行为数据采集、User-based/Item-based协同过滤算法实现及混合推荐策略设计。压缩包共611个文件25.57MB以49个Python脚本含算法核心逻辑与数据预处理、108个Vue前端组件、159个SVG图标、57个JPG/PNG图片及多个批处理脚本如运行.bat、init_sql.bat为主结构清晰支持一键部署与本地调试。已有147人学习下载读者可直接复用代码框架、参考论文写作范式、借鉴管理员审核机制与饮食禁忌过滤等业务细节并通过SQL初始化脚本与备份文件快速搭建测试环境。 毕业设计年年都有大批人选“推荐系统”方向但真正能把一个推荐系统讲清楚、做出来、还能写进论文里的其实不多。今天聊的这个项目——基于协同过滤算法的美食推荐系统的设计与实现就是一个典型的“看着不难做完能学到很多东西”的题目。它不只是一个简单的Web应用里面涉及了协同过滤算法的原理落地、Python数据处理、推荐引擎设计、论文撰写和PPT答辩准备是一个完整的、可以作为计算机专业毕业设计或课程设计参考的项目。这个项目的核心价值在于它把抽象的推荐算法放到了一个生活化场景——美食推荐里。用户对菜品打分、浏览、收藏系统根据这些行为数据用协同过滤算法找出“和你口味相似的人”或者“和你喜欢的菜相似的其他菜”从而给出个性化推荐。适合正在准备毕业设计的学生、想入门推荐系统的开发者以及需要一个完整项目练手的朋友参考。我会把整个项目从需求拆解、算法原理、系统设计、代码实现到论文写作、答辩PPT的路数都捋一遍重点讲清楚每个环节“为什么这样做”。1. 项目需求拆解与方案选型1.1 毕设选题要解决的核心问题做美食推荐系统首先要搞清楚你要解决什么问题。表面上看是“给用户推荐好吃的”但落到系统层面要拆成三个具体问题第一数据从哪来。推荐系统是数据驱动的东西没有用户行为数据算法就是空中楼阁。常见做法是使用公开数据集比如Yelp的部分数据或者自己写脚本生成模拟数据——模拟一批用户给一批菜品打分构成用户-菜品评分矩阵。我刚做的时候图省事直接用Python的random库生成了500个用户对1000道菜品的评分数据虽然看起来有点“假”但用来验证算法效果完全够用。第二怎么算“相似”。协同过滤的两大分支——基于用户的协同过滤UserCF和基于物品的协同过滤ItemCF——核心都是计算相似度。前者算的是用户与用户之间的口味相似度后者算的是菜品与菜品之间的被购买/评分模式相似度。第三怎么把结果展示出来。推荐结果算出来了总要有个界面给用户看。这部分用Flask搭一个简单的Web应用就行推荐结果通过后端接口传给前端页面展示。如果不做Web界面做一套命令行演示程序也能说明问题但作为毕设有一个可视化界面答辩时演示效果会好很多。这三个问题拆清楚了项目的技术路线也就清晰了Python做算法和数据处理Flask做Web服务SQLite或MySQL存数据论文部分围绕“协同过滤在美食推荐场景的应用”展开。1.2 为什么选协同过滤而不是深度学习现在推荐系统领域深度学习模型比如DeepFM、DIN确实很火但毕业设计选协同过滤不是因为学不会深度学习而是因为协同过滤在美食推荐这个场景下更合适、也更能把原理讲透。美食推荐和电商推荐有个很大的区别用户对菜品的偏好相对稳定。“爱吃辣的人就是爱吃辣喜欢清淡的人就是喜欢清淡”这个口味特征是长周期稳定的。协同过滤恰好擅长捕捉这种稳定的偏好模式——它不关心菜品的具体属性食材、烹饪方式只从用户的历史行为出发找到相似用户或相似物品就能给出不错的推荐。另外毕业设计有一个隐形要求要让评委老师能在十分钟内听懂你的核心算法。协同过滤的思想非常直观——“物以类聚人以群分”用一句话就能解释清楚。而深度学习模型需要讲清楚网络结构、损失函数、训练过程答辩时很容易陷入细节讲不透反而扣分。当然如果你学有余力可以在协同过滤的基础上做一个改进——比如引入菜品类别信息作为辅助特征或者在评分预测时结合时间衰减因子给近期行为更高的权重。这种“经典算法小创新”的路线在毕设里是很讨巧的既保证了工作量又有可讲的理论深度。1.3 技术栈选型Python生态是这类项目的舒适区Python在这个项目里几乎是唯一理性选择原因很直接数据处理方便。pandas处理评分数据、计算相似度矩阵代码量比Java/C少一个数量级。算法库成熟。scikit-learn里直接封装了cosine_similarity、pearsonr等相似度计算方法也可以自己手写几十行就搞定。Web框架轻量。Flask写一个推荐结果展示页面前后端加起来200行代码。论文相关图表好生成。matplotlib画准确率、召回率对比图论文里直接能用。这里多说一句有些同学会纠结“算法是不是自己手写的”这个问题。我的建议是核心的协同过滤逻辑相似度计算、评分预测一定自己写不要直接调surprise库的现成算法。答辩时老师大概率会问“你讲一下协同过滤的实现细节”如果只是调包答不上来就很尴尬。自己写一遍不仅对算法理解更深论文里的“核心代码”部分也有东西可写。2. 协同过滤算法原理解读两种思路一个目标2.1 基于用户的协同过滤UserCF找口味相同的人UserCF的核心思想是如果你和某个用户对很多菜品的打分都很接近那TA喜欢而你还没吃过的菜你有很大概率也会喜欢。具体分三步第一步构建用户-菜品评分矩阵。行是用户列是菜品格子里是评分可以用1-5分0表示未评分。第二步计算用户之间的相似度。以两个用户共同评分过的菜品为样本计算他们评分的相似度。最常用的是余弦相似度similarity(u, v) sum(r_ui * r_vi) / (sqrt(sum(r_ui^2)) * sqrt(sum(r_vi^2)))其中r_ui是用户u对物品i的评分。分子是两个用户对所有共同评分物品的乘积求和分母是各自评分向量的模的乘积。第三步预测评分并推荐。对于用户u没吃过的菜品i用与u最相似的K个用户的评分加权平均算出u对i的预测评分取分数最高的N个菜品推荐给u。UserCF在美食推荐场景里有个天然优势它推荐的结果“有惊喜”。因为它找的是和你口味相似的人而这个人可能会吃一些你从来没尝试过但很可能喜欢的菜——这比单纯推荐同品类菜品更有新鲜感。2.2 基于物品的协同过滤ItemCF找相似的菜ItemCF的思路和UserCF正好反过来如果一个用户同时喜欢宫保鸡丁和鱼香肉丝那说明这两道菜在被喜欢这件事上是相似的下次可以给其他喜欢吃宫保鸡丁的用户推荐鱼香肉丝。它的计算步骤是先根据所有用户的行为构建菜品-菜品相似度矩阵然后对用户历史喜欢的菜品找出与它们最相似的菜品生成推荐候选集。ItemCF在电商领域是绝对主流“买了A的人还买了B”因为物品相似度矩阵可以离线计算好线上推荐时响应速度很快。但在美食推荐场景里ItemCF有一个明显的局限推荐结果偏“同质化”——它倾向于推荐和用户吃过的菜相似的菜品不太擅长跨品类推荐。比如用户历史记录里全是川菜ItemCF推荐出来的大概率还是川菜。2.3 相似度计算方法的选型与对比做这个项目时相似度计算有几种选择我对比后最终选了余弦相似度但这里给你把账算清楚方法计算公式简化适用场景注意事项余弦相似度两个评分向量的夹角余弦评分数据稀疏矩阵没考虑用户评分尺度的差异皮尔逊相关系数对评分做中心化后再算余弦用户评分尺度不一时更好需要至少两个共同评分项Jaccard相似度共同评分物品数 / 并集物品数只关注是否评过分不考虑分数忽略评分数值信息利用不充分我最终用余弦相似度主要是实现简单、计算快而且在这个项目里数据集是模拟的用户评分尺度差异不大余弦相似度表现足够好。做皮尔逊的话代码上只是多一步中心化处理但论文里可以加一段对比实验说“在冷启动场景下皮尔逊更稳定”内容就更丰富了。2.4 评分预测公式与Top-N推荐相似度算完最重要的是预测用户对未评分菜品的评分。这里我用的加权平均法pred(u, i) sum(sim(u, v) * r_vi) / sum(sim(u, v))注意分母是相似度之和分子是相似度乘以对方用户对菜品i的评分。如果某个菜品没有任何相似用户评过分预测值就为空这种情况下就不推荐这个菜品。Top-N推荐就是取预测评分最高的N个菜品。N的取值通常为5-10我项目中设置的默认值就是10可以在前端页面让用户自己调整。3. 系统设计与数据准备3.1 数据获取策略公开数据集与模拟数据组合做这个项目的第一个坑就是数据。真实的餐饮点评数据很难拿到我推荐两种方案方案一用公开数据集。Yelp Dataset Challenge提供过包含餐厅信息和用户评分的数据子集学术评估里很常用。Kaggle上也能找到一些美食评分数据。优点是真实、有说服力缺点是数据清洗工作量比较大——餐厅类别、地址、评分格式都不太规整。方案二自己写脚本造模拟数据我实际采用的是这个方案。生成逻辑是先定义50个用户口味标签比如“重辣爱好者”“养生派”“甜品控”再定义菜单和菜品口味标签“辣”“甜”“清淡”“油炸”等然后根据用户口味和菜品标签的匹配程度生成评分匹配度高的给4-5分低的给1-2分中间随机浮动。这样造出来的数据有一个额外好处——算法应该能挖掘出“重辣爱好者”之间的相似性方便验证算法逻辑是否正确。3.2 数据库表结构设计我是用SQLite存储数据的两张核心表就够了表名字段说明usersid, username, password用户基本信息dishesid, name, category, taste_tags, price菜品信息ratingsid, user_id, dish_id, score, timestamp用户评分记录如果要加分可以再加一张behavior_log表记录用户的点击、收藏行为这样论文里可以写“本系统融合了显式反馈和隐式反馈”。但作为基础版评分表已经能支撑协同过滤算法跑通了。有一点要注意rating表里user_id和dish_id要建立联合索引后面算相似度时要频繁按用户或按菜品做分组查询没有索引数据量大了会慢。3.3 系统整体架构与模块划分整个系统我分成三个模块数据层SQLite数据库存储用户、菜品、评分数据。算法层协同过滤推荐引擎负责相似度计算、评分预测、Top-N推荐。表现层Flask Web应用提供用户登录、菜品浏览、评分提交、推荐展示页面。这三个模块的划分论文里正好对应“系统总体设计”“系统详细设计”“系统实现与测试”三章结构非常清晰。你写论文时不用绞尽脑汁想章节怎么排照着代码模块划分来写就行。4. 核心推荐引擎代码实现4.1 数据加载与预处理这一段是后面所有计算的基础。用pandas读数据库构建用户-菜品评分矩阵import pandas as pd import numpy as np from sklearn.metrics.pairwise import cosine_similarity def load_data(): conn sqlite3.connect(food_recsys.db) ratings_df pd.read_sql_query(SELECT user_id, dish_id, score FROM ratings, conn) dishes_df pd.read_sql_query(SELECT id, name, category FROM dishes, conn) conn.close() return ratings_df, dishes_df def build_rating_matrix(ratings_df): # 行是用户列是菜品未评分的用0填充 rating_matrix ratings_df.pivot_table( indexuser_id, columnsdish_id, valuesscore ).fillna(0) return rating_matrix这里填0的代价是评分矩阵会非常稀疏大部分用户只对少数菜品评分过但余弦相似度计算不受影响因为0不会对分子分母产生贡献。需要说明的是这和“用户没吃过的菜评分是0”是两回事——这里的0只表示“没有评分记录”不作为真实的低分参与计算。4.2 用户相似度矩阵计算构建好评分矩阵后计算用户之间的余弦相似度def compute_user_similarity(rating_matrix): user_sim cosine_similarity(rating_matrix) user_sim_df pd.DataFrame( user_sim, indexrating_matrix.index, columnsrating_matrix.index ) return user_sim_df这里直接用sklearn的cosine_similarity它内部会自动对两两行向量做余弦相似度计算比自己写for循环快很多而且不容易出错。但为了论文里能讲清楚我建议你在“系统详细设计”这一章里把原始的公式和手写计算逻辑都写出来代码部分再用sklearn的实现这样既有理论深度又有工程效率。手写一个用户相似度也很快这里给个参考def manual_cosine_similarity(vec_a, vec_b): dot np.dot(vec_a, vec_b) norm_a np.linalg.norm(vec_a) norm_b np.linalg.norm(vec_b) if norm_a 0 or norm_b 0: return 0 return dot / (norm_a * norm_b)这里有个容易踩的坑如果两个用户没有共同评分过的菜品余弦相似度应该直接视为0而不是硬算一个结果出来。因为两个全是0的向量norm是0除法会报错。4.3 推荐函数预测评分与Top-N生成推荐函数是整个系统的心脏def predict_for_user(user_id, rating_matrix, user_sim_df, top_k10): # 1. 找到当前用户最相似的K个用户 if user_id not in user_sim_df.index: return [] sim_scores user_sim_df[user_id].drop(indexuser_id).sort_values(ascendingFalse) top_similar_users sim_scores.head(top_k) # 2. 找出当前用户未评分的菜品 user_rated rating_matrix.loc[user_id] unrated_dishes user_rated[user_rated 0].index predictions [] for dish_id in unrated_dishes: numerator 0 denominator 0 for sim_user, sim_score in top_similar_users.items(): r_user_dish rating_matrix.loc[sim_user, dish_id] if r_user_dish 0: numerator sim_score * r_user_dish denominator sim_score if denominator 0: pred_score numerator / denominator predictions.append((dish_id, pred_score)) # 3. 按预测分数排序取前N个 predictions.sort(keylambda x: x[1], reverseTrue) return predictions[:top_k]这段代码的逻辑很直白先找相似用户再看这些相似用户评过什么菜而当前用户没吃过最后加权平均算预测分。需要注意的是drop(indexuser_id)这一步是把用户自己排除掉否则自己和自己相似度永远是1会影响推荐结果。4.4 冷启动问题与简化处理策略冷启动是协同过滤的原生缺陷新用户没有任何评分行为系统无法为他计算相似用户也就无法推荐。我在项目里做了一个简单的兜底方案新注册用户在未产生任何评分行为之前系统返回热门菜品Top10即全站评分人数最多的10道菜附带一个“大家都在吃”的推荐理由。当新用户对菜品产生3条以上评分后系统自动切换为协同过滤推荐。这个方案虽然技术上不新颖但合理且有说服力论文里可以专门写一小节“冷启动问题的分析与处理”答辩时就能体现你有考虑到实际落地的难点。5. 论文与PPT的核心结构安排5.1 毕业论文的章节划分与写作技巧论文部分我推荐的章节安排是这样的章节内容要点写作建议第一章 绪论研究背景、国内外研究现状、研究内容背景从“信息过载”切入研究现状写协同过滤算法的演进第二章 相关技术介绍Python、Flask、协同过滤算法、SQLite每个技术点写清楚“为什么选它”别写成百度百科第三章 系统需求分析功能性需求、非功能性需求用用例图配合文字说明注意不要只贴图第四章 系统设计总体架构、数据库设计、算法设计算法设计是重点写清楚UserCF的流程和公式第五章 系统实现各模块功能实现、核心代码展示核心代码要配合解释代码和文字比例控制在1:2左右第六章 系统测试功能测试、算法效果评估测试用例表格化算法评估用准确率、召回率、覆盖率这里有个很重要的写作技巧论文里的代码不要贴大段大段的无注释代码贴核心代码片段并配文字解释就够了。评阅老师主要看设计思路和逻辑是否清晰不是来数你代码行数的。5.2 答辩PPT制作要点7分钟讲完一个系统答辩PPT我的经验是控制在12-15页讲的时间控制在7分钟左右。这7分钟怎么分配是最关键的前30秒一句话介绍系统是什么“这是一个基于协同过滤算法的美食推荐系统核心功能是给用户提供个性化菜品推荐”。2分钟讲研究背景和意义重点是“信息过载”和“个性化推荐”这两个关键词不要铺开讲太多。3分钟讲系统设计和核心技术包括总体架构图、协同过滤算法原理和公式。这里放一张用户评分矩阵的示意图评委能快速理解。1.5分钟系统演示提前录好操作视频最稳妥现场演示容易翻车比如数据库没启动、网络不通。最后30秒总结创新点以及下一步改进方向。PPT制作的原则是能用图不用表能用表不用文字。算法流程图、系统架构图、数据库ER图每个都值得做一张好看的图。5.3 图表与实验数据的呈现技巧毕设论文中的图表有一个通用标准图要能自解释。也就是说读者不看正文只看图的标题和标注就能看懂大概意思。在“系统测试”这一章我做了一张推荐效果分析的图表——横向对比了不同相似用户数K取值下K5, 10, 15, 20推荐准确率的变化。这样一张折线图既展示了实验过程又能引出“K值选择对推荐质量的影响”讨论。图表数据怎么来在代码里写一段评估函数从测试集中随机抽出一部分评分记录作为验证集看看推荐结果中有多少是用户实际喜欢的菜品。这部分工作量不大但论文档次会明显上一个大台阶。6. 常见问题与排查技巧实录6.1 推荐结果不准确全是零分物品这个现象我调试时遇到过。一开始发现给用户推荐的菜品预测评分全是0查了半天问题是出在评分矩阵的预处理上。当时的评分矩阵没有评分的格子填充的是NaN然后用fillna(0)填充为0这步没问题。但计算余弦相似度之后0值用户对相似度计算产生了干扰——某一个菜品对所有用户都是0它就会被认为是“所有用户都不喜欢”但其实是“所有用户都没吃过”。解决办法是在相似度计算时只考虑两个用户都有评分记录的菜品。scikit-learn的cosine_similarity默认会对所有元素求点积所以对稀疏矩阵直接套用是有问题的。更好的做法是先把矩阵转为稀疏矩阵格式如scipy.sparse或者自己实现“只看共同评分项”的相似度逻辑。6.2 推荐结果太“大众化”个性化不足UserCF的一个通病是推荐结果偏向热门物品——因为热门物品被相似用户评分的概率大预测分自然高。这个问题在美食推荐场景尤其突出系统推荐的永远是火锅、烧烤、小炒肉这类大众菜用户自己的小众偏好比如喜欢陕西的浆水鱼鱼完全体现不出来。改进方案有很多最实用的一种是对预测分数做“流行度惩罚”在排序时用预测评分减去该物品流行度系数比如该物品被评分的总次数乘以一个很小的惩罚因子。这样既能保留热门菜的推荐又给相对小众但匹配用户口味的菜露出机会。这个思路在论文里可以写成“基于流行度惩罚的推荐结果优化”作为算法的改进点。6.3 Web界面展示时中文乱码这是Python Web项目的经典坑。Flask返回的JSON里有中文前端页面显示是乱码。解决方法是确认Python文件是UTF-8编码在文件头部加# -*- coding: utf-8 -*-。Flask返回JSON时设置ensure_asciiFalse否则中文会被转成ASCII编码。前端HTML页面的head里加meta charsetUTF-8。这些看起来是小问题但课上演示时如果出现乱码会直接影响答辩的观感。6.4 数据量大时计算明显变慢模拟数据量到了一定规模比如1万用户对5000道菜打分用户-菜品矩阵就是1万行×5000列计算用户相似度矩阵需要循环1万×1万次普通的pandas DataFrame操作会卡到怀疑人生。解决思路有三个层级用scipy.sparse稀疏矩阵存储降低内存占用。利用numpy向量化计算替代for循环。相似度计算放到离线定时任务里跑推荐结果存到Redis/内存缓存Web端只做查询不做实时计算。毕业设计阶段做到第一层就够用了但论文的“系统优化”章节里可以提一下后两层的设计思路展示你对生产环境性能问题的理解。我自己的体会是做毕设不要只盯着“把功能跑出来”还要想“怎么把项目讲出一朵花来”。同样一个美食推荐系统有的人答辩只能说“我用Python做了一个推荐系统”但你把冷启动处理、流行度惩罚、K值对推荐效果的影响这些细节都做了、都写了这项目的层次就完全不一样了。这也是我在这个项目里收获最大的一点——真正拉开差距的不是技术多牛而是对一个工程问题思考得够不够完整、能不能把每个决策背后的理由讲清楚。本文还有配套的精品资源点击获取
返回列表