恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

协同过滤电影推荐系统:从CSV到评分矩阵的Python实现

  • 首页
  • 资讯中心
  • /
  • 协同过滤电影推荐系统:从CSV到评分矩阵的Python实现

相关资讯

Qt5.13连接Oracle 11g的32位兼容性实战指南 2026/10/18 11:21:54
C语言链表完全指南:从结构定义到工程实战 2026/10/18 11:21:54
JSP+SSH毕设实战:时装设计网站的架构选型与避坑指南 2026/10/18 11:21:54

最新资讯

mmclassification自定义数据集训练全流程:从配置到部署
逆向工程四大方向:Web、软件、安卓与APP逆向实战指南
从零构建自定义数据集训练链路:mmclassification实战与避坑指南
四大逆向方向全解析:从Web到APP的逆向工程实战指南
软考中级系统集成:项目采购管理10大默写考点精讲
BrowserSkill:基于DevTools协议的CLI浏览器自动化框架

今日推荐

基于GMM的语音识别系统原理与MATLAB实现详解
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

本周热门

基于GMM的语音识别系统原理与MATLAB实现详解
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

协同过滤电影推荐系统:从CSV到评分矩阵的Python实现

发布时间:2026/10/18 11:26:54
协同过滤电影推荐系统:从CSV到评分矩阵的Python实现 简介基于Python的协同过滤电影推荐系统源码包完整覆盖用户相似度计算、基于用户与基于物品的两种协同过滤实现、推荐排序及结果评估等核心环节适合推荐系统初学者、算法研究者以及需要完成毕业设计的开发者参考借鉴。压缩包内含228个文件整体约6.92MB以17个py源码文件为主体承载数据清洗、用户相似度计算与推荐生成逻辑另有14个pyc编译文件、7个csv数据集、1个sql数据库脚本以及css、js、html等前端页面文件和148张界面展示图片构成一套可运行、可扩展的完整项目工程。当前已有60人学习下载尤其适用于需要从零搭建推荐系统原型的实验场景。借助这份源码读者能直观理解协同过滤如何处理用户评分记录掌握余弦相似度、皮尔逊相关系数等经典相似度计算方法并参考其前端交互与可视化设计快速迭代自己的实验项目同时源码中包括的评估与优化思路也为后续引入矩阵分解或深度学习模型、进一步解决冷启动等问题提供了扎实基础。1. 打开这个zip的正确顺序先把三个CSV当说明书看完打开这个压缩包最先看到的不是 .py 文件而是一堆 bootstrap.min.css、Test.css、firstPage.css、star.css 这类样式文件——我第一反应是怀疑自己下错了资源。再往下翻ratings.csv、MoviesGenre3.csv、rrtotaltable.csv 三张表摆在面前Python 源码夹在中间这才确认它是一个能直接跑起来的基于协同过滤算法的电影推荐系统不是只有公式的讲义。它解决的核心问题很具体根据用户历史评分从几千部电影里推断他下一步最可能看什么。适合正在准备推荐系统相关课题的学生也适合想快速在 Flask 或 Django 后端里嫁接一个个性化推荐接口的从业者。我拆完这个包之后最大的感受是这个项目最好的入口不是先读源码而是先把三个 CSV 当说明书读完。2. 从三个CSV到评分矩阵数据预处理与稀疏度控制2.1 三张表是协同过滤的输入输出契约ratings.csv、MovieGenre3.csv、rrtotaltable.csv三张表几乎把协同过滤的输入、辅助信息和输出都定义完了。拿到手第一件事就是把它们并列放在一起看字段因为它决定了后面的矩阵怎么建、预测结果怎么复核。文件典型字段在推荐链路里的角色ratings.csvuserId, movieId, rating, timestamp协同过滤的主输入谁在什么时候给哪部电影打了多少分MovieGenre3.csvmovieId, title, genres电影元数据用于展示标题、按类型做冷启动兜底rrtotaltable.csvuserId, movieId, rating, predict, title, rank作者跑完算法导出的推荐结果相当于一份“标准答案”样例很多初学者拿到压缩包第一眼会往 Python 源码里冲想赶紧看协同过滤怎么写的。但真正会让我反复翻看的是 rrtotaltable.csv。它把算法的输出结构固定成了“用户 ID 电影 ID 预测分 排名”你后面写的任何推荐函数最终都要落到这个格式上。这不是玄学是工程约定前端要展示推荐列表、后端要做分页和日志靠的就是这个统一输出格式。2.2 用Pandas把长表转成评分矩阵rating.csv 是典型的“长表”每个用户对每部电影的一次评分就是一行。协同过滤算法需要的却是“宽表”行是用户列是电影单元格是分数。这个转换不做好后面所有相似度计算都是空谈。我一般会先用 Pandas 做一次合并把电影标题带过来然后直接用 pivot_table 转矩阵。import pandas as pd import numpy as np # 读取评分数据dtype 显式指定可以避免大表时 float 被读成 object ratings pd.read_csv( ratings.csv, dtype{userId: int32, movieId: int32, rating: float32} ) # 读取电影信息只保留构建矩阵和兜底推荐用到的字段 movie_info pd.read_csv( MovieGenre3.csv, dtype{movieId: int32} )[[movieId, title, genres]] # 把电影标题联回评分表 merged ratings.merge(movie_info, onmovieId, howleft) # 长表转宽表indexuserId, columnsmovieId, valuesrating rating_matrix merged.pivot_table( indexuserId, columnsmovieId, valuesrating ) print(用户数 x 电影数:, rating_matrix.shape) print(稀疏度: %.2f%% % (100 * rating_matrix.notna().sum().sum() / rating_matrix.size))这段代码里最容易被忽略的是 dtype 参数。rating 表在实际项目中往往几百 MB如果不指定 dtypePandas 会按 int64 和 float64 去猜内存占用直接翻倍。pivot_table 之后得到的 rating_matrix默认会把缺失评分填成 NaN这一点非常关键——它代表“用户没看过这部电影”而不是“用户打了 0 分”。所以稀疏度算出来通常会超过 95%也就是说矩阵里绝大多数格子都是空值。2.3 均值中心化消除用户打分尺度差异拿到评分矩阵后直接算相似度是一个很常见的低级错误。因为每个人的打分习惯不一样有人习惯打 3 到 4 分有人要么 1 分要么 5 分原始的 4 分对不同用户含义完全不同。解决方式是做均值中心化把每个用户自己的平均分减掉剩下的值代表“这部电影比这个用户平时口味高/低多少”。# 每个用户的平均评分axis1 表示按行计算 user_mean rating_matrix.mean(axis1) # 归一化矩阵减去用户均分缺失评分先填 0 # 注意这个 0 只在相似度计算时用不代表真实评分 norm_matrix rating_matrix.sub(user_mean, axis0).fillna(0.0) # 同样可以算每部电影的平均分Item-based 预测时会用到 item_mean rating_matrix.mean(axis0) print(示例用户均值:, user_mean.head(3).round(2).to_dict())参数 axis0 的含义是沿着行方向做减法也就是每个用户减自己的均值。fillna(0.0) 把缺失位置补成 0因为中心化后的 0 表示“和用户平均口味持平”这在数学上比直接补原始评分靠谱得多。但这里有个隐藏坑如果用户只打过一部电影中心化后该行只剩一个 0其他全是 0这个用户和谁算相似度都得不到有效邻居。后面避坑章节会专门说这个场景。3. 两种协同过滤的实现余弦相似度、User-based与Item-based3.1 相似度计算的三个候选余弦、皮尔逊、Jaccard协同过滤的本质是找“关系近的人或物”。关系怎么量化源码包里最常用的就是余弦相似度、皮尔逊相关系数和 Jaccard 相似度。它们不是互相替代的关系而是适用场景不同。方法数学直觉适合场景缺点余弦相似度向量夹角越小越相似评分向量完整时效果好对用户评分尺度敏感皮尔逊相关系数减去均值后的余弦评分尺度不一时更稳冷启动时容易分母为 0Jaccard 相似度交集 / 并集只看是否看过不管打几分丢失评分强度信息在 Python 里计算三种相似度都不难。我会优先用 scikit-learn 的 cosine_similarity因为它内部对稀疏数据做了优化比自己写循环快一个量级。from sklearn.metrics.pairwise import cosine_similarity # 对中心化后的矩阵做用户相似度行是用户 user_sim cosine_similarity(norm_matrix) user_sim_df pd.DataFrame( user_sim, indexrating_matrix.index, columnsrating_matrix.index ) # 对中心化矩阵转置可以算物品相似度 item_sim cosine_similarity(norm_matrix.T) item_sim_df pd.DataFrame( item_sim, indexrating_matrix.columns, columnsrating_matrix.columns )注意 cosine_similarity 默认会对向量做 L2 归一化所以直接传入原始评分矩阵也可以跑但效果远不如先做均值中心化。如果追求更严格的皮尔逊系数可以直接调用 DataFrame.corr(methodpearson)不过当矩阵里有大量 NaN 时corr 只能利用两个用户共同评过分的电影来计算共同评分太少时相关系数会非常不稳定。这也是为什么实践中很多人宁可用“中心化 余弦”来代替皮尔逊形式上等价但数值稳定性更好。3.2 User-based CF找口味相近的人加权算预测分基于用户的协同过滤逻辑很简单找到和目标用户最像的 K 个用户看这 K 个人怎么评价一部目标用户没看过的电影然后按相似度加权汇总。源码包里如果只实现了一个预测函数大概率就是这段逻辑。def user_based_predict(user_id, movie_id, K20, threshold0.1): # 电影不存在于评分矩阵时直接返回 NaN if movie_id not in rating_matrix.columns: return np.nan # 取出用户之间相似度行 sims user_sim_df.loc[user_id] # 只保留给这部电影打过分的其他用户 other_users rating_matrix[movie_id].dropna().index candidates sims[other_users].drop(user_id, errorsignore) # 过滤相似度过低的再取前 K 个 candidates candidates[candidates threshold].nlargest(K) if len(candidates) 0: return np.nan # 加权求和归一化评分 * 相似度 / 相似度总和最后加回用户均值 weights candidates.values norm_vals norm_matrix.loc[candidates.index, movie_id].values pred (weights * norm_vals).sum() / weights.sum() user_mean.loc[user_id] return round(float(pred), 4)这里 K 控制邻居数量threshold 用来过滤相似度太低的用户。K 取太小预测方差大K 取太大会把不相关的用户也拉进来。常见做法是 K20 到 K50 之间再配合交叉验证去找最优值。threshold0.1 的意思是相似度低于 0.1 的邻居直接不要作用是防止一堆低相似度用户把预测结果拖向全局均值。外层调用时需要处理两个失败分支电影不存在返回 NaN邻居不足返回 NaN。NaN 会向后传导最后前端会看到一个空推荐位这就是冷启动问题的一种表现。3.3 Item-based CF转置评分矩阵把电影当用户算基于用户的 CF 最大的问题是用户数量增长很快每次都要算两两用户之间的相似度在线场景基本扛不住。而基于物品的 CF 是提前算好电影之间的相似度在线推荐时只需要查表。电影数量相对稳定所以这个方案被工业界用得更广泛。def item_based_predict(user_id, movie_id, K20, threshold0.0): if movie_id not in item_sim_df.columns: return np.nan # 取目标电影的相似度行 sims item_sim_df[movie_id] # 找出目标用户看过的电影 rated_items rating_matrix.loc[user_id].dropna() # 从看过的电影里找出与目标电影最像的 K 部 candidates sims[rated_items.index].drop(movie_id, errorsignore) candidates candidates[candidates threshold].nlargest(K) if len(candidates) 0: return np.nan # 用候选电影的评分加权平均预测目标电影得分 weights candidates.values user_ratings rated_items.loc[candidates.index].values pred (weights * user_ratings).sum() / weights.sum() return round(float(pred), 4)Item-based 的预测公式比 User-based 少一步均值回填因为这里衡量的是电影之间的绝对相似度用户的评分尺度影响不大。不过更好的做法是把用户均值和电影均值都纳入公式比如pred item_mean[movie_id] (加权评分的偏离量)。这个偏离量就是候选电影评分减去各自均值的加权和。源码包里的 rrtotaltable.csv 如果对比过两套算法你会发现 Item-based 的结果通常“更稳”它不会因为某个新用户只有三五条评分就完全崩掉只要用户看过的电影里有一部能和目标电影算相似度就能给出预测。缺点是冷门电影因为很少被共同评分相似度全是零永远没有露脸机会。所以很多系统会在相似度矩阵里加一个“平滑项”给冷门电影一个最低权重。4. 协同过滤排查与调参四个必查点4.1 新用户一进来推荐列表全空白现象用户 ID 在 ratings.csv 里没有任何评分记录调用 user_based_predict 或 item_based_predict 返回值全是 NaN前端榜单直接空白。原因协同过滤完全依赖历史评分建立联系。新用户是“无历史、无邻居、无评分”的三无用户相似度矩阵里对应行全为零候选邻居被 threshold 全部过滤掉。解决在推荐入口加一个回退策略。最直接的办法是取全局热度榜兜底按所有评分的平均分排序选前 N 部电影给新用户。更精细一点的做法是使用 MovieGenre3.csv 的 genres 字段做类型覆盖率先让用户选三个喜欢的类型然后从这些类型里按热度排序。def safe_recommend(user_id, top_n10): preds batch_predict(user_id) if preds.dropna().empty: # 回退到全局热度榜评分数量 * 平均分加权 hot rating_matrix.count().mul(rating_matrix.mean()).sort_values(ascendingFalse) return hot.head(top_n).index.tolist() return preds.sort_values(ascendingFalse).head(top_n).index.tolist()这个逻辑看起来简单但能挡住线上环境最尴尬的“白屏推荐”。后来我养成的习惯是任何推荐接口上线前先用一个从没出现过的 userId 去压一遍测试确认兜底逻辑有效再放行。4.2 评分全是一个均值预测结果毫无区分度现象算法跑通了但每个用户拿到的 Top-10 推荐列表高度相似预测分都集中在 3.5 到 3.8 之间几乎没有波动。原因训练矩阵过于稀疏大部分候选电影只有零星几个人看过相似度计算时这些冷门电影的邻居数量太少加权平均后自然滑向全局均值。另一个原因是用户评分数据本身就集中在中段比如大部分人都打 3 到 4 分中心化之后的偏差值很小。解决先看评分分布。如果 ratings.csv 里评分方差过小第一优先级不是调算法而是检查数据采集口径是不是用了“五星制但只允许三星起评”的控件如果是算法本身的问题可以给相似度加一个“共同评分数量”下限。# 约束两个用户至少共同评过 5 部电影相似度才有效 common_count rating_matrix.notna().astype(int).dot(rating_matrix.notna().astype(int).T) valid_mask common_count 5 user_sim_df user_sim_df.where(valid_mask, 0.0)4.3 相似度被热门电影带偏推荐结果全是“大众爆款”现象推荐列表里频繁出现钢铁侠、复联这类高热度电影用户明明偏好小众文艺片结果榜上全是商业大片。原因余弦相似度在计算电影相似度时热门电影因为被大量用户评过分在很多用户的 norm 向量里都残留非零值导致它与大部分电影都有不低的相似度。相似度被“流行度”主导而不是被“共同偏好”主导。解决对 norm_matrix 做流行度惩罚。常见做法是在计算物品相似度时把电影的被评次数作为分母权重类似 IDF 的思路。# 电影流行度被评次数 item_popularity rating_matrix.notna().sum(axis0) # 惩罚系数被评次数越多的电影权重越低 penalty 1.0 / (1.0 np.log(item_popularity)).values # 加权后再算相似度需要逐项相乘 weighted_norm norm_matrix.mul(penalty, axis1) item_sim_penalized cosine_similarity(weighted_norm.T)这个处理会让冷门电影在相似度矩阵里获得更多话语权但也别惩罚过度否则相似度会变得很碎推荐结果跳跃性太强。penalty 公式里的 1 是防止极端数据下取对数出错工程上是个常见小细节。4.4 训练测试划分不当评估分数虚高现象把评分数据随机按 80/20 切分训练集和测试集完全随机混合然后拿测试集数据回测准确率和召回率都高得离谱上线后推荐效果却明显缩水。原因随机划分导致同一个用户的多条评分被同时分到训练集和测试集算法在训练时已经见过用户对某部电影的口味测试时只不过是在“回忆答案”。这属于数据泄漏不是真实泛化能力。解决按用户维度做划分。比如每个用户取其最早 80% 的评分做训练最晚 20% 的评分做测试。ratings.csv 里本身有 timestamp 字段正好用上时间序划分。from sklearn.metrics import precision_score, recall_score, f1_score def temporal_split(ratings, test_ratio0.2): ratings ratings.sort_values([userId, timestamp]) train_list, test_list [], [] for _, group in ratings.groupby(userId): cut int(len(group) * (1 - test_ratio)) train_list.append(group.iloc[:cut]) test_list.append(group.iloc[cut:]) return pd.concat(train_list), pd.concat(test_list) train_df, test_df temporal_split(merged)这种划分方式更接近真实场景用户只拥有过去的数据要预测的是未来的行为。评估时再用 precisionK 和 recallK 看推荐命中率而不是拿预测分数和真实分数对比。预测分数是一个序数概念和真实分差 0.5 不代表推荐失败关键看排序是否正确。5. 进阶用 rrtotaltable.csv 反推模型质量做好冷启动验证rrtotaltable.csv 表面上是推荐结果导出其实是一个很好的“对照样本”。我拿到这个文件后做的第一件事是把它和原始 ratings.csv 做一次差分比较看看哪些电影被推荐给了用户但这些用户从来没给它们评过分。这才是协同过滤真正产生价值的地方。进阶玩法是把这个表当作一个回归目标用代码去反推算法参数。比如你怀疑邻居数量 K 没调好可以固定其他参数不变分别跑 K10、20、50然后用 rrtotaltable.csv 里的预测分数和当前模型的预测分数做 RMSE 对比选误差最小的 K。这一步直接把你从“看代码”推进到“调代码”。# 顺手用命令行先看 rrtotaltable.csv 的列结构和行数 head -5 rrtotaltable.csv wc -l rrtotaltable.csv另一个我特别建议做的最小验证是冷启动测试。# 构造一个只有 1 条评分的“半冷启动”用户 fake_user_id 99999 rating_matrix.loc[fake_user_id] 0 rating_matrix.loc[fake_user_id, 1] 5.0 norm_matrix.loc[fake_user_id] rating_matrix.loc[fake_user_id] - rating_matrix.loc[fake_user_id].mean() cold_pred user_based_predict(fake_user_id, 1) print(半冷启动用户预测分:, cold_pred)如果这条预测返回 NaN说明算法的邻居过滤条件太苛刻threshold 或 K 需要调低。如果返回一个接近用户均值的分数说明预测结果还有参考价值但要注意置信度很低。然后你可以同样拿这个假用户走一遍 item_based_predict比较两种算法在冷启动下的表现差异。这个测试过程我每次拆新的推荐项目都会强制走一遍因为它能在五分钟内暴露相似度矩阵是否可行、兜底策略是否生效、评分尺度是否统一比直接跑完整评估更快。希望这个验证习惯也能帮到你少走一些弯路。本文还有配套的精品资源点击获取

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号