ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Python协同过滤电影推荐系统毕设:从源码到答辩的完整指南

Python协同过滤电影推荐系统毕设:从源码到答辩的完整指南 简介这份资源是Python基于协同过滤推荐算法的电影推荐系统完整毕设项目面向计算机相关专业需要完成毕业设计、课程设计或期末大作业的学生。项目已获导师指导并通过答辩评审分达97分下载后无需修改即可直接运行能有效解决选题难、代码不完整、论文无从下笔等问题。压缩包共687个文件约13.32MB包含38个py源码文件、34个pyc编译文件、33个vue前端组件、20个html页面、2个sql数据库脚本及doc论文文档另配有bat一键安装运行脚本前端资源涵盖svg、js、css、png等结构完整、层次清晰。目前已有356人学习下载。读者可获得协同过滤推荐算法的完整实现方案、配套论文与全部数据集以及可直接部署运行的前后端工程便于快速理解推荐流程、复现实验结果并在此基础上进行二次开发与答辩准备。1. 从一份毕设压缩包说起协同过滤电影推荐到底难在哪很多计算机专业的同学拿到「python基于协同过滤推荐算法的电影推荐系统源码全部数据论文」这类毕设题目时第一反应是去搜免费python源码大全下载一个压缩包解压跑起来看到网页上出现几部电影就以为大功告成。真正答辩的时候老师问一句「你这个相似度怎么算的为什么给这个用户推这部片子」当场就卡住了。问题不在于代码能不能跑而在于你有没有真正理解协同过滤这条链路数据怎么进、相似度怎么算、推荐列表怎么生成、冷启动怎么兜底。这篇笔记就围绕这个标题拆开讲。它本质上是一个用 Python 实现的电影推荐系统核心算法是协同过滤配套有数据集和论文文档面向的是毕设场景。我会把「这是什么、怎么做、参数怎么调、坑在哪」四件事讲清楚让你不仅能跑通还能在答辩时把每个环节说明白。适合正在做毕设选题、想找一个能讲透的推荐系统方向的同学也适合已经拿到源码但看不懂内部逻辑、想自己重写一遍的人。下面从数据、算法、工程三个层面往下走。2. 协同过滤的两条路线UserCF 和 ItemCF 到底选哪个2.1 推荐系统的最小数据模型长什么样在写任何算法之前先把数据这件事想清楚。电影推荐系统最常用的公开数据集是 MovieLens它提供三种规模的版本小规模的有 10 万条评分、约 600 个用户和 9000 部电影足够跑通一个毕设。数据文件通常是三个ratings.csv 存用户对电影的评分movies.csv 存电影 ID、标题和类型users.csv 可选存用户的基本属性。核心的一张表就是评分矩阵行是用户列是电影格子里是评分通常是 1 到 5 分。这个矩阵极度稀疏因为一个用户最多看过几百部电影而电影总数上万稀疏度经常在 95% 以上。协同过滤的所有麻烦本质上都是从这个稀疏矩阵里挖出可用的信号。用 pandas 读进来大概是这样import pandas as pd # 读取评分数据MovieLens 的 ratings.csv 列名是 userId, movieId, rating, timestamp ratings pd.read_csv(ml-latest-small/ratings.csv) movies pd.read_csv(ml-latest-small/movies.csv) # 看一下数据规模和稀疏度 n_users ratings[userId].nunique() n_movies ratings[movieId].nunique() n_ratings len(ratings) sparsity 1 - n_ratings / (n_users * n_movies) print(f用户数: {n_users}, 电影数: {n_movies}, 评分数: {n_ratings}) print(f稀疏度: {sparsity:.4f}) # 通常输出 0.98 左右这段代码做三件事加载评分和电影元数据、统计用户和电影数量、计算稀疏度。参数上要注意ml-latest-small是相对路径实际项目里建议用os.path.join拼绝对路径避免换机器就找不到文件。稀疏度算出来接近 0.98 是正常的如果你看到 0.5 那说明数据有问题可能是把测试集和训练集混在一起了。2.2 UserCF 和 ItemCF 的适用边界协同过滤分两大流派。UserCF 是「找和你口味相似的人把他们喜欢的推给你」ItemCF 是「找和你喜欢的电影相似的电影推给你」。两者没有绝对优劣但有明确的适用场景。UserCF 适合用户数量远小于物品数量的场景比如新闻推荐因为新闻更新极快物品相似度算不过来但用户兴趣相对稳定。ItemCF 适合物品数量相对稳定、用户数量庞大的场景比如电影、电商因为电影库不会天天变物品相似度可以离线算好缓存起来。电影推荐系统里ItemCF 是更常见的选择也是毕设里更容易讲清楚的一条线。相似度计算是核心。余弦相似度最常用公式是两向量点积除以模长乘积。对于评分矩阵还要考虑用户评分尺度差异——有人习惯打 5 分有人最高只打 3 分所以实践中常用调整余弦相似度先减去用户平均分再算。下面是一个 ItemCF 相似度计算的实现import numpy as np from sklearn.metrics.pairwise import cosine_similarity # 构建用户-电影评分矩阵缺失值填 0 user_movie_matrix ratings.pivot_table( indexuserId, columnsmovieId, valuesrating ).fillna(0) # 转置成电影-用户矩阵计算电影之间的相似度 movie_user_matrix user_movie_matrix.T.values item_similarity cosine_similarity(movie_user_matrix) # 把相似度转成 DataFrame方便按 movieId 索引 item_sim_df pd.DataFrame( item_similarity, indexuser_movie_matrix.columns, columnsuser_movie_matrix.columns ) print(item_sim_df.shape) # (电影数, 电影数)这里有几个参数和逻辑要说明。pivot_table默认对重复的 userId-movieId 组合取均值MovieLens 里一般没有重复评分但如果你自己爬的数据有重复这一步会自动聚合。fillna(0)是把没看过的电影当 0 分处理这是最粗暴的做法会引入「没看过等于不喜欢」的偏差后面讲坑的时候会展开。cosine_similarity直接吃二维数组返回的是对称矩阵对角线是 1。电影数量上万时这个矩阵是万乘万内存占用不小毕设规模下还能扛生产环境必须做截断或稀疏化。2.3 从相似度到推荐列表的完整链路有了相似度矩阵给用户生成推荐就三步找到用户看过的电影、根据相似度加权算出候选电影的得分、按得分排序取 TopN。这里要注意排除用户已经看过的电影否则推荐列表里全是老片。def recommend_for_user(user_id, user_movie_matrix, item_sim_df, top_n10): # 取出该用户的评分记录 user_ratings user_movie_matrix.loc[user_id] # 只看用户评过分的电影 rated_movies user_ratings[user_ratings 0].index.tolist() if not rated_movies: return [] # 冷启动后面单独处理 # 用相似度矩阵加权求和得到每部候选电影的得分 sim_scores item_sim_df[rated_movies].dot(user_ratings[rated_movies]) # 归一化避免热门电影因为被相似电影多而得分虚高 sim_scores sim_scores / item_sim_df[rated_movies].sum(axis1) # 排除已看过的电影 sim_scores sim_scores.drop(labelsrated_movies, errorsignore) # 取 TopN top_movies sim_scores.sort_values(ascendingFalse).head(top_n) return top_movies.index.tolist()逻辑上item_sim_df[rated_movies]取出用户看过的那些电影对应的相似度列.dot(user_ratings[rated_movies])做加权求和权重就是用户对每部电影的评分。归一化那一步很关键如果不除相似度之和一部被很多电影相似的片子会天然得分高推荐结果会偏向热门。drop排除已看过的电影errorsignore防止某些电影 ID 不在索引里报错。top_n默认 10毕设演示够用实际可以调到 20 到 50 做 A/B 测试。3. 把算法跑起来从环境配置到 Web 界面3.1 Python 环境与依赖的安装顺序毕设翻车最高频的地方不是算法是环境。python安装教程网上一搜一大把但很多人装完还是跑不起来原因是版本和依赖顺序不对。我的建议是Python 用 3.8 到 3.10 之间的版本太新的版本某些科学计算库还没跟上太老的版本 pandas 语法不兼容。用 conda 建虚拟环境比直接装全局干净得多。# 创建虚拟环境指定 Python 版本 conda create -n movie_rec python3.9 conda activate movie_rec # 按顺序安装numpy 和 scipy 先装pandas 依赖它们 pip install numpy scipy pip install pandas scikit-learn pip install flask # 如果要做 Web 界面 pip install jieba # 如果电影标题或简介要做中文分词安装顺序有讲究。numpy 和 scipy 是底层pandas 和 scikit-learn 依赖它们先装底层能避免 pip 去编译源码。如果你在 Windows 上遇到Microsoft Visual C 14.0 is required这类报错说明某个包没有预编译轮子去装一下 Build Tools 或者换用 conda 安装。vscode python 环境配置的话装完 Python 扩展后按 CtrlShiftP 选解释器指向你刚建的 conda 环境就行。3.2 离线训练与在线推荐的工程拆分一个能答辩的系统不能只是 Jupyter Notebook 里跑几个函数。合理的做法是把离线训练和在线推荐拆开离线部分负责算相似度矩阵并存到磁盘在线部分负责读矩阵、生成推荐、返回给前端。这样即使相似度计算要几分钟用户请求也不用等。import pickle import os # 离线训练脚本算完相似度后持久化 def train_and_save(ratings_path, model_dirmodel): os.makedirs(model_dir, exist_okTrue) ratings pd.read_csv(ratings_path) user_movie_matrix ratings.pivot_table( indexuserId, columnsmovieId, valuesrating ).fillna(0) item_sim cosine_similarity(user_movie_matrix.T.values) item_sim_df pd.DataFrame( item_sim, indexuser_movie_matrix.columns, columnsuser_movie_matrix.columns ) # 保存矩阵和相似度 user_movie_matrix.to_pickle(os.path.join(model_dir, user_movie.pkl)) item_sim_df.to_pickle(os.path.join(model_dir, item_sim.pkl)) print(模型已保存) # 在线服务加载模型并推荐 def load_and_recommend(user_id, top_n10): user_movie_matrix pd.read_pickle(model/user_movie.pkl) item_sim_df pd.read_pickle(model/item_sim.pkl) return recommend_for_user(user_id, user_movie_matrix, item_sim_df, top_n)用 pickle 存 DataFrame 是最省事的做法缺点是跨 Python 版本可能不兼容答辩演示前一定要在目标机器上重新跑一遍训练脚本。model_dir参数让你可以切换不同数据集训练出的模型方便对比实验。在线服务里每次请求都读一遍 pickle 其实有性能问题生产环境会用 Redis 或内存缓存但毕设规模下这样写足够而且逻辑清晰好讲。3.3 用 Flask 搭一个能演示的推荐接口答辩时老师最想看到的是「输入一个用户 ID页面上出现推荐结果」。用 Flask 写一个最小接口配合简单的前端页面就能演示。from flask import Flask, request, jsonify, render_template app Flask(__name__) app.route(/) def index(): return render_template(index.html) # 一个输入用户ID的表单 app.route(/recommend) def recommend(): user_id int(request.args.get(user_id, 1)) try: movie_ids load_and_recommend(user_id, top_n10) # 把 movieId 换成电影标题返回 movies_df pd.read_csv(ml-latest-small/movies.csv) titles movies_df[movies_df[movieId].isin(movie_ids)][title].tolist() return jsonify({user_id: user_id, recommendations: titles}) except Exception as e: return jsonify({error: str(e)}), 500 if __name__ __main__: app.run(debugTrue, port5000)request.args.get拿 URL 参数默认用户 ID 设为 1 方便演示。jsonify返回 JSON前端用 fetch 拿数据渲染成列表。debugTrue只在开发时开答辩演示时如果关掉出错页面不会暴露堆栈信息更稳妥。端口 5000 是 Flask 默认如果被占用改成 5001。这个接口没有做用户认证和限流毕设演示够用但论文里要说明这是简化实现。4. 避坑与排查协同过滤毕设里最容易翻车的五件事4.1 现象推荐结果全是热门电影冷门好片一部都不出原因相似度矩阵没有做归一化或者用了原始余弦相似度而没有惩罚热门物品。热门电影被大量用户看过和很多电影都有共现相似度求和后天然得分高。另外fillna(0)把没看过当 0 分导致冷门电影因为评分人数少而相似度计算不稳定。解决在相似度计算时引入热门惩罚比如用1 / log(1 电影被评分次数)做权重。推荐得分那一步一定要除以相似度之和做归一化。如果还是偏热门可以在排序时加一个多样性重排比如同类电影最多推 3 部。4.2 现象换一个用户 ID推荐结果完全一样原因用户 ID 在评分矩阵里不存在或者该用户评分数太少代码走了冷启动分支返回空列表前端没做处理就显示了默认热门榜。还有一种可能是pivot_table之后索引类型变了loc[user_id]匹配不上。解决在推荐函数入口先检查用户是否存在不存在就返回基于流行度的兜底推荐。检查索引类型必要时用user_movie_matrix.index user_movie_matrix.index.astype(int)统一。前端对空结果要有提示不能静默显示错误数据。4.3 现象相似度矩阵内存爆了程序被系统杀掉原因电影数量上万时cosine_similarity返回的是 float64 的稠密矩阵万乘万就是 8 亿个浮点数约 6.4 GB普通笔记本直接扛不住。解决用 float32 存相似度内存减半。更彻底的做法是只保留每个电影相似度最高的 K 个邻居K 取 50 到 100用稀疏矩阵存储。sklearn 的NearestNeighbors可以只算近邻不用全量矩阵。4.4 现象训练时评分矩阵和预测时对不上报 KeyError原因离线训练用的数据集和在线加载的数据集版本不一致或者中间做了过滤比如去掉评分少于 5 条的用户但保存的矩阵没有同步更新。解决把数据预处理和矩阵构建写成一个函数训练和预测都调同一个函数。保存模型时把电影 ID 列表和用户 ID 列表一起存下来加载时校验一致性。任何过滤操作都要在保存前完成不要在线阶段再过滤。4.5 现象论文里的准确率指标算出来高得离谱接近 1.0原因评估时把训练集里的评分也拿来预测了模型「见过」这些数据当然准。或者负样本采样时把用户没看过的电影全当负样本但其中很多是用户可能喜欢只是没看到的导致评估偏差。解决严格划分训练集和测试集按时间划分比随机划分更贴近真实场景。评估指标用 RecallK 和 NDCGK不要只用准确率。负样本采样时每个正样本配 4 到 10 个负样本不要全量。论文里要写清楚评估协议这是答辩老师常问的点。5. 让推荐结果经得起追问评估、调参与论文写作技巧5.1 用 RecallK 和 NDCGK 证明你的模型有效毕设论文里光说「推荐效果良好」是没用的必须有数字。协同过滤最常用的两个指标是 RecallK 和 NDCGK。RecallK 衡量的是「用户真正喜欢的电影里有多少出现在推荐列表前 K 位」NDCGK 则考虑了位置因素排在前面的命中比排在后面的命中得分更高。def recall_at_k(recommended, actual, k10): # recommended 是推荐列表actual 是测试集里用户实际喜欢的电影 top_k set(recommended[:k]) hit len(top_k set(actual)) return hit / len(actual) if actual else 0 def ndcg_at_k(recommended, actual, k10): top_k recommended[:k] dcg 0.0 for i, movie in enumerate(top_k): if movie in actual: dcg 1.0 / np.log2(i 2) # 位置从 1 开始所以 i2 # 理想 DCG假设所有命中都排在最前面 idcg sum(1.0 / np.log2(i 2) for i in range(min(len(actual), k))) return dcg / idcg if idcg 0 else 0recall_at_k里top_k set(actual)是集合交集算出命中数。ndcg_at_k的折损因子1/log2(i2)是标准公式位置越靠后权重越低。idcg是理想情况下的 DCG用来归一化到 0 到 1 之间。跑评估时对所有测试用户取平均K 一般取 10 或 20。如果你的 Recall10 能到 0.15 以上在 MovieLens 小数据集上就算正常水平低于 0.05 说明模型有问题。5.2 相似度算法和邻居数量的调参对照调参不要瞎试要有对照表。下面是我在 MovieLens 10 万条评分上跑出来的经验值你可以照着复现但具体数字会因数据划分方式不同而有波动。相似度算法邻居数 KRecall10NDCG10训练耗时余弦相似度200.110.09快余弦相似度500.140.11中调整余弦500.160.13中皮尔逊相关系数500.150.12慢调整余弦1000.160.13慢从表里能看出两个规律邻居数从 20 加到 50 有明显提升但从 50 加到 100 基本没变化说明 50 左右是性价比最高的点。调整余弦比普通余弦好因为它消除了用户评分尺度差异。皮尔逊和调整余弦效果接近但皮尔逊计算更慢毕设里用调整余弦更划算。论文里可以把这张表放进去说明你做过参数敏感性分析这是加分项。5.3 论文里必须写清楚的三个技术细节答辩老师不会逐行看你的代码但一定会追问三个地方。第一是相似度公式你要能写出调整余弦的数学表达式并解释为什么要减去用户平均分。第二是评估协议训练集测试集怎么分的、负样本怎么采的、指标怎么算的这三句话要能脱口而出。第三是冷启动处理新用户没有评分时你的系统返回什么是热门榜还是基于注册信息的粗粒度推荐这个必须有兜底方案。论文写作上不要把源码贴进去凑字数。核心代码放附录正文里用伪代码或流程图说明逻辑。实验部分要有对比至少对比 UserCF 和 ItemCF 两种方法最好再加一个基于流行度的基线证明你的协同过滤确实比「直接推最热门的」要好。如果时间充裕可以加一组冷启动用户的实验说明你的兜底策略有效。最后说一个我自己的习惯每次改完代码先跑一遍完整流程从读数据到出推荐列表确认没有报错再去看指标。很多人调参调了半天结果是数据读取那一步就漏了几百条记录指标全是错的。毕设这东西跑通比跑分重要讲清楚比跑通更重要。希望帮到你。本文还有配套的精品资源点击获取
返回列表