基于协同过滤的豆瓣电影推荐系统设计与实现
1. 项目概述基于协同过滤的豆瓣电影推荐系统这个电影推荐系统是我去年指导的一个计算机专业毕业设计项目采用PythonDjango全栈开发核心算法使用经典的协同过滤推荐技术。系统通过分析豆瓣电影数据集中的用户评分行为挖掘用户-电影之间的潜在关联为每位用户生成个性化推荐列表。前端采用ECharts实现数据可视化整体架构简洁但功能完整非常适合作为毕业设计或推荐系统入门练手项目。推荐算法本质上是在解决信息过载问题——当豆瓣电影库中有上万部作品时如何帮助用户快速发现符合口味的电影协同过滤(Collaborative Filtering)通过物以类聚人以群分的思路既不需要理解电影内容特征也不需要用户填写复杂偏好问卷仅凭历史评分数据就能实现相当准确的推荐。我在项目中特别注重算法可解释性所有推荐结果都会显示因为您喜欢A电影所以推荐相似的B电影这样的逻辑链条。2. 技术架构解析2.1 整体技术栈选型后端采用Django框架而非Flask的原因有三一是Django自带的Admin后台非常适合快速管理电影数据二是其ORM系统能优雅地处理用户-电影-评分这类多对多关系三是模板系统可以方便地集成ECharts。数据库使用MySQL 8.0主要考虑其JSON字段对电影元数据的良好支持。前端技术栈比较轻量Bootstrap 5实现响应式布局ECharts 5.3.2用于数据可视化jQuery处理基础DOM操作特别说明版本选择Python 3.8是必须的因为项目中使用到了海象运算符(:)和f-string等新特性。Django选择4.1 LTS版本在稳定性和新功能之间取得平衡。2.2 协同过滤算法实现项目采用基于用户的协同过滤(UserCF)核心公式如下用户相似度计算皮尔逊相关系数def pearson_sim(user1, user2): # 获取共同评分电影 common_movies set(user1.ratings.keys()) set(user2.ratings.keys()) n len(common_movies) if n 0: return 0 # 计算各项求和 sum1 sum(user1.ratings[m] for m in common_movies) sum2 sum(user2.ratings[m] for m in common_movies) sum1Sq sum(pow(user1.ratings[m], 2) for m in common_movies) sum2Sq sum(pow(user2.ratings[m], 2) for m in common_movies) pSum sum(user1.ratings[m] * user2.ratings[m] for m in common_movies) # 计算皮尔逊值 num pSum - (sum1 * sum2 / n) den sqrt((sum1Sq - pow(sum1, 2) / n) * (sum2Sq - pow(sum2, 2) / n)) return num / den if den ! 0 else 0推荐生成逻辑找出目标用户的K个最近邻相似度最高的其他用户聚合这些邻居评分过但目标用户未看过的电影根据相似度加权计算推荐得分按得分降序返回Top N推荐实际项目中需要处理冷启动问题对新用户采用热门电影填充对新电影采用基于内容的推荐作为fallback3. 数据准备与处理3.1 豆瓣数据集获取使用公开的豆瓣电影数据集约10万条评分记录主要包含三个CSV文件movies.csv电影ID、标题、类型、年份等元数据ratings.csv用户ID、电影ID、评分(1-5)、时间戳users.csv用户ID、注册时间等基础信息数据预处理关键步骤# 读取原始数据 ratings pd.read_csv(ratings.csv) movies pd.read_csv(movies.csv) # 处理缺失值 movies movies.dropna(subset[year]) ratings ratings[ratings.movieId.isin(movies.movieId)] # 类型转换 movies[year] movies[year].astype(int) movies[genres] movies[genres].str.split(|) # 构建评分矩阵 rating_matrix ratings.pivot_table( indexuserId, columnsmovieId, valuesrating ).fillna(0)3.2 数据存储设计Django模型定义示例class Movie(models.Model): movie_id models.CharField(max_length20, uniqueTrue) title models.CharField(max_length200) year models.IntegerField() genres models.JSONField() avg_rating models.FloatField(default0) class Rating(models.Model): user models.ForeignKey(User, on_deletemodels.CASCADE) movie models.ForeignKey(Movie, on_deletemodels.CASCADE) score models.FloatField() timestamp models.DateTimeField(auto_now_addTrue) class Meta: unique_together (user, movie)4. 系统功能实现4.1 核心功能模块用户认证系统注册/登录/个人中心基于session的身份验证评分历史记录查询推荐引擎实时推荐用户登录后立即计算批量推荐定时任务更新推荐缓存推荐解释生成电影浏览分页列表多条件筛选类型/年份/评分详情页展示可视化分析用户评分分布雷达图电影类型占比饼图评分时间趋势折线图4.2 ECharts集成实例电影类型分布可视化// Django模板中嵌入 function initGenreChart() { const chart echarts.init(document.getElementById(genre-chart)); const option { title: { text: 电影类型分布 }, tooltip: {}, series: [{ name: 类型, type: pie, radius: 60%, data: [ {% for genre in genres %} { value: {{ genre.count }}, name: {{ genre.name }} }, {% endfor %} ] }] }; chart.setOption(option); }5. 部署与优化5.1 性能优化技巧相似度矩阵预计算每天凌晨通过Celery定时任务更新使用Redis缓存最近计算结果数据库查询优化为常用查询字段添加索引使用select_related/prefetch_related减少查询次数推荐结果缓存每个用户的推荐结果缓存1小时使用Django的cache框架5.2 部署方案推荐使用宝塔面板部署主要步骤安装Python项目管理器创建MySQL数据库上传项目代码配置uWSGINginx设置定时任务Celery Beat关键uWSGI配置[uwsgi] chdir/path/to/project moduleproject.wsgi:application masterTrue processes4 socket/tmp/project.sock vacuumTrue6. 常见问题解决6.1 算法相关Q计算用户相似度时内存溢出 A采用稀疏矩阵存储评分数据使用scipy.sparse中的csr_matrixQ推荐结果总是热门电影 A引入推荐分数归一化final_score prediction_score / (popularity^0.5)6.2 工程实现QDjango ORM查询超时 A1) 添加数据库索引 2) 分页查询 3) 使用iterator()流式处理QECharts图表不显示 A检查1) DOM元素尺寸是否有效 2) 数据格式是否符合要求 3) 控制台报错信息7. 项目扩展方向混合推荐策略结合基于内容的推荐CB加入时间衰减因子实时推荐使用Kafka处理用户行为流增量更新用户相似度深度学习模型尝试NeuMF等神经网络模型使用TensorFlow Serving部署模型这个项目最让我惊喜的是协同过滤算法在简单实现下就能获得不错的效果。建议初次实现时先完成基础版本再逐步添加缓存、优化等高级特性。对于毕业设计而言完整走通数据获取-算法实现-系统开发-效果评估全流程比追求算法复杂度更重要