ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

美食推荐系统毕业设计:协同过滤原理与Python实战

美食推荐系统毕业设计:协同过滤原理与Python实战 简介互联网时代的信息过载让推荐系统成为电商、外卖、内容平台的关键技术而协同过滤则是其中应用最广、最容易落地的经典算法。它不需要昂贵的GPU和海量数据集仅依靠用户行为数据通过余弦相似度、皮尔逊相关系数等方法计算用户或物品之间的相似关系就能生成个性化的Top-N推荐列表。这一技术价值在于兼顾效果与可解释性尤其适合美食、电影、图书等兴趣相对稳定的垂直场景。以美食推荐系统毕业设计为例可将协同过滤算法与Python、Flask、MySQL结合完成从评分数据清洗、相似度矩阵构建、UserCF/ItemCF算法实现到推荐接口开发的全流程并针对冷启动和数据稀疏问题给出工程化解决方案。这套设计思路同样可迁移到其他物品推荐项目是理解推荐系统原理与工程实践的理想切入点。1. 项目整体设计与核心思路1.1 为什么选“美食推荐系统”作为课题如果你是计算机、软件工程或者大数据相关专业的学生应该能感觉到毕业设计选题这件事有多关键。题目太简单答辩的时候导师几个问题就把你问住了题目太难开发周期拖到天荒地老论文还憋不出字。美食推荐系统这个题我觉得是性价比非常高的一类选择。先说行业背景。推荐系统在电商、短视频、外卖平台里已经是标配技术了比如你打开外卖软件会看到“猜你喜欢”打开菜谱App会看到“今日推荐”这些都是推荐算法在背后工作。把协同过滤算法落到美食场景既有真实的应用价值又有清晰可拆解的技术点用来做毕业设计或课程项目素材非常充足。再说技术层面。协同过滤Collaborative Filtering是推荐系统里最经典的算法家族它不像深度学习方法那样需要昂贵的GPU和超大数据集只要有一台普通笔记本、一份像样的用户评分数据就能跑出效果明显的推荐结果。这对本科生或刚入门的研究生来说非常友好。1.2 一个系统拆开看推荐引擎只是核心不是全部我见过很多同学做这类系统上来就闷头写算法结果算法写完发现系统交互、数据库、可视化、论文图表全都没有着落。真正完整的“美食推荐系统”应该包含三层。第一层是数据层。用户信息、菜品信息、用户对菜品的评分行为这些数据要能存下来、查得快、方便做分析。第二层是算法层也就是协同过滤推荐引擎接收用户的评分历史输出Top-N推荐列表。第三层是应用层用户通过网页或桌面端的界面注册登录、浏览菜品、给菜品打分然后看到一个“为你推荐”的列表。三层都打通这个系统的完整度才算合格。而且这三层恰好对应毕业论文里的几个核心章节需求分析、系统设计、核心算法实现、系统测试。也就是说你每做完一层论文就多出一章素材不会出现“算法做得挺好但论文凑不够字数”的尴尬。还有一点值得注意这套系统的设计思路可以通用。今天做美食明天把数据换成电影、图书、音乐算法逻辑不需要大改。这也是我在答辩时比较喜欢强调的点——项目的扩展性和方法论价值这比单纯说“我实现了一个算法”要有说服力得多。1.3 技术栈选型别为了炫技选不熟悉的东西我整理过一份做这类系统的主流技术方案你可以根据自己的熟悉程度来选。层次推荐方案A主流推荐方案B轻量说明语言Python 3.8Python 3.8算法生态最成熟pandas/numpy/scikit-learn齐全Web框架FlaskDjangoFlask轻量好上手适合单体小系统Django自带Admin后台管理方便数据库MySQLSQLite开发调试用SQLite省事写论文建议用MySQL体现“企业级”前端Bootstrap jQuery原生HTML CSS不追求复杂交互的话Bootstrap最快出效果算法实现手动实现 scikit-learn验证仅手动实现手动实现能写在论文里sklearn用于交叉验证结果个人建议如果时间紧Flask SQLite Bootstrap 手写协同过滤这个组合最稳。Python的安装和环境配置是个老话题常见坑包括环境变量没配好、pip下载慢、numpy版本冲突等直接用Anaconda或者Python官方安装包装好再用pip install -i https://pypi.tuna.tsinghua.edu.cn/simple flask pandas numpy这类国内镜像源装依赖能把时间省下一大截。提示尽量别选你不熟悉的框架。毕业设计的核心是把推荐原理讲清楚、系统能跑起来不是展示你会多少冷门技术。2. 协同过滤算法核心原理解析2.1 两种路线UserCF与ItemCF协同过滤的核心思想一句话就能说透跟你相似的人喜欢吃的东西你大概率也喜欢你以前喜欢的东西的相似品你大概率也会喜欢。前一句话对应基于用户的协同过滤UserCF后一句话对应基于物品的协同过滤ItemCF。UserCF的步骤是第一步计算用户之间的相似度找到当前用户的“邻居”第二步把邻居们评分高但当前用户没吃过的菜品聚合起来按预测评分排序输出推荐列表。ItemCF的步骤是第一步计算菜品之间的相似度注意这个相似度是基于用户行为算的不是基于菜品的原料、口味等属性第二步根据用户历史评分过的菜品找出相似的菜品按预测评分排序输出推荐。两个路线各有适合的场景。UserCF更偏“社交化”适合新闻、社区这类用户兴趣变化快的场景ItemCF更偏“个性化”适合图书、电影、美食这类兴趣相对稳定的场景。做美食推荐系统我在实际开发中其实更推荐ItemCF作为主力算法原因后面细说。2.2 相似度计算的三种常用方法无论UserCF还是ItemCF都绕不开“相似度计算”这个核心步骤。常用的有三种方法。余弦相似度是最容易理解的把用户A的评分向量和用户B的评分向量看作高维空间里的两个向量计算它们夹角的余弦值。夹角越小余弦值越接近1说明两个人越相似。计算公式是cos(θ) (A·B) / (|A| × |B|)在Python里用numpy实现只需要几行代码import numpy as np def 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)皮尔逊相关系数是余弦相似度的升级版它对评分做了“中心化”处理也就是减去每个用户的平均打分。这样做的好处是如果一个用户普遍打分偏高给什么都给4分另一个用户打分偏低喜欢才给3分直接用余弦相似度会误判他们口味不同而皮尔逊相关系数能消除这种评分尺度差异。公式是r Σ(A_i - avgA)(B_i - avgB) / sqrt(Σ(A_i - avgA)^2 × Σ(B_i - avgB)^2)修正余弦相似度是ItemCF里常用的变体用来消除不同用户打分习惯对物品相似度的影响。它的做法是计算物品相似度时先把每个用户的评分减去该用户的平均分再套用余弦相似度公式。这三种方法的选择逻辑我建议这么定数据稀疏、评分尺度差异大优先皮尔逊评分数据相对稠密、想快速看到效果用余弦ItemCF场景直接上修正余弦。2.3 评分预测最常用的加权求和公式算完相似度之后下一步是预测用户对未吃过菜品的评分。这里最常用的方法是“加权求和”公式长这样pred(u, i) Σ(sim(u, v) × r(v, i)) / Σ(sim(u, v))意思是用户u对菜品i的预测评分等于“与u最相似的K个用户v对菜品i的评分”的加权平均权重就是u和v的相似度。分母做归一化防止不同K取值导致分数范围不稳定。ItemCF的预测公式稍微有点不同它的加权对象是“用户u评过分的物品与目标物品i的相似度”pred(u, i) Σ(sim(i, j) × r(u, j)) / Σ(sim(i, j))在实际写代码的时候这个公式更适合用一个预计算好的“物品相似度矩阵”来查表而不是每次请求都现场算一遍。把相似度矩阵提前算好存下来推荐接口的响应速度能从秒级降到毫秒级这个优化在论文的“系统性能优化”章节里是很好的加分项。3. 数据方案与预处理实战3.1 评分数据从哪来别在爬虫上栽跟头开发推荐系统最理想的数据集是像MovieLens那样公开的评分数据但美食领域公开数据集相对少。常见的解决方案有两条路。第一条路是自己造数据。让宿舍同学、网友帮忙填一批评分比如20个用户对50道菜品的打分评分范围1到5。数据量虽小但能跑通全链路而且你可以清楚知道每一行数据的来龙去脉。对毕业设计来说数据量不是问题算法的完整性和推理过程才是重点。第二条路是用爬虫抓公开数据。这个方向要特别谨慎抓取网站数据要遵守目标网站的robots协议和用户条款只抓合法允许的公开信息做个人学习研究不得用于任何商业用途。我个人的建议是优先找公开的菜谱数据集比如一些开源社区整理的带评分字段的菜品数据或者自己构造别把精力耗在爬虫和反爬对抗上那对本课题的核心目标帮助不大。注意论文里写数据处理流程时一定要说明数据来源和预处理规则。答辩老师很可能会问“你的数据量这么小推荐结果有说服力吗”这时候你要回答的是算法流程的合理性和评估指标的设计而不是夸大数据的规模。3.2 核心数据表设计用户、菜品、评分设计数据库表的时候我习惯遵循“常规字段 推荐专用字段”的思维。用户表user的常规字段包括user_id、username、password、register_time。如果想给系统加一点个性化可以加food_preference字段存用户偏好的口味比如“辣、清淡、甜”之类的标签。但要注意协同过滤本身不需要这些属性字段加上它只是为了在冷启动场景用户还没有评分历史时有东西可用。菜品表dish的常规字段包括dish_id、dish_name、category分类如川菜、粤菜、甜品、price、description、image_url。你可以额外加一个avg_rating字段用来存菜品平均分。这个字段有两个用处一是新用户冷启动时可以按平均分推荐二是做排行榜功能时直接用SQL排序不用现场算。评分表rating是最核心的一张表字段包括rating_id、user_id、dish_id、score、rating_time。这张表一定要给(user_id, dish_id)建联合唯一索引防止同一个用户对同一道菜重复评分。每次写入评分时顺便同步更新dish表的avg_rating字段这样排行榜和推荐候选池的数据始终是最新的。3.3 数据清洗稀疏矩阵的坑与应对协同过滤的数据清洗和普通Web项目不太一样关键在“评分矩阵”的构建。假设有20个用户、50道菜全量评分是20×501000个评分。但现实里每个用户可能只评了10道菜那矩阵里就有800个空位稀疏度达到了80%。直接用这个矩阵算相似度结果会非常不稳定。我的处理顺序是第一步过滤掉评分数量少于5条的用户这些人提供的信息太少算出的相似度没有统计意义第二步过滤掉被评分次数少于3次的菜品这些菜属于长尾中的长尾推荐出来用户也没听说过第三步补全空缺值常用两种策略填0适合余弦相似度或填用户平均分适合皮尔逊系数。在Python里构建评分矩阵用pandas的pivot_table可以一行搞定import pandas as pd # 假设rating_df是原始评分表字段为user_id, dish_id, score rating_matrix rating_df.pivot_table( indexuser_id, columnsdish_id, valuesscore ).fillna(0) # 打印矩阵形状确认行列对不对 print(rating_matrix.shape)这个矩阵的行是用户列是菜品值就是评分。后面计算相似度的时候每次取一行就是一个用户的评分向量非常方便。我在实际项目里习惯把这步处理封装成build_rating_matrix(user_rating_df)这样的函数输入原始评分表输出可直接用于相似度计算的矩阵保持代码结构清晰。4. 推荐引擎完整实现从算法到接口4.1 工具函数先算好相似度矩阵在写推荐函数之前先把相似度矩阵准备好。我习惯用一个单独的函数来算UserCF的用户相似度矩阵再用另一个函数算ItemCF的物品相似度矩阵。用户相似度矩阵的实现思路对每一个用户i遍历所有用户j计算i和j的皮尔逊相关系数。数据规模不大时双重循环是没问题的。20个用户算下来最多400次相似度计算毫秒级完成。import pandas as pd import numpy as np def calculate_user_similarity(rating_matrix): 计算用户之间的皮尔逊相似度矩阵 rating_matrix: DataFrame行是用户列是菜品 返回: 相似度DataFrame行和列都是用户id users rating_matrix.index.tolist() sim_matrix pd.DataFrame(0, indexusers, columnsusers) for u in users: for v in users: if u v: sim_matrix.loc[u, v] 1.0 continue # 找出两个用户都评过分的菜品 u_ratings rating_matrix.loc[u] v_ratings rating_matrix.loc[v] # 只取双方评分都 0 的项 common_mask (u_ratings 0) (v_ratings 0) common_items u_ratings[common_mask] if len(common_items) 2: sim_matrix.loc[u, v] 0 continue # 皮尔逊相关系数 corr np.corrcoef( u_ratings[common_mask].values, v_ratings[common_mask].values )[0, 1] if np.isnan(corr): corr 0 sim_matrix.loc[u, v] round(corr, 4) return sim_matrix这段代码里有两个细节值得注意一是common_items 2时直接置0这是为了避免“两个用户只共同评过一道菜系数恰好算出来是1”这种偶然情况二是np.corrcoef可能返回NaN原因是共同评分项的方差为0比如两个用户共同评分都是5分要把它处理成0否则会影响后续计算。4.2 基于用户的协同过滤推荐有了相似度矩阵UserCF的推荐函数就顺理成章了。核心逻辑是找到和目标用户最相似的K个用户K通常取5到10把这K个用户评分高且目标用户没吃过的菜品找出来按相似度加权算预测分返回Top-N。def recommend_by_user_cf(user_id, rating_matrix, sim_matrix, top_n10, k5): 基于用户的协同过滤推荐 user_id: 目标用户 rating_matrix: 用户-菜品评分矩阵 sim_matrix: 用户相似度矩阵 top_n: 返回推荐菜品数量 k: 邻居数量 if user_id not in rating_matrix.index: return [] # 目标用户已评分的菜品 rated set(rating_matrix.loc[user_id][rating_matrix.loc[user_id] 0].index) # 获取相似用户列表排除自己 sim_scores sim_matrix.loc[user_id].sort_values(ascendingFalse) # 只看相似度大于0的邻居 neighbors [(uid, score) for uid, score in sim_scores.items() if uid ! user_id and score 0] neighbors neighbors[:k] if not neighbors: return [] # 候选菜品集合 candidate_scores {} candidate_weight {} for neighbor_id, sim_score in neighbors: neighbor_ratings rating_matrix.loc[neighbor_id] # 邻居评分过的菜品 neighbor_rated_items neighbor_ratings[neighbor_ratings 0] for dish_id, score in neighbor_rated_items.items(): if dish_id in rated: continue # 用户已经吃过了不推荐 candidate_scores[dish_id] candidate_scores.get(dish_id, 0) sim_score * score candidate_weight[dish_id] candidate_weight.get(dish_id, 0) sim_score # 归一化计算最终预测分 predictions {} for dish_id, total_score in candidate_scores.items(): if candidate_weight[dish_id] 0: predictions[dish_id] total_score / candidate_weight[dish_id] # 按预测分从高到低排序 sorted_predictions sorted(predictions.items(), keylambda x: x[1], reverseTrue) return sorted_predictions[:top_n]这里的加权公式用了“归一化”核心原因是避免相似度值大的邻居带偏结果。你设想一下如果用户A只和一个人特别像这个人给某道菜打了5分A对这道菜的预测分就是5分但如果有三个不那么像的人也打了4分平均下来才是更稳的结果。归一化恰好能平衡这个偏差。4.3 基于物品的协同过滤推荐ItemCF的推荐逻辑有点不同。它的核心不是“看邻居吃了什么”而是“看你吃过的菜与什么菜相似”。def calculate_item_similarity(rating_matrix): 计算物品之间的修正余弦相似度矩阵 items rating_matrix.columns.tolist() sim_matrix pd.DataFrame(0, indexitems, columnsitems) # 物品的评分向量 item_vectors {} # 对每个菜品计算被用户评分的平均分修正 for item in items: item_rating rating_matrix[item] # 对每个用户做中心化处理 centered [] user_ids [] for uid in rating_matrix.index: score item_rating[uid] if score 0: # 该用户所有评分的平均值 user_avg rating_matrix.loc[uid][rating_matrix.loc[uid] 0].mean() if user_avg user_avg: # 检查非NaN centered.append(score - user_avg) user_ids.append(uid) item_vectors[item] (np.array(centered), user_ids) for i, item_i in enumerate(items): for j, item_j in enumerate(items): if i j: sim_matrix.loc[item_i, item_j] 1.0 continue # 找出同时评分过物品i和物品j的用户 vec_i, users_i item_vectors[item_i] # 构建用户到评分的映射 map_i dict(zip(users_i, vec_i)) common [map_i[u] for u in users_i if u in dict(zip(*item_vectors[item_j][::-1]))] # 实际项目中更简洁的写法 set_i set(users_i) set_j set(item_vectors[item_j][1]) common_users set_i set_j common_i [] common_j [] for uid in common_users: common_i.append(map_i[uid]) common_j.append(item_vectors[item_j][0][item_vectors[item_j][1].index(uid)]) if len(common_users) 2: sim_matrix.loc[item_i, item_j] 0 continue common_i np.array(common_i) common_j np.array(common_j) # 修正余弦相似度 denom np.sqrt(np.sum(common_i**2)) * np.sqrt(np.sum(common_j**2)) if denom 0: sim_matrix.loc[item_i, item_j] 0 continue sim np.sum(common_i * common_j) / denom sim_matrix.loc[item_i, item_j] round(sim, 4) return sim_matrix这段代码在实现时比较考验对“修正余弦”的理解。修正的核心在于先对每个用户评分做中心化减去该用户平均分再计算余弦。不这么做的话一个总是打5分的用户和一个总是打3分的用户即使口味高度一致算出来的相似度也会失真。实际开发里我通常把ItemCF的物品相似度矩阵存成Spark DataFrame或直接在MySQL里建一张表item_id_a, item_id_b, similarity因为计算一次之后就不需要频繁重算。只有新菜品加入或者用户评分发生明显变化时才做增量更新。4.4 双算法融合与冷启动策略单独用UserCF或ItemCF都可能遇到效果不稳定的情况。我最后实现的是混合推荐策略当用户的评分数量少于一定阈值时走冷启动逻辑评分数量足够时用UserCF和ItemCF各出一份推荐列表加权融合后再输出。冷启动逻辑又分三种场景。第一种是系统里完全没有评分这时候只能按菜品平均分推荐做得更好一点是按照分类做多样性推荐。第二种是用户没有任何评分但注册时填了口味偏好可以按偏好分类推荐。第三种是用户只有1到3条评分这时我倾向于直接用内容匹配比如用户评分过的菜品的同分类菜品来补充算法推荐放到评分数据积累到5条以上再介入。加权融合的公式也不复杂final_score(d) α × score_userCF(d) (1 - α) × score_itemCF(d)α的取值可以在实验里调。我在自己的数据集上测下来α取0.4左右效果最好也就是稍微偏向ItemCF。原因也简单美食推荐场景里“你以前喜欢的菜的相似菜”比“和你相似的人喜欢的菜”更稳妥因为前者的推荐理由用户更容易接受。5. 系统功能设计与论文PPT素材搭建5.1 功能模块拆解照着做不会漏功能一个完整的Web版美食推荐系统功能模块可以这样拆。用户模块负责注册、登录、个人信息管理注册时尽可能让用户选择口味偏好这是冷启动的重要数据。菜品模块负责菜品信息展示、分类浏览、关键词搜索。评分模块是核心交互用户给吃过的菜打分这个动作决定了推荐质量。推荐模块负责生成“为你推荐”列表要同时给“推荐理由”。榜单模块负责展示热门菜品、高分菜品本质上是推荐系统的兜底。这套功能拆解对应到代码结构上用Flask蓝图来组织非常合适比如一个auth_bp负责登录注册一个dish_bp负责菜品浏览一个recommend_bp负责推荐接口。每个蓝图独立成文件代码不会乱。5.2 Flask后端接口设计我整理了一份最小的接口清单照着写就能支撑整个系统前后端联动。接口路径方法功能关键参数/api/registerPOST用户注册username, password, taste/api/loginPOST用户登录username, password/api/dishesGET菜品列表分页page, category/api/dishes/idGET菜品详情无/api/ratePOST提交评分user_id, dish_id, score/api/recommend/user_idGET获取推荐列表top_n/api/hotGET热门榜单limit在实现时/api/rate这个接口稍微特殊一点它不只是往数据库里插入一条评分记录还应该同步更新菜品表的avg_rating。这个操作可以用数据库事务来保证一致性防止评分插入成功但平均分更新失败的情况。5.3 毕业论文章节怎么搭、PPT怎么提炼论文这块我建议章节结构直接按系统构建的自然顺序来写。第一章绪论写研究背景、意义、国内外研究现状、论文结构。第二章相关技术介绍写Python、Flask、协同过滤算法概述、MySQL。第三章需求分析与概要设计写功能性需求、非功能性需求、系统架构图、功能模块图。第四章详细设计与实现这部分是重点写数据库设计、每个功能模块的时序图、推荐算法的实现细节、核心代码分析。第五章系统测试写测试环境、功能测试用例表、推荐效果评估准确率、召回率、Top-N命中率、测试结果分析。第六章总结与展望。PPT的提炼逻辑和论文不同核心是“讲故事”。我的经验是首页用系统截图吸引眼球然后一页讲痛点信息过载、选择困难一页讲解决方案的宏观流程图再花三四页讲算法原理与效果对比最后用一张架构图收尾。PPT上尽量少放代码多放流程图和效果对比图那才是答辩老师会关注的。这里有个很实用的技巧答辩PPT里的算法讲解页别只放公式放一张“用户A和用户B的评分矩阵以及相似度计算过程”的手工演算小表直观到答辩老师一眼就懂。表里两行用户数据、简单的加减乘除、最终相似度0.87这段讲解会让你在答辩时的表达顺畅很多。6. 推荐效果评估与常见问题排查6.1 离线评估指标别只盯着准确率很多同学做推荐系统评估就是“看起来推荐得还挺准”这在论文里是不太够的。我建议至少做三个维度。准确率Precision的含义是推荐列表里用户实际喜欢评分≥4的比例。比如推荐了10道菜用户点了其中4道准确率就是40%。计算方法是在测试集里把用户评分行为随机划分成80%训练集和20%测试集用训练集学到的模型预测测试集里的评分行为。召回率Recall的含义是用户喜欢的菜有多少被推荐出来了。如果用户一共喜欢10道菜推荐列表覆盖了其中4道召回率就是40%。覆盖率Coverage的含义是推荐系统能推荐出的菜品占全部菜品的比例。覆盖率太低说明系统总在推荐热门菜长尾菜品永远没曝光机会这在真实场景里是个问题。覆盖率计算公式是推荐出去的不同菜品数 / 菜品总数。三个指标不能只看单一值。我在实验中发现UserCF在小数据集上准确率往往比ItemCF高一点点但覆盖率明显偏低因为它总把用户引向“大家都爱吃的热门菜”。ItemCF在覆盖率上表现好得多这也是我最终系统把ItemCF作为主力、UserCF做补充的另一个原因。6.2 冷启动与稀疏性处理方案冷启动是推荐系统里绕不开的经典问题也是答辩老师最喜欢追问的点。我的系统里用了一套分场景策略。新用户冷启动分三步走注册时收集口味偏好标签没有评分时按口味标签推荐对应分类的热门菜品有少量评分后进入混合推荐模式。新菜品冷启动的处理方式不同菜品刚上线没有评分无法参与协同过滤计算所以需要在展示上给个“新品尝鲜”入口让新菜先获得曝光和被评分的机会等积累到一定评分量再进入推荐候选池。用户冷启动的侧重点不同如果只有个别用户评分稀疏用“全局热门榜”兜底推荐如果整体数据稀疏就增加评分引导交互比如推荐列表下方提示“点个评分推荐更懂你”。这些策略在实现上并不复杂但写进论文里很有价值体现的是你对推荐系统工程问题的理解深度而不仅仅是“我调了一个库”。6.3 常见错误和坑花式Debug实录这个部分我整理了几条我实际踩过的坑每一个都能节省你半天以上的调试时间。第一个坑是np.corrcoef返回全NaN。引起这个问题的原因是共同评分项方差为0比如两个用户对同一批菜全部打了5分。解决办法是在用之前做NaN检查或者手动实现皮尔逊公式推荐后者自己写公式还能加深理解。第二个坑是pandas的pivot_table用了fillna(0)之后评分矩阵变得极度稀疏。0在数学上等于“没有评分”但在余弦相似度的计算里0直接参与运算两个“没评过分”的菜品可能因此被判定为“不相似”。这个问题没那么容易察觉因为程序不报错只是结果诡异。解决办法是计算相似度的时候只取共同评分的维度也就是我前面代码里common_mask的处理方式。第三个坑是评分表没有唯一索引导致同一个用户对同一道菜重复评分。从用户体验上看用户可能不小心点了两次提交系统就记录了两次评分推荐结果会出现一种不合理波动。解决办法是建表时给(user_id, dish_id)加联合唯一索引再用INSERT ... ON DUPLICATE KEY UPDATE做覆盖式更新。第四个坑是推荐接口返回太慢。如果你每次请求都现场算相似度矩阵20个用户、50道菜的数据规模可能感觉不出来但数据量上千后就会明显卡顿。我建议把相似度矩阵的构建放在一个独立模块里程序启动时预计算并缓存到内存或数据库推荐接口只做查表和Top-N排序实测响应时间能从1秒多降到50毫秒以内。6.4 实操总结一步一步跑通全项目的顺序我最后整理一个实操顺序清单适合完全没思路的同学按步骤执行。第一步安装Python环境并配置好pycharm或vscode装好pandas、numpy、flask、pymysql等库。第二步准备数据优先用公开数据集或自定义数据构建rating.csv、dish.csv、user.csv三个文件。第三步单独写一个算法测试脚本用pandas读数据、构建评分矩阵、实现UserCF和ItemCF、输出推荐结果并打印评估指标这一步的重点是确认算法逻辑正确。第四步搭建Flask项目结构创建蓝图、模板目录、静态资源目录。第五步设计数据库表结构并建表写数据库连接模块。第六步实现注册登录、菜品浏览、评分提交等基础功能。第七步把推荐引擎嵌入到推荐接口中做冷启动和混合推荐逻辑。第八步写测试用例准备论文里的截图和测试数据。第九步按照论文章节结构填充内容PPT提炼核心逻辑。在做完这九步之后你会发现自己对推荐系统的理解已经不是停留在“调了一个库”的水平而是真正能讲清楚“推荐结果为什么是这样”的人。这种从原理到实现的闭环才是这门课真正带给你的东西。最后分享一个我在实际测试中发现的规律协同过滤最怕的不是数据量少而是行为数据不真实。如果造数据时所有人都只打高分、不打低分推荐结果几乎没有区分度。所以不管是自己造数据还是找公开数据一定确保评分有高有低、用户口味有差异这样算法效果才能真实反映出来。数据质量决定了推荐质量这个道理在真实业务里一样成立。本文还有配套的精品资源点击获取
返回列表