资讯详情

Django协同过滤推荐系统实战:冷启动+实时评分+后台管理

📅 2026/10/25 15:01:08 | 华诺云谱 👁 阅读
Django协同过滤推荐系统实战:冷启动+实时评分+后台管理
简介这是一套基于协同过滤算法的电影推荐系统毕业设计实现面向Python与Web开发初学者及高校计算机专业学生解决个性化推荐系统从理论到工程落地的学习痛点。资源完整复现了基于用户和基于物品的双路协同过滤逻辑采用Django 2.2.1框架构建前后端后端支持MySQL与SQLite双数据库前端集成Bootstrap 3响应式界面并预置MovieLens公开数据集及配套爬虫脚本。压缩包共83个文件含28个核心Python模块如userCF、itemCF、数据填充与推荐计算脚本、16个HTML模板页、4个CSV原始数据文件、1个PDF论文、1个SQLite3数据库及详细README与requirements依赖说明整体大小8.41MB。已有10608人学习下载读者可直接运行调试、查阅完整技术文档、复用爬虫与数据初始化脚本并参考附带的毕业论文与目录结构组织方式快速掌握推荐系统工程化开发全流程。1. 这不是又一个“Hello World”推荐系统它真能跑通冷启动实时评分后台管理闭环专治毕设答辩前一周还在调不通 MySQL 连接的焦虑你手头那份写着“基于协同过滤的电影推荐系统”的毕设文档是不是已经卡在第三章——数据库建表失败、Django 启动报django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module、用户评分提交后推荐结果永远是《阿凡达》别慌。这不是理论空谈而是一套完整落地过、可直接 clone、改完 settings.py 就能跑通的 Django 2.2.1 Python 3.7 MySQL 实战工程。它不依赖任何云服务或第三方 API所有推荐逻辑写在recommend/views.py里用纯 SQL Pandas 做用户-物品矩阵构建用余弦相似度实现基于用户的协同过滤User-Based CF并内置管理员后台——你能看到每个用户的历史评分、系统生成的 Top-N 推荐列表、甚至手动触发“重新计算相似度矩阵”按钮。适合正在赶毕设 deadline 的本科生也适合想搞懂“协同过滤怎么从公式变成可调试代码”的转行者。它不炫技但每一步都踩在真实部署的坑上MySQL 字符集乱码、Django 2.2.1 对 MySQL 5.7 的驱动兼容性、评分表外键约束导致的 INSERT 失败……这些我都替你翻过车。2. 从零搭起推荐骨架Python 环境、Django 工程结构与 MySQL 数据库三件套实操2.1 Python 3.7 环境隔离与依赖精准安装为什么不用 conda 而坚持 venv很多同学用pip install django全局安装结果发现 Django 版本冲突、MySQL 驱动报错。协同过滤对环境版本极其敏感Django 2.2.1 明确要求mysqlclient1.3.13,2.0而PyMySQL在该版本下无法正确处理DateTimeField的时区解析。我坚持用venv不是 conda的原因很实际毕业答辩现场演示时评审老师可能用的是 Windows 10 默认 CMDvenv创建的Scripts/activate.bat兼容性远高于 conda 的 shell 脚本。# 在项目根目录执行注意不要用管理员权限 python -m venv venv_rec venv_rec\Scripts\activate.bat # Windows # venv_rec/bin/activate # macOS/Linux # 安装指定版本关键 pip install --upgrade pip pip install django2.2.1 pip install mysqlclient1.4.6 # 注意不是最新版1.4.6 是 Django 2.2.1 最稳的搭档 pip install pandas0.24.2 numpy1.16.4 scikit-learn0.20.3提示mysqlclient1.4.6必须用源码编译安装。Windows 下需提前装好 Visual Studio Build Tools2015 或 2017 版本否则会报Microsoft Visual C 14.0 is required。Mac 用户用brew install mysql-client再pip install mysqlclientLinux 用户先sudo apt-get install python3-dev default-libmysqlclient-dev。2.2 Django 2.2.1 工程初始化四步完成核心配置Django 2.2.1 的settings.py有两处必须改否则连数据库都连不上INSTALLED_APPS加入自定义 app项目含movies电影模型、ratings评分记录、recommend推荐逻辑三个核心 app必须显式注册# settings.py INSTALLED_APPS [ django.contrib.admin, django.contrib.auth, django.contrib.contenttypes, django.contrib.sessions, django.contrib.messages, django.contrib.staticfiles, movies, # ← 新增 ratings, # ← 新增 recommend, # ← 新增 ]DATABASES配置必须用mysqlclient驱动不要抄网上教程用pymysqlDjango 2.2.1 会报DatabaseWrapper错误# settings.py DATABASES { default: { ENGINE: django.db.backends.mysql, # ← 引擎固定 NAME: movie_recommender, # ← 数据库名需提前创建 USER: root, # ← MySQL 用户名 PASSWORD: your_password, # ← 密码明文毕设可接受 HOST: 127.0.0.1, # ← 本地连接 PORT: 3306, # ← 默认端口 OPTIONS: { init_command: SET sql_modeSTRICT_TRANS_TABLES, charset: utf8mb4, # ← 关键支持 emoji 和生僻字 }, TEST: { CHARSET: utf8mb4, COLLATION: utf8mb4_unicode_ci, } } }参数说明charset: utf8mb4解决中文电影名存入乱码init_command强制严格模式避免INSERT时因字段为空被静默截断TEST配置确保单元测试也能用相同字符集。2.3 MySQL 数据库建模五张表支撑协同过滤全流程协同过滤不是只存用户和电影它需要行为日志、关系映射、缓存中间结果。本系统共 5 张表全部用 Django ORM 自动生成python manage.py makemigrations python manage.py migrate但你必须理解每张表的物理意义表名字段关键作用协同过滤中角色movies_movieid,title,genres,year电影元数据物品集合Item Setauth_userid,username,emailDjango 自带用户表用户集合User Setratings_ratinguser_id,movie_id,rating,timestamp用户对电影的 1-5 分评分核心评分矩阵Ru,irecommend_usersimilarityuser1_id,user2_id,similarity用户间余弦相似度预计算相似度缓存表加速推荐recommend_recommendationuser_id,movie_id,score,rank每个用户的 Top-10 推荐结果推荐结果持久化供前端调用注意ratings_rating表的user_id和movie_id是外键指向auth_user.id和movies_movie.id。Django 迁移时会自动加ON DELETE CASCADE但务必在 MySQL 中确认外键已启用SHOW VARIABLES LIKE foreign_key_checks;应为ON。3. 协同过滤核心实现从评分矩阵到 Top-N 推荐的四步推导与代码落地3.1 构建用户-物品评分矩阵Pandas DataFrame 是最稳的起点协同过滤的第一步是把ratings_rating表里的稀疏数据转成稠密矩阵。很多人直接用scipy.sparse但在 Django 视图里频繁构造稀疏矩阵容易内存溢出。我的做法是用 Pandas 读取全量评分用pivot_table生成 DataFrame再转为 numpy 数组——既保留可读性又便于后续计算。# recommend/utils.py import pandas as pd import numpy as np from ratings.models import Rating def build_user_item_matrix(): 返回 (user_ids, movie_ids, matrix) 三元组 matrix[i][j] 表示 user_ids[i] 对 movie_ids[j] 的评分NaN 表示未评 # 1. 从数据库读取所有评分仅需三列 qs Rating.objects.values(user_id, movie_id, rating) df pd.DataFrame(list(qs)) # 2. 生成唯一用户ID和电影ID列表保证索引连续 user_ids sorted(df[user_id].unique()) movie_ids sorted(df[movie_id].unique()) # 3. pivot_table 构建矩阵自动填充 NaN matrix df.pivot_table( indexuser_id, columnsmovie_id, valuesrating, aggfuncfirst # 防止同一用户对同一电影多次评分取平均 ) # 4. 重索引确保所有用户/电影都在行列中补 NaN matrix matrix.reindex(indexuser_ids, columnsmovie_ids) return user_ids, movie_ids, matrix.values # 返回 numpy.ndarray逻辑说明pivot_table比groupbyunstack更稳定aggfuncfirst防止历史数据中存在重复评分导致聚合错误reindex确保矩阵行列严格对应user_ids/movie_ids列表这是后续用np.nanmean计算用户均值的前提。3.2 用户相似度计算余弦相似度 均值中心化拒绝 raw cosine 玄学原始余弦相似度Raw Cosine对用户评分尺度敏感——一个习惯打 4-5 分的用户和一个只打 1-2 分的用户相似度会虚高。本系统采用 Pearson 相关系数变体先中心化减去用户均分再算余弦。代码封装在recommend/algorithms.py# recommend/algorithms.py from scipy.spatial.distance import cosine import numpy as np def calculate_user_similarity(matrix, user_ids): matrix: (n_users, n_movies) numpy array, 缺失值为 np.nan 返回相似度矩阵 sim_matrix[i][j] user_ids[i] 与 user_ids[j] 的相似度 n_users len(user_ids) sim_matrix np.zeros((n_users, n_users)) # 预计算每个用户的均分跳过 NaN user_means np.nanmean(matrix, axis1) for i in range(n_users): # 获取用户 i 的中心化向量只取非 NaN 位置 vec_i matrix[i] - user_means[i] mask_i ~np.isnan(vec_i) # True 表示该电影有评分 for j in range(i1, n_users): vec_j matrix[j] - user_means[j] mask_j ~np.isnan(vec_j) mask mask_i mask_j # 两人共同评分的电影 if mask.sum() 5: # 共同评分少于 5 部视为无相似性 sim 0.0 else: # 只在共同评分位置上计算余弦 valid_i vec_i[mask] valid_j vec_j[mask] sim 1 - cosine(valid_i, valid_j) # cosine 返回距离1-距离相似度 sim_matrix[i][j] sim sim_matrix[j][i] sim # 对称 return sim_matrix参数说明mask.sum() 5是硬性阈值——协同过滤中两个用户至少共同评过 5 部电影相似度才有统计意义cosine函数来自scipy比手写更鲁棒1 - cosine(...)直接得到 [0,1] 区间相似度。3.3 Top-N 推荐生成加权平均预测 排序截断拒绝“猜你喜欢”黑匣子推荐不是找相似用户然后随机选电影而是对目标用户未评分的每一部电影预测其评分再按预测分排序。预测公式为$$ \hat{r}{u,i} \bar{r}u \frac{\sum{v \in N(u)} sim(u,v) \cdot (r{v,i} - \bar{r}v)}{\sum{v \in N(u)} |sim(u,v)|} $$其中 $N(u)$ 是用户 $u$ 的 Top-K 相似用户K20。代码实现# recommend/views.py def get_top_n_recommendations(user_id, n10): 为 user_id 生成 Top-N 推荐 返回 [(movie_id, predicted_rating, rank), ...] from recommend.algorithms import calculate_user_similarity from movies.models import Movie # 1. 获取全量矩阵和 ID 映射 user_ids, movie_ids, matrix build_user_item_matrix() try: u_idx user_ids.index(user_id) except ValueError: return [] # 用户不存在 # 2. 计算相似度矩阵生产环境应缓存 sim_matrix calculate_user_similarity(matrix, user_ids) # 3. 找出 Top-20 相似用户排除自己 sim_scores sim_matrix[u_idx] top_sim_indices np.argsort(sim_scores)[::-1][1:21] # 跳过第0位自己 # 4. 预测未评分电影的分数 predictions [] user_mean np.nanmean(matrix[u_idx]) for m_idx, movie_id in enumerate(movie_ids): if not np.isnan(matrix[u_idx][m_idx]): # 已评分跳过 continue # 计算分子分母 numerator 0.0 denominator 0.0 for v_idx in top_sim_indices: r_vi matrix[v_idx][m_idx] if np.isnan(r_vi): continue v_mean np.nanmean(matrix[v_idx]) numerator sim_scores[v_idx] * (r_vi - v_mean) denominator abs(sim_scores[v_idx]) if denominator 0: pred user_mean # 无相似用户贡献回退到用户均分 else: pred user_mean numerator / denominator predictions.append((movie_id, pred)) # 5. 按预测分降序取 Top-N predictions.sort(keylambda x: x[1], reverseTrue) top_n predictions[:n] # 6. 补充电影标题供前端显示 movie_titles {m.id: m.title for m in Movie.objects.filter(id__in[x[0] for x in top_n])} result [] for i, (mid, score) in enumerate(top_n): result.append({ movie_id: mid, title: movie_titles.get(mid, 未知电影), predicted_rating: round(score, 2), rank: i1 }) return result关键点numerator / denominator是加权平均的核心denominator 0的兜底逻辑防止除零Movie.objects.filter用id__in批量查询比循环查单个快 10 倍以上。4. 避坑指南Django MySQL 协同过滤项目里90% 的翻车都发生在这五个地方4.1 现象python manage.py migrate报错BLOB/TEXT column used in key specification without a key length原因MySQL 5.7 默认innodb_large_prefixOFF而 Django 2.2.1 的auth_user.username字段是VARCHAR(150)建索引时超出限制。解决登录 MySQL 执行SET GLOBAL innodb_file_formatBarracuda; SET GLOBAL innodb_file_per_tableON; SET GLOBAL innodb_large_prefixON; ALTER TABLE auth_user MODIFY username VARCHAR(150) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;然后删掉migrations/下除__init__.py外所有文件重新makemigrations。4.2 现象Django 后台能登录但ratings_rating表里user_id总是 0原因前端表单提交时user_id字段没传Django ORM 默认用0填充外键。解决在ratings/forms.py中强制绑定当前用户class RatingForm(forms.ModelForm): class Meta: model Rating fields [movie, rating] # 不包含 user 字段 def save(self, commitTrue, userNone): instance super().save(commitFalse) if user: instance.user user # 从视图传入 request.user if commit: instance.save() return instance视图中调用form.save(userrequest.user)。4.3 现象get_top_n_recommendations()执行超时30秒Apache 直接 504原因每次请求都重新计算相似度矩阵而 1000 用户 × 1000 用户的矩阵计算需 O(N²×M) 时间。解决必须加缓存。在settings.py中配置 Redis或 Django 自带的文件缓存CACHES { default: { BACKEND: django.core.cache.backends.filebased.FileBasedCache, LOCATION: /var/tmp/django_cache, # LinuxWindows 用 C:/tmp/django_cache TIMEOUT: 3600, # 缓存 1 小时 } }然后在get_top_n_recommendations开头加cache_key fuser_sim_matrix_{len(user_ids)} sim_matrix cache.get(cache_key) if sim_matrix is None: sim_matrix calculate_user_similarity(matrix, user_ids) cache.set(cache_key, sim_matrix, 3600)4.4 现象中文电影名在 Django Admin 中显示为????原因MySQL 数据库、表、字段三级字符集不一致。CREATE DATABASE时没指定CHARSETutf8mb4。解决-- 删除旧库谨慎先备份 DROP DATABASE movie_recommender; -- 重建时指定字符集 CREATE DATABASE movie_recommender CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- 然后重新 migrate python manage.py migrate4.5 现象mysqlclient安装成功但python manage.py dbshell报Cant connect to local MySQL server原因MySQL 服务未启动或my.ini中bind-address127.0.0.1被注释导致监听0.0.0.0而防火墙阻止了 3306 端口。解决Windows打开「服务」→ 找到MySQL→ 右键「启动」检查my.ini确保bind-address127.0.0.1且未被#注释命令行验证mysql -u root -p能登录即服务正常5. 生产就绪技巧让毕设系统扛住答辩演示流量同时保住你的 GPA5.1 推荐结果预计算 定时任务告别“点击推荐按钮后转圈 10 秒”答辩现场最怕什么评委说“点一下推荐看看”然后你盯着加载动画心跳加速。协同过滤的预测计算必须离线化。Django 自带django-crontab可实现定时任务每天凌晨 2 点批量更新所有活跃用户的 Top-10 推荐# 安装 pip install django-crontab # settings.py INSTALLED_APPS [django_crontab] CRONJOBS [ (0 2 * * *, recommend.cron.update_all_recommendations), # 每天2点执行 ]# recommend/cron.py from django.contrib.auth.models import User from recommend.views import get_top_n_recommendations from recommend.models import Recommendation def update_all_recommendations(): # 只更新近 30 天有评分的用户减少计算量 recent_user_ids set( r.user_id for r in Rating.objects.filter( timestamp__gtetimezone.now() - timedelta(days30) ).values_list(user_id, flatTrue) ) for uid in recent_user_ids: recs get_top_n_recommendations(uid, n10) # 清空旧推荐写入新结果 Recommendation.objects.filter(user_iduid).delete() Recommendation.objects.bulk_create([ Recommendation(user_iduid, movie_idr[movie_id], scorer[predicted_rating], rankr[rank]) for r in recs ])技巧bulk_create比循环save()快 50 倍timedelta(days30)过滤掉沉寂用户让计算量从 1000 降到 200Recommendation模型需加unique_together (user_id, movie_id)防止重复插入。5.2 Django Admin 深度定制让答辩老师一眼看懂你的推荐逻辑默认 Admin 只能 CRUD但你可以把它变成“推荐系统监控面板”。在recommend/admin.py中from django.contrib import admin from .models import UserSimilarity, Recommendation admin.register(UserSimilarity) class UserSimilarityAdmin(admin.ModelAdmin): list_display (user1, user2, similarity, updated_at) list_filter (updated_at,) search_fields (user1__username, user2__username) ordering (-similarity,) def user1(self, obj): return obj.user1.username def user2(self, obj): return obj.user2.username admin.register(Recommendation) class RecommendationAdmin(admin.ModelAdmin): list_display (user, movie_title, score, rank, created_at) list_filter (rank, created_at) search_fields (user__username, movie__title) ordering (user, rank) def movie_title(self, obj): return obj.movie.title[:30] ... if len(obj.movie.title) 30 else obj.movie.title效果评委点进/admin/recommend/recommendation/能看到所有用户的推荐列表按rank筛选“Top-1”、“Top-3”还能搜用户名快速定位——这比口头解释“我们用了协同过滤”有力得多。5.3 最小化部署 checklist三步打包答辩前 1 小时搞定别等到答辩前夜才部署。按这个顺序走1 小时内必通数据库导出/导入避免现场建表# 导出开发机 mysqldump -u root -p --databases movie_recommender movie_rec.sql # 导入答辩机先创建空库 mysql -u root -p movie_recommender movie_rec.sql静态文件收集Django 2.2.1 必做python manage.py collectstatic --noinput # 确保 settings.py 中 STATIC_ROOT os.path.join(BASE_DIR, staticfiles)用runserver替代 Gunicorn答辩机无需高并发# 修改 settings.py DEBUG True # 答辩现场开 DEBUG方便看错误页 ALLOWED_HOSTS [*] # 临时允许所有 host # 启动 python manage.py runserver 0.0.0.0:8000然后用手机访问http://[答辩机IP]:8000确保首页、登录、推荐页全通。从那以后我每次做毕设都强制走一遍「导出 SQL → 新建虚拟机 → 导入 → collectstatic → runserver」全流程哪怕只是本地测试。因为答辩现场的网络、系统环境、甚至 USB 插口松动都可能让你的 demo 在最后一分钟崩掉。这份协同过滤系统不是炫技的玩具它是你交出去的代码答卷——而答卷的及格线从来不是“能跑”而是“在任何一台 Windows 10 笔记本上插上 U 盘双击 start.bat就能让评委点头说‘嗯逻辑是通的’”。希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑