简介:基于Python和协同过滤算法的电影推荐系统,是一份面向毕业设计的高分完整项目资源,适合具备Python和Web开发基础的学生学习。项目采用Django框架与MySQL数据库,设计管理员、用户两种角色,管理员可管理用户、电影分类、电影信息、电影评分和系统,用户则通过协同过滤算法获得个性化电影推荐。资源包共690个文件,大小21.8MB,包含Python源码、Vue前端组件、JavaScript脚本、CSS样式、SQL数据库脚本、万字论文文档、MP4演示视频和配置文件等。压缩包内含可运行的完整源码、论文、视频演示和数据库,评审分95分以上,可直接运行。通过学习可掌握协同过滤推荐算法实现、Django+Vue前后端开发及数据库设计。目前已有58人学习下载,适合作为毕业设计或项目实战参考。
1. 协同过滤电影推荐系统:大作业刚需,也是入门推荐系统最短的路径
每年课程设计和大作业季,总有人到处问:有没有一个能跑、能讲、能写论文的推荐系统项目?这个基于 Python 和协同过滤算法的电影推荐系统资源包,就是奔着这个问题去的。它包含完整源码、万字论文、MySQL 数据库脚本、视频演示和配套文档,装好环境导入数据就能跑,适合拿去做 Python 课程设计、毕业设计,或者作为推荐系统入门的第一个完整项目。它的核心不是调一个现成 API,而是从评分数据出发,自己实现用户相似度计算、评分预测和 TopN 推荐——算法全程透明、可讲解、可改参数,这才是大作业最需要的东西。
2. 协同过滤的两种算路:UserCF 与 ItemCF 的选型逻辑与计算细节
2.1 协同过滤的基本假设与评分矩阵
协同过滤(Collaborative Filtering)的核心假设只有一句话:过去行为相似的人,未来偏好也相似;或者,用户喜欢过的物品的相似物品,用户也会喜欢。整个系统围绕一张用户-物品评分矩阵展开,行是用户,列是电影,单元格是评分。
import pandas as pd import numpy as np ratings = pd.read_csv('ratings.csv') # 构造用户-电影评分矩阵,缺失值填 0 rating_matrix = ratings.pivot_table( index='user_id', columns='movie_id', values='rating', fill_value=0 ) print(rating_matrix.shape)这段代码把原始的三列评分表(user_id、movie_id、rating)重排成二维矩阵。fill_value=0表示用户没看过的电影记为 0,而不是 NaN,原因是在后续相似度计算中,NaN 会被很多距离公式直接跳过,导致计算结果不稳定。矩阵的行列数就是用户数和电影数,这个 shape 直接决定了算法的时间复杂度,后面做数据规模评估时最先看它。
2.2 UserCF 与 ItemCF 的选型对比
UserCF(基于用户的协同过滤)找的是「和我口味相似的人」,把相似用户看过的电影推荐给我。ItemCF(基于物品的协同过滤)找的是「和我看过的电影相似的其他电影」,把相似电影推荐给我。两者各有适用场景,不能拍脑袋选。
| 维度 | UserCF | ItemCF |
|---|---|---|
| 适用场景 | 用户少、物品多、用户兴趣变化快的新闻/短视频 | 物品数量相对稳定、用户量大的电商/电影 |
| 冷启动表现 | 新用户无行为,无法计算相似用户 | 新电影无评分,无法计算相似物品 |
| 可解释性 | 弱,「和你相似的人还看了…」 | 强,「因为你看过《XX》,推荐《YY》」 |
| 计算复杂度 | 用户两两相似,用户量大时开销高 | 物品两两相似,物品量大时开销高 |
| 实时性 | 用户行为变化快,需频繁更新相似度 | 物品相似度相对稳定,可离线计算 |
电影推荐场景里我一般偏 ItemCF,因为电影数量相对固定、用户数量远大于电影数量,ItemCF 的相似度矩阵可以离线算好存库,线上只做查表和聚合。但这个项目源码里两种算法都实现了,切换只改一个配置项,正好可以拿同一份数据做对比实验,论文里也多一个分析点。
2.3 相似度计算、评分预测与 TopN 生成
相似度计算是协同过滤的核心环节,常见的有皮尔逊相关系数、余弦相似度和调整余弦相似度。电影评分场景我用皮尔逊比较多,因为它能消除用户打分尺度差异——有的人习惯打 4 分以下,有的人动不动就 5 分,皮尔逊先把各自均值中心化再算相关性,正好规避这个偏差。
def pearson_sim(vec_a, vec_b): # 只取两个用户都评过分的电影 common = (vec_a > 0) & (vec_b > 0) if common.sum() < 2: return 0.0 a = vec_a[common] b = vec_b[common] if a.std() == 0 or b.std() == 0: return 0.0 return np.corrcoef(a, b)[0, 1]common.sum() < 2这个条件是血泪经验,只有一条共同评分时相关系数恒为 1 或 -1,看起来相似度极高,实际毫无统计意义,必须过滤。a.std() == 0处理的是某个用户给所有共同电影打了相同分数的情况,此时皮尔逊公式分母为 0,会出现 NaN,等于 0 是最稳妥的兜底。
预测评分采用加权平均公式:目标用户对电影 i 的预测分等于相似用户对 i 的评分按相似度加权求和,再除以相似度之和。
def predict_rating(user_vec, movie_idx, sim_users): total_sim = 0.0 total_score = 0.0 for sim_user, sim_score in sim_users: if sim_user[movie_idx] > 0: total_sim += abs(sim_score) total_score += sim_score * sim_user[movie_idx] return total_score / total_sim if total_sim > 0 else 0这里取abs(sim_score)是因为负相似度用户虽然口味相反,但他们的评分对预测仍然有参考价值,直接约掉会丢失信息。项目源码里 TopN 生成就是在全部未看过的电影上跑预测函数,按预测分降序取前 N 个。还有一个容易被忽略的细节:预测分计算时要排除目标用户已经看过的电影,否则推荐列表里会出现用户刚打完分的片子,属于低级但常见的错误。
3. 源码与数据库拆解:评分矩阵怎么存、推荐接口怎么出
3.1 目录结构与文件职责
拿到资源包先别急着跑,花十分钟把目录结构理清楚,后面排错会省很多时间。典型结构分四块:算法核心模块、数据处理脚本、Web 接口层、论文与文档。
movie_recommend/ ├── cf/ │ ├── __init__.py │ ├── similarity.py # 相似度计算 │ ├── recommend.py # 推荐生成 │ └── config.py # 算法参数配置 ├── data/ │ ├── movies.csv # 电影信息 │ ├── ratings.csv # 评分数据 │ └── init_db.sql # 建库建表脚本 ├── server/ │ ├── app.py # Flask 接口 │ └── db.py # 数据库连接 ├── docs/ │ ├── 论文.docx │ └── 使用说明.pdf └── requirements.txt3.2 相似度模块与推荐模块的实现要点
相似度模块是整个项目的算法心脏。源码里同时实现了皮尔逊和余弦两种相似度,通过config.py里的SIM_METHOD切换。推荐模块承接相似度矩阵,生成每个用户的 TopN 列表,并把结果写回数据库,减少重复计算。
# config.py SIM_METHOD = 'pearson' # 可选 'pearson' / 'cosine' TOPN = 10 # 推荐数量 MIN_COMMON = 2 # 最小共同评分数 NEIGHBOR_SIZE = 20 # 最近邻数量 USE_ITEM_CF = True # True 用 ItemCF,False 用 UserCF这几个参数决定推荐效果。MIN_COMMON太小会产生虚假高相似度,太大则稀疏矩阵下找不到足够邻居;NEIGHBOR_SIZE控制参与预测的用户数,20 是经验和效果平衡点,太小预测偏差大,太大引入噪声。答辩时老师问「参数怎么定的」,能答出这一层基本就过关了。
# recommend.py 核心片段 def generate_topn(ratings_df, k=TOPN): # 计算物品相似度矩阵(离线,只算一次) item_sim = build_item_similarity(ratings_df) result = [] for user_id, group in ratings_df.groupby('user_id'): watched = set(group['movie_id']) scores = {} for movie_id in item_sim: if movie_id in watched: continue scores[movie_id] = weighted_score(user_id, movie_id, item_sim) top = sorted(scores.items(), key=lambda x: x[1], reverse=True)[:k] result.append((user_id, top)) return result3.3 数据库表设计:三张核心表
数据库是这个项目的数据底座,也是论文里「系统设计」章节的主要素材。三张表分别是用户表、电影表、评分表,评分表是核心,外键关联另外两张表。
CREATE TABLE users ( user_id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL, gender CHAR(1), age INT ); CREATE TABLE movies ( movie_id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(200) NOT NULL, genres VARCHAR(100) ); CREATE TABLE ratings ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, movie_id INT NOT NULL, rating DECIMAL(2,1) NOT NULL, timestamp INT, FOREIGN KEY (user_id) REFERENCES users(user_id), FOREIGN KEY (movie_id) REFERENCES movies(movie_id), UNIQUE KEY uk_user_movie (user_id, movie_id) );评分表最值得说的是UNIQUE KEY uk_user_movie,它保证同一用户对同一电影只能有一条评分记录。数据清洗时如果没有这个约束,重复导入会直接污染相似度计算结果。DECIMAL(2,1)用来存 0.0 到 5.0 的评分,比 FLOAT 更精确,也避免浮点误差。
3.4 数据流:从 CSV 到推荐结果
完整数据流分成四条链路:初始化链路把 CSV 灌进 MySQL;离线计算链路读取评分表算相似度矩阵;在线链路接收用户请求,查库、聚合、返回推荐列表;反馈链路把用户新评分写回库,触发增量更新。理解这条链路的意义在于,所有排错都能按链路分段定位——数据没进去先查导入脚本,推荐结果为空先查相似度矩阵,接口超时先查是否每次请求都在重算相似度。
数据库的增删改查在这个项目里覆盖得很全:INSERT 是评分导入,SELECT 是推荐查询,DELETE 是用户注销清理,UPDATE 是评分修正。论文的「数据库设计」章节直接对着这三张表写,再画一张 ER 图就完整了。
4. 从零跑通这个系统:环境、建库、启动与接口调用
4.1 环境准备与依赖安装
先交代运行环境,我在 Ubuntu 和 Windows 上都跑过,行为一致。Python 版本建议 3.8 到 3.10,太新的 3.12 某些依赖可能还没跟上。依赖清单在 requirements.txt 里,核心是 Flask、pandas、numpy、mysql-connector-python。
# 创建虚拟环境(强烈建议,别直接装到全局) python3 -m venv venv source venv/bin/activate # Windows 下用 venv\Scripts\activate # 安装依赖 pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple4.2 MySQL 建库与数据导入
数据库部分可能是整个项目第一个翻车点。先用 init_db.sql 建库建表,再导入 CSV 数据。MySQL 8.0 默认认证插件是 caching_sha2_password,老版本的 mysql-connector 连不上,表现为报错Authentication plugin 'caching_sha2_password' cannot be loaded,解决方法是升级连接库。
mysql -u root -p < data/init_db.sql mysql -u root -p movie_db -e " LOAD DATA LOCAL INFILE 'data/movies.csv' INTO TABLE movies FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\"' IGNORE 1 LINES; "IGNORE 1 LINES跳过 CSV 表头。OPTIONALLY ENCLOSED BY '\"'处理电影标题里带逗号的情况,比如「The Godfather, Part II」,没有这行会在导入时把字段拆散,数据错位很难发现。评分数据量大,LOAD DATA 比逐条 INSERT 快一个数量级以上,一万条评分几秒就进去。
4.3 启动推荐服务与接口调用
数据就绪后,启动 Flask 服务,测试推荐接口。
python server/app.py # 输出 Running on http://127.0.0.1:5000curl "http://127.0.0.1:5000/api/recommend?user_id=1&topn=10"返回的 JSON 结构是电影 ID、标题、预测评分三元组。接口层做了两件容易被忽视的事:一是给每个用户加了推荐缓存,避免重复请求反复计算;二是做了 UserCF 和 ItemCF 的双接口,/api/recommend?cf_type=item可以切换算法,方便对比实验。
接口时序也要注意:第一次请求会触发相似度矩阵计算,耗时较长,这是正常的,后续请求走缓存就会快很多。如果嫌慢,可以提前跑一次预热脚本,把相似度矩阵和 TopN 结果全部预生成入库。
5. 避坑实录:六个必踩的坑与对应排查手段
5.1 现象:推荐结果为空列表
原因:评分矩阵极度稀疏,目标用户没有足够近邻。新用户只看过一两部电影,皮尔逊计算时common.sum() < 2直接返回 0,一个邻居都找不到。这是协同过滤的冷启动问题,不是代码 bug。
解决:对没有历史行为的用户,退化为热门榜推荐——按全局评分次数和平均分综合排序补足 TopN。源码里我已经写了fallback逻辑,配置项COLD_START_FALLBACK = True控制。自己复现时可以在系统里注册一个「新用户」账号验证这个分支。
5.2 现象:相似度矩阵大量为 0,推荐结果跟随机差不多
原因:评分数据没有对齐,或者数据导入时 user_id 和 movie_id 被当成字符串处理。pandas 里一列是 int 一列是 string,pivot_table 时会拆出重复列,相似度全部算错。
解决:导入后立刻做一次类型检查,print(ratings.dtypes)确认两列都是数值类型。再检查缺失值,ratings.isnull().sum(),评分表里有 NaN 说明原数据有空行,需要 dropna。这两步写成一个check_data()函数,每次跑数据前先执行,五分钟能省半天排查时间。
5.3 现象:MySQL 连接报错getaddrinfo failed或认证插件错误
原因:连接参数里 host 写成了localhost,在某些系统上解析到 IPv6::1,而 MySQL 只监听 IPv4;另一个高频原因是 MySQL 8 默认认证插件与旧版连接库不兼容。
解决:host 统一写127.0.0.1,显式指定 TCP 连接。认证问题用pip install --upgrade mysql-connector-python升级驱动。如果还不行,登录 MySQL 执行ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '密码';这是兼容性解药,但注意 MySQL 8.4 起已移除该插件,最好还是升级驱动。
5.4 现象:导入后中文电影标题乱码
原因:CSV 文件编码与 MySQL 表字符集不一致。CSV 是 UTF-8,表却是 latin1,或者连接时没指定字符集。
解决:建库时指定CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,连接字符串加上charset='utf8mb4'。CSV 本身若是 GBK 编码(Windows 导出的老文件),用iconv -f GBK -t UTF-8 movies.csv > movies_utf8.csv先转码再导入。utf8mb4 和 utf8 的差异也要留意,utf8mb4 才支持完整的 Unicode 字符。
5.5 现象:推荐结果永远同一批,换用户差别不大
原因:热门电影评分数量多,在加权公式里天然占优,ItemCF 算出来的相似物品大量指向热门片。这是协同过滤的「马太效应」,所有推荐系统都会遇到,只是本项目里更明显。
解决:评分归一化时引入流行度惩罚,score = raw_score * (1 - popularity_ratio),或者相似度计算时对热门物品降权。源码里POPULARITY_PENALTY参数可以调节惩罚强度,从 0 调到 0.3 左右效果会比较明显,也适合写进论文做超参实验。
5.6 现象:升级 Python 后 numpy 装不上
原因:Python 3.12 移除了 distutils,老版本 numpy 无法编译;或者 pip 版本太旧,解析不了新版依赖。
解决:项目限定 Python 3.8-3.10,在这个范围内重装即可。如果系统里装了多个 Python 版本,务必确认python --version指向建虚拟环境时的解释器,我见过太多人虚拟环境里跑着全局 Python 的翻车现场。安装 numpy 用pip install numpy指定版本,例如pip install "numpy>=1.24,<2.0"。
6. 把项目变成自己的:参数调优、效果验证与论文撰写技巧
推荐系统项目的答辩重点永远是「你是怎么调的、怎么证明它有效」。算法是教材里都有的东西,但参数是你调的,实验是你做的,这部分谁也质疑不了。
先做参数调优。核心实验围绕四个参数展开:MIN_COMMON从 1 到 5 变化,观察推荐覆盖率;NEIGHBOR_SIZE从 5 到 50 变化,观察预测准确度;TOPN从 5 到 30 变化,观察推荐列表多样性;SIM_METHOD在皮尔逊和余弦之间切换,做对比。每次只动一个参数,其他固定,结果记成表格,这就是论文「实验结果分析」章节的原始素材。
def evaluate(predicted, actual): # 简单评估:预测评分与真实评分的 RMSE pred = np.array([p for p, a in predicted if a > 0]) real = np.array([a for p, a in predicted if a > 0]) return np.sqrt(np.mean((pred - real) ** 2))评估要分清离线指标和在线指标。离线用 RMSE、MAE 评估评分预测准确度,用 Precision@N、Recall@N 评估 TopN 推荐质量——做法是把评分数据按时间切分,前 80% 训练,后 20% 测试,测试集里用户真正看过的电影如果出现在推荐列表里就算命中。这个方法论文里必写,最好做成图表。
如果导师问「为什么推荐结果里没有最新电影」,这正好引出 ItemCF 的一个已知短板:只看评分矩阵时,没有评分记录的电影无法参与相似度计算。源码里留了一个基于电影类型的cold_start_movies()函数,对新入库电影按类型匹配相似电影,属于内容推荐和协同过滤的简单混合,可以作为论文的改进方向写两段。
论文写作我建议直接对照资源包里的万字论文改结构,核心章节顺序:绪论(背景与意义)、相关技术(协同过滤原理)、系统设计(架构图和数据库表)、核心实现(相似度计算与推荐生成代码)、系统测试(界面和接口截图)、总结与展望。不要照抄,保留算法原理部分,把实验数据、截图、参数表换成自己跑出来的,用 Word 的「审阅-修订」模式逐段改,降重和保质量同时完成。
从那以后我每次跑推荐系统项目,都会强制自己先做一次数据校验、再跑一行代码验证——先check_data()再generate_topn(),这个习惯帮我挡掉了至少十次无效调试。希望这个资源包能让你少走我走过的弯路,把时间花在真正有产出的实验和论文上。
本文还有配套的精品资源,点击获取