简介这份资源面向计算机相关专业毕业生与推荐系统入门学习者提供一套可直接运行的电影推荐系统完整实现同时覆盖基于用户与基于物品两种协同过滤算法适合用作毕业设计参考或课程项目实践。压缩包共83个文件、约8.41MB以Python源码与编译缓存为主辅以HTML模板、CSV数据集、数据库文件及图片素材另含论文PDF、说明文档与爬虫脚本技术栈为Python3.7、Django2.2.1搭配MySQL或SQLite前端使用Bootstrap3数据源自MovieLens。已有10613人学习下载说明其参考价值得到较多验证。读者可获得完整项目结构、算法实现代码、数据库文件、论文与详细技术文档便于快速理解推荐流程、复现系统并在此基础上做二次开发或撰写自己的设计说明。1. 一套能跑通的协同过滤毕设为什么我建议你先看数据流再碰模型毕业设计里做电影推荐系统最怕的不是算法写不出来而是环境装不上、数据对不齐、推荐结果全是同一批片子。这套基于 Python 3.7 Django 2.2.1 MySQL 的协同过滤电影推荐系统本质上是一个「用户—评分—电影」三表驱动的 Web 应用后端用 Django 组织用户、影片、评分和推荐接口MySQL 存原始交互数据协同过滤负责把评分矩阵转成推荐列表。它适合正在做毕设、需要一套能演示、能答辩、能二次改动的完整工程的人也适合想搞清楚推荐系统从数据表到页面到底怎么串起来的开发者。下面按「资源是什么、怎么落地、坑在哪」的顺序拆开讲。2. 协同过滤在这套 Django 工程里到底怎么落地从评分矩阵到推荐接口2.1 为什么选 UserCF 而不是一上来就上深度学习这套工程的技术栈是 Python 3.7 Django 2.2.1 MySQL协同过滤是核心推荐逻辑。很多毕设一上来就想套神经网络结果数据量只有几千条评分模型训不动答辩时也讲不清。协同过滤的优势在于它只依赖用户对电影的评分行为不需要电影内容特征工程实现路径短结果可解释。常见做法是先用 UserCF 跑通「找相似用户 → 加权评分 → 生成推荐」这条链路再考虑是否加 ItemCF 做对比实验。UserCF 的核心是用户相似度矩阵。假设有 m 个用户、n 部电影评分表就是 m×n 的稀疏矩阵。工程里一般用余弦相似度或皮尔逊相关系数计算用户之间的相似度然后取 Top-K 相似用户把他们评过、目标用户没看过的电影按相似度加权打分排序。这里有个选型细节余弦相似度对评分尺度不敏感皮尔逊会做中心化处理适合评分偏置明显的场景。毕设数据量小两种都能跑但建议在代码里留一个相似度函数开关方便后面做对比。2.2 数据表怎么设计三张核心表撑起推荐链路MySQL 里至少要有用户表、电影表、评分表。评分表是协同过滤的燃料字段一般包括用户 ID、电影 ID、评分值、时间戳。下面是一个可抄作业的建表片段字段名按 Django 模型习惯来方便后面 ORM 映射。CREATE TABLE rating ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL, movie_id BIGINT NOT NULL, score DECIMAL(3,1) NOT NULL, -- 评分常见 0.5~5.0 created_at DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_user_movie (user_id, movie_id), -- 防止同一用户重复评分 KEY idx_movie (movie_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这段建表语句的关键在UNIQUE KEY uk_user_movie它保证一个用户对一部电影只有一条评分记录否则相似度计算时会出现重复加权推荐结果会偏。idx_movie是为了按电影聚合评分时走索引。评分字段用DECIMAL(3,1)而不是整数是因为很多电影评分数据集是半星制整数会丢精度。时间戳字段在毕设里常被忽略但后面做「最近评分权重」或冷启动分析时会用到。2.3 推荐接口的代码骨架从相似用户到推荐列表Django 里一般把推荐逻辑放在recommend.py或 service 层视图只负责取参数和返回 JSON。下面是一段 UserCF 的核心骨架依赖 numpy 和 pandasPython 3.7 下建议锁 numpy 1.16.x、pandas 0.25.x避免高版本不兼容。import numpy as np import pandas as pd def user_cf_recommend(user_id, top_k20, top_n10): # 1. 从 MySQL 拉评分数据实际工程用 Django ORM 或 pymysql ratings pd.DataFrame(list(Rating.objects.values(user_id, movie_id, score))) matrix ratings.pivot(indexuser_id, columnsmovie_id, valuesscore).fillna(0) # 2. 余弦相似度行向量点积除以模长 norm np.linalg.norm(matrix.values, axis1, keepdimsTrue) norm[norm 0] 1e-9 # 防止除零 sim matrix.values.dot(matrix.values.T) / (norm * norm.T) sim_df pd.DataFrame(sim, indexmatrix.index, columnsmatrix.index) # 3. 取 Top-K 相似用户排除自己 similar_users sim_df[user_id].drop(user_id).nlargest(top_k) # 4. 加权评分相似度 × 评分累加后归一化 scores {} for other_user, similarity in similar_users.items(): for movie_id, score in matrix.loc[other_user].items(): if score 0 and matrix.loc[user_id, movie_id] 0: scores[movie_id] scores.get(movie_id, 0) similarity * score ranked sorted(scores.items(), keylambda x: x[1], reverseTrue)[:top_n] return ranked这段代码的逻辑分四步先把评分表透视成用户—电影矩阵缺失值填 0再用余弦相似度算用户两两相似度norm置极小值是为了防止全零行导致除零然后取目标用户的 Top-K 相似用户drop(user_id)把自己排除最后只对「相似用户评过、目标用户没评过」的电影做加权累加。参数top_k控制参与推荐的邻居数量太小推荐不稳定太大计算量上升且噪声变多毕设数据量下 20 到 40 比较常见top_n是返回条数页面展示一般 10 条够用。2.4 Django 视图和路由怎么接让推荐结果能出现在页面上推荐函数写完后要在 Django 里暴露成接口。常见做法是写一个recommend_view从 session 或 URL 参数取user_id调用上面的函数返回 JSON。路由配置在urls.py里加一行即可。这里要注意 Django 2.2.1 的path写法不要混用 1.x 的url正则风格否则启动时报错。from django.http import JsonResponse from .recommend import user_cf_recommend def recommend_view(request): user_id request.GET.get(user_id) if not user_id: return JsonResponse({code: 400, msg: 缺少 user_id}) try: result user_cf_recommend(int(user_id), top_k20, top_n10) return JsonResponse({code: 200, data: [{movie_id: m, score: round(s, 3)} for m, s in result]}) except Exception as e: return JsonResponse({code: 500, msg: str(e)})视图层只做参数校验和异常兜底推荐逻辑不写在视图里是为了后面换算法时不动路由。JsonResponse返回的score做了三位小数处理前端展示更干净。异常捕获里直接返回str(e)在毕设演示阶段够用但正式一点应该记日志、返回统一错误码避免把数据库细节暴露到前端。3. 环境搭建与数据准备Python 3.7 Django 2.2.1 MySQL 的版本锁死策略3.1 为什么这套组合必须锁版本Python 3.7 和 Django 2.2.1 是一组老搭档Django 2.2 是 LTS 版本官方支持到 2022 年Python 3.7 也在同期停止维护。这套组合在毕设场景里仍然常见原因是很多学校的镜像源、教程和依赖包都围绕这个版本。问题在于如果你直接pip install django装到的是 4.x 或 5.xdjango.conf.urls.url被移除、MIDDLEWARE_CLASSES改名工程会直接起不来。所以第一步不是写代码而是把版本钉死。常见做法是建一个requirements.txt把核心依赖写死再用虚拟环境隔离。下面是一份可用的依赖清单版本号按 Python 3.7 的兼容区间来。python3.7 -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate pip install -r requirements.txtDjango2.2.1 mysqlclient1.4.6 numpy1.16.6 pandas0.25.3mysqlclient在 Windows 上编译经常翻车血泪经验是优先用pip install mysqlclient的预编译 wheel装不上再换pymysql并在__init__.py里打补丁。numpy和pandas的版本不要贪新1.16 和 0.25 是 Python 3.7 下比较稳的组合高版本会要求更高 Python装到一半报Requires-Python 3.8很常见。3.2 MySQL 连接配置与 Django 数据库迁移Django 的数据库配置在settings.py的DATABASES里MySQL 需要填ENGINE、NAME、USER、PASSWORD、HOST、PORT。字符集建议显式写utf8mb4否则电影名里的特殊字符会存成问号。DATABASES { default: { ENGINE: django.db.backends.mysql, NAME: movie_recommend, USER: root, PASSWORD: your_password, HOST: 127.0.0.1, PORT: 3306, OPTIONS: {charset: utf8mb4}, } }配置完成后执行python manage.py makemigrations和python manage.py migrateDjango 会自动建出auth_user等内置表。评分表和电影表如果自己写 SQL 建要确保字段类型和模型定义一致否则 ORM 查询时会报字段不匹配。迁移前先确认 MySQL 服务已启动、库已创建Unknown database是新手最常见的第一个报错。3.3 评分数据怎么灌从公开数据集到 MySQL 的清洗步骤协同过滤需要评分数据毕设常用的是 MovieLens 这类公开数据集格式一般是userId::movieId::rating::timestamp或 CSV。灌数据前要做三件事字段映射、去重、空值检查。下面是一段用 pandas 清洗后写入 MySQL 的脚本骨架。import pandas as pd from sqlalchemy import create_engine # 读取原始评分文件分隔符按数据集实际格式调整 df pd.read_csv(ratings.csv, sep,, names[user_id, movie_id, score, ts]) df df.drop_duplicates(subset[user_id, movie_id]) # 去重 df df.dropna(subset[user_id, movie_id, score]) # 去空 df[score] df[score].clip(0.5, 5.0) # 评分截断到合理区间 engine create_engine(mysqlpymysql://root:your_password127.0.0.1:3306/movie_recommend?charsetutf8mb4) df[[user_id, movie_id, score]].to_sql(rating, engine, if_existsappend, indexFalse)这段脚本的关键在drop_duplicates和clip。重复评分会让相似度计算偏斜评分越界会让归一化失真。if_existsappend表示追加写入第一次灌数据前要确保表已建好。数据量大的时候to_sql会慢可以加chunksize1000分批写避免一次性占满内存。4. 避坑与排查这套老版本工程最容易翻车的五个地方4.1 现象ModuleNotFoundError: No module named MySQLdb原因Django 2.2.1 默认用MySQLdb驱动但mysqlclient没装成功或者装到了全局环境而不是虚拟环境。解决先确认pip list里有mysqlclient没有就重装Windows 上编译失败可以换pymysql在项目__init__.py里加import pymysql; pymysql.install_as_MySQLdb()。注意pymysql版本也要锁0.9.x 比较稳。4.2 现象推荐结果全是热门电影个性化几乎为零原因评分矩阵太稀疏Top-K 相似用户里很多人只评过几部热门片加权后热门片得分天然高。解决在相似度计算前过滤掉评分数量少于阈值的用户比如min_ratings5或者在加权评分时除以相似度之和做归一化而不是直接累加。另一个常见原因是top_k设得太大邻居里混进了不相似的用户把top_k降到 20 以内往往能改善。4.3 现象页面能打开但推荐接口返回 500日志里是KeyError原因matrix.loc[user_id]找不到该用户说明这个用户在评分表里没有任何记录冷启动用户直接进了推荐函数。解决视图层先判断用户是否有评分记录没有就返回热门电影兜底不要硬算相似度。冷启动是推荐系统的经典问题毕设里用「热门榜兜底」是最省事的做法答辩时也能讲清楚。4.4 现象makemigrations报No changes detected原因Django 只识别INSTALLED_APPS里注册过的 app 下的models.py如果模型写在别的目录或者 app 没注册迁移命令就看不到。解决检查settings.py的INSTALLED_APPS是否包含你的 app 名模型类是否继承models.Model字段是否有拼写错误。另一个隐蔽原因是 app 目录下缺少__init__.pyPython 不认为它是包。4.5 现象中文电影名存进 MySQL 变成问号原因数据库或表的字符集不是utf8mb4或者连接时没指定字符集。解决建库时用CREATE DATABASE movie_recommend CHARACTER SET utf8mb4;DATABASES的OPTIONS里加charset: utf8mb4已有表可以用ALTER TABLE movie CONVERT TO CHARACTER SET utf8mb4;转换。注意utf8在 MySQL 里最多三字节存不了部分特殊字符统一用utf8mb4省心。5. 进阶验证用离线指标判断推荐到底有没有用5.1 别只看页面能不能跑先算一组离线指标很多毕设演示时只展示「推荐出来了」但推荐质量好不好没有依据。协同过滤常用的离线指标是 PrecisionK、RecallK 和覆盖率。做法是把评分数据按时间或随机切分成训练集和测试集用训练集算相似度和推荐列表再和测试集里用户实际评过高分的电影做比对。下面是一个简化的评估骨架。def evaluate(train_df, test_df, k10): hits, total 0, 0 for user_id in test_df[user_id].unique(): recommended set(m for m, _ in user_cf_recommend(user_id, top_k20, top_nk)) actual set(test_df[test_df[user_id] user_id][movie_id]) hits len(recommended actual) total len(actual) return hits / total if total else 0 # 简化版 RecallK这段评估只算了召回实际写论文时建议同时算准确率和覆盖率。参数k一般取 10 或 20和页面展示条数对齐。注意训练集和测试集要按用户切分不能随机打散后同一用户的评分同时出现在两边否则指标会虚高答辩时被问到就尴尬了。5.2 一个我每次都会走的检查习惯这套工程跑通之后我习惯在交付前强制走一遍「冷启动用户 → 新电影 → 重复评分 → 空推荐」四个边界场景每个场景手动调一次接口看返回。冷启动用户看有没有热门兜底新电影看相似度矩阵里全零列会不会导致除零重复评分看唯一索引有没有生效空推荐看前端会不会白屏。这四个场景覆盖了协同过滤在毕设演示里最容易被问到的边界提前走一遍能省掉答辩现场的后悔药。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?