☰
Django电影推荐系统毕业设计实战:协同过滤算法与项目架构解析
2026/9/26 7:22:17 网站建设 项目流程

做了这么多年技术,我见过太多一上来就问“推荐系统怎么做”的同学。电影个性化推荐系统在毕业设计里算是最老牌也最稳妥的方向,Django做后端、协同过滤算法出结果、再把用户行为数据喂进去生成 TopN 列表,链路清晰,演示直观,答辩也容易讲。这套组合我实际带项目时反复用过,源码、文档、远程调试这些周边服务我也配合做过不少。今天干脆把我实操过的一套思路摊开聊聊,从选题、架构、算法、编码到避坑,一篇文章给你讲完整。适合正在准备毕业设计的本科生,也想用 Django 快速搭一个推荐系统 Demo 的同学参考。

1. 选题定位:为什么电影推荐系统是“稳中带彩”的毕业设计

1.1 技术栈成熟,试错成本低

如果你把历年本科生的软工选题翻一遍,Django 独挑大梁的题目特别常见:网上商城、博客系统、学生管理系统、二手交易平台……这些都属于“业务型”系统,开发难度不高,但算法含量很低,答辩时老师很容易往功能细节里深挖。电影推荐系统不一样,它同时覆盖了 Web 后端、数据库设计、推荐算法、前端展示四个层面,任何一层都有东西可以讲。

Django 本身是个“保姆级”框架:ORM 帮你管数据库,模板系统帮你渲染页面,Admin 后台可以快速录入演示数据,session、登录认证也都是现成的。协同过滤算法则给系统增加了一层真正意义上的“智能化”,哪怕用的只是最简单的那几个公式,也比单纯增删改查有卖点得多。两者组合起来,既有工程又有算法,答辩时能讲的东西非常多。

再加上电影这个场景本身很讨巧:用户对推荐结果好不好有天然直观的感受,不需要额外解释业务背景。你换成“招聘推荐”“供应链商品推荐”,数据获取麻烦,演示效果还不直观。电影数据可以直接用公开数据集(比如 MovieLens)跑,视觉上又有海报、简介、评分,页面一出来就很有“产品感”。对大部分应届生来说,这个选题风险低、保底容易,做深了又能画出不少技术亮点。

1.2 别把“大数据”三个字当成负担

很多同学看到题目里带“大数据”三个字就慌了,以为非要上 Hadoop、Spark、HDFS 不可。实际上,本科阶段的毕业设计,“大数据”更多体现的是数据处理思路,而不是分布式技术栈。

我做过的方案里,最常见也最合理的是:用 MovieLens 1M 数据,里面包含 6000 多个用户、4000 多部电影、100 万条评分记录。这个规模单机完全扛得住,SQLite 或 MySQL 都能流畅处理,Django ORM 的 bulk_create 几分钟就能完成导入。而“大数据”的感觉从哪来?来自你处理稀疏矩阵的思想,来自离线批量计算的架构设计,来自你解释“为什么推荐热门榜可以兜底冷启动用户”的全局视角。

1.3 从答辩角度反推你的设计目标

选完题之后,一定要先想清楚答辩老师可能盯住你哪几个问题:

  • “你的推荐结果是怎么算出来的?”
  • “协同过滤的原理是什么,你的代码里怎么体现的?”
  • “用户量大了之后,这个方案还跑得动吗?”
  • “新用户第一次进来,系统推荐什么?”
  • “你的 100 万条数据是怎么导入的,刚才演示怎么不用这个数据集?”

其实不用慌,这些问题反而说明选题有深度。你只要在设计和文档撰写时有意识地回应这些点,答辩现场就是“表演”时间,而不是“被拷问”时间。所以下面我按自己做项目的顺序,把架构、算法、编码、数据导入和调试经验分别展开。

2. 系统架构与数据建模:先把工程骨架搭明白

2.1 总体架构:Django MVT 加离线推荐

推荐系统最容易犯的错,就是让用户在点“推荐页”的那一瞬间实时跑相似度计算。如果你是拿 1000 部电影的小数据集跑,可能还好,页面转两三秒勉强能看;一旦换成 MovieLens 1M 这种百万级评分,实时算相似度矩阵基本就是灾难,接口直接超时,演示当场翻车。

所以架构上我习惯拆成两条链路:

  • 在线链路:用户登录、浏览、评分,访问“猜你喜欢”页面时直接读取已经算好的推荐结果,毫秒级返回。
  • 离线链路:定期扫描评分表,重建用户/电影评分矩阵,计算物品相似度,然后为每个用户生成 TopN 推荐列表,把结果写入数据库或缓存。

Django 里做离线计算,最优雅的方式是写一个 management command,比如python manage.py build_recs。你手动跑一次,或者加个定时任务,推荐结果就固化下来了。页面只是把结果查出来渲染。这样代码结构更清晰,答辩时你也能说:“在线服务只做轻量查询,重计算放到离线批量任务里,这是工业级推荐系统的通用做法。”这句话的分量,比你把计算硬塞进 view 函数里强太多。

2.2 核心数据表设计

数据库是整个项目的地基。我的建议是设计三张核心表:用户表、电影表、评分表。

影视评分表要特别注意联合唯一约束:同一用户对同一部电影只能有一条评分记录。如果没有这个约束,用户反复点击评分按钮,评分矩阵里会混进重复数据,协同过滤算出来一大堆异常值,排查起来极其痛苦。

from django.db import models from django.contrib.auth.models import AbstractUser class User(AbstractUser): nickname = models.CharField(max_length=50, blank=True) avatar = models.URLField(blank=True) class Meta: db_table = 'user' class Movie(models.Model): title = models.CharField(max_length=255) genres = models.CharField(max_length=255) poster = models.URLField(blank=True) release_date = models.DateField(null=True, blank=True) class Meta: db_table = 'movie' class Rating(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) movie = models.ForeignKey(Movie, on_delete=models.CASCADE) score = models.FloatField() create_time = models.DateTimeField(auto_now_add=True) class Meta: db_table = 'rating' unique_together = ('user', 'movie')

这三张表基本就够用了。电影表里的 genres 字段也要存储下来,以后就算算法不做内容匹配,你也可以在页面按类型筛选、统计,功能上会丰满很多。评分表的时间戳我建议保留,虽然基础版没用到,但如果你后面想加“用户近期偏好权重”或是画评分趋势图,这个字段就是救命的。

2.3 为什么选 Django 而不是 Flask、SpringBoot

这个问题答辩极容易踩到。Flask 确实更轻,但你需要自己搭登录、ORM、后台管理,还要自己处理数据库迁移,工作量全部堆到眼前。SpringBoot 做这类系统也可以,但 Java 生态重,很多毕业生半年没碰 Java,重新捡起来成本太高。

Django 的优势是“约定大于配置”,你不需要在工程搭建上反复纠结。自带的认证模块和 Admin 后台特别适合毕设过渡:刚搭好项目时,你可以直接登录 Admin 后台给电影表补数据,不用先写好几十个表单页面。另外 Django 的 ORM 对初学者相当友好,查询语句写起来像英语,出问题的概率低,远程调试时也能少折腾你几个晚上。

3. 协同过滤算法的核心实现:从公式到代码

3.1 选 UserCF 还是 ItemCF

协同过滤算法分两类:

基于用户的协同过滤(UserCF)先找到跟你口味相似的用户,再把那些相似用户喜欢过、但你还没看过的电影推荐给你,社交属性很强。逻辑很直观,“和你口味像的人都在看什么”,但它有个致命问题:用户数量一旦增长,用户之间两两算相似度的成本会非常高,而且评分矩阵稀疏时经常找不到真正的“邻居”。

基于物品的协同过滤(ItemCF)先算电影之间的相似度。给定一部电影 A,系统发现看过 A 的人还大量看过 B、C,那 B、C 的评分权重就会提高。你在电商平台看到的“购买了该商品的用户还购买了”就是这种思路。电影推荐场景下,ItemCF 实现更简单、效果更稳定,也是我更推荐的主力算法。

毕业设计里我通常建议两个都实现,ItemCF 做主推荐,UserCF 做分析和对比。答辩时老师问你“为什么不用用户协同过滤”,你回答“用户数量远大于物品数量,且矩阵更稀疏,UserCF 的计算和存储开销增长更快”,这就是一个很好的得分点。

3.2 相似度计算:余弦相似度拆开讲

协同过滤的灵魂就是相似度。最常用的是余弦相似度,把每个用户对某部电影的评分想象成一个向量,夹角越小代表越相似。

公式是:

cos_sim(A, B) = Σ(ai * bi) / (sqrt(Σai^2) * sqrt(Σbi^2))

举个例子,用户 A 对电影《肖申克的救赎》《教父》《霸王别姬》评分分别是 5、4、3,用户 B 在这三部电影上的评分是 4、5、2,那么分子就是 5×4 + 4×5 + 3×2 = 46,分母是 sqrt(25+16+9) × sqrt(16+25+4) = sqrt(50) × sqrt(45) ≈ 7.07 × 6.71 ≈ 47.43,余弦相似度大约 0.97,说明两个用户口味接近。

需要注意一个细节:计算时只统计两个人共同评过分的电影,没有交集的部分不要用 0 填进去。如果强行补 0,分母会变得很大,所有相似度都会被稀释,推荐结果毫无区分度。这个坑我见人踩过无数回。

3.3 ItemCF 推荐生成代码

我自己常用的一种写法是:先把电影相似度矩阵构造成字典,key 是电影 ID,value 是“相似电影ID: 相似度”的列表;然后对用户已评分电影,用相似度乘以用户评分作为加权得分,对未看过的电影累计求和,最后排序取 TopN。

核心代码如下:

import math from collections import defaultdict def build_movie_similarity(ratings, movie_count): # ratings: list of (user_id, movie_id, score) # 先构建"电影-用户-评分"的倒排表 movie_users = defaultdict(dict) for user_id, movie_id, score in ratings: movie_users[movie_id][user_id] = score sim = {} for movie_id, users in movie_users.items(): for other_id, other_users in movie_users.items(): if movie_id >= other_id: continue common = set(users.keys()) & set(other_users.keys()) if len(common) < 5: # 共同评分人数太少,相似度不可靠 continue dot = sum(users[u] * other_users[u] for u in common) norm_a = math.sqrt(sum(s * s for s in users.values())) norm_b = math.sqrt(sum(s * s for s in other_users.values())) if norm_a and norm_b: sim.setdefault(movie_id, []).append( (other_id, dot / (norm_a * norm_b)) ) return sim

然后为某个用户生成推荐:

def recommend_for_user(user_id, user_ratings, movie_sim, top_n=20): scores = defaultdict(float) for movie_id, score in user_ratings: for sim_movie, sim_value in movie_sim.get(movie_id, []): if sim_movie in [m for m, _ in user_ratings]: continue scores[sim_movie] += score * sim_value return sorted(scores.items(), key=lambda x: x[1], reverse=True)[:top_n]

这里没有用复杂的 Pandas 矩阵,全部用 Python 字典和列表完成,够轻量,也好在论文里解释清楚每一步。

3.4 冷启动问题必须先想好

协同过滤最大的软肋是冷启动。新用户没有任何评分记录,相似用户算不出来,推荐列表就是空的;新电影刚进库,评分太少,相似度矩阵里排不上号。

我常用的兜底方案是:

  • 新用户:直接推荐全站平均分最高的热门电影Top20,页面文案写“热门推荐”。
  • 新电影:打标签或者按类型匹配,如果项目预算有限,干脆在详情页展示“新片上映”,不做算法推荐。
  • 用 ItemCF 时,给相似度加一个“共同评分人数”的硬门槛,比如共同评分数少于 5 的电影对直接丢弃,这样能有效过滤掉偶然巧合带来的噪声相似度。

冷启动是每个推荐系统都会遇到的问题,你提前解决了,答辩就有底气。

4. 基于 Django 的完整实现流程

4.1 环境准备与项目初始化

Django 版本建议直接用长维护版本,比如 Django 4.2 LTS,配 Python 3.10 左右。别一上来追最新版,容易碰上第三方库没跟上、文档零散的问题。

初始化命令很简单:

django-admin startproject movie_recsys cd movie_recsys python manage.py startapp recommend

然后在settings.py里把 recommend 注册进 INSTALLED_APPS,再配置好数据库和模板目录。如果没有特殊原因,数据库直接用 SQLite 起步,等后面要上 MySQL 再把配置改掉,Django ORM 帮你做了大部分兼容工作。

4.2 URL 路由与视图函数怎么写

毕业设计层面的项目,我建议用函数视图,别硬上类视图。函数视图写起来直接,远程调试时也更好定位问题。核心路由大概这样:

# recommend/urls.py from django.urls import path from . import views urlpatterns = [ path('', views.index, name='index'), path('register/', views.register, name='register'), path('login/', views.login_view, name='login'), path('logout/', views.logout_view, name='logout'), path('movie/<int:movie_id>/', views.movie_detail, name='movie_detail'), path('rate/<int:movie_id>/', views.rate_movie, name='rate_movie'), path('recommend/', views.recommend, name='recommend'), ]

首页就是电影列表页,登录后才能访问推荐页。视图里先判断用户是否登录,用 Django 自带的@login_required装饰器即可。

from django.contrib.auth.decorators import login_required from django.shortcuts import render, redirect @login_required def recommend(request): user_id = request.user.id recs = get_user_recommendations(user_id) # 读取离线生成的结果 return render(request, 'recommend/list.html', {'recs': recs})

核心的推荐结果读接口,不要现场触发算法计算,而是查预先算好的推荐表。有人会问:推荐结果表的数据什么时候更新?我的方案是,只要用户产生一条新评分,就在评分视图里顺手更新这个用户的 TopN 列表;如果用户量太少,也可以直接重新跑一遍build_recs任务,反正离线计算也就十几秒到几十秒。

4.3 电影列表、评分模块与个人中心

电影列表页基本就是给已登录用户浏览用的,一个普通表格或卡片墙就能展示。评分模块反而要做得顺手一点:用户在电影详情页选择 1 到 5 星提交,后端拿到数据后先判断当前用户是否已经对该电影评过分。如果评过分,就更新分数;如果没评过,就新建一条评分记录。

高能预警:必须处理评分重复问题。如果不去重,评分表会越滚越越大,算法结果却越来越不稳定,用户评分一多,推荐效果反而崩。我上面用了 unique_together 约束,数据库层就能挡住重复数据。

个人中心可以做用户自己评过的电影列表,方便用户查看和二次调整评分。这一块工作量不大,但能让页面显得完整,很多老师喜欢看这种“闭环功能”。

4.4 模板渲染与静态文件坑

前端推荐页用 Bootstrap 就能做得足够耐看。重点是样式和图片要放进 static 目录,模板里用 Django 静态文件标签加载。

很多新手在本地跑通后,把 DEBUG 改成 False 再运行,静态文件全部 404。这个坑太经典了,因为 Django 在 DEBUG=False 时不会自动帮你 serve 静态文件。毕设演示如果只是python manage.py runserver,请保持 DEBUG=True,再准备好环境的说明;如果你要部署到服务器演示,要么配 Nginx,要么先跑collectstatic。答辩前一定要把这一步测试好。

5. 数据来源与“大数据”环节的落地

5.1 MovieLens 数据集怎么导入

MovieLens 是最常用的电影推荐数据集,我用得最多的是 ml-1m,文件名就叫ratings.dat、movies.dat、users.dat。它的字段分隔符是::,注意不是逗号,第一次处理数据经常在这一步懵掉。

数据导入代码不复杂:

import csv def import_movies(): with open('movies.dat', encoding='latin-1') as f: for line in f: mid, title, genres = line.strip().split('::') Movie.objects.update_or_create( id=int(mid), defaults={'title': title, 'genres': genres} )

评分数据导入时,强烈建议用bulk_create。100 万条就按 1 万条一批批量插入,避免一条条 save 造成极大的时间开销。我自己测试过,一条条插入可能要几个小时,用 bulk_create 后也就是几分钟的事。这也是论文里能写的好细节。

5.2 数据清洗环节不能跳过

虽然公开数据集相对干净,但你依然要演示数据清洗的过程。比如:

  • 检查评分值分布,MovieLens 的评分是 1 到 5 的整数,但你自己加的评分可能是 0.5 到 5 的浮点数,需要统一。
  • 去掉评分记录很少的冷门电影,比如评分人数少于 5 的。
  • 时间戳字段做不做处理?我建议把时间戳转成可读日期保留,方便后续分析。
  • 电影标题里的年份大小写不统一,建议单独拆分年份字段。

这些工作放到“大数据分析与预处理”章节里讲,内容非常充实,而且老师也知道你确实处理过数据,不是空口说。

5.3 答辩时怎么回答“大数据体现在哪里”

如果老师真的追问“你的系统哪里体现了大数据”,我的建议是不要绕弯子。你可以说:

“本系统使用 MovieLens 1M 数据集,总计约一百万条用户行为记录。由于单机内存受限,不能对全部实时计算,所以系统采用离线批量计算生成推荐结果。同时算法层面采用了物品协同过滤,相似度矩阵按电影数量存储,相对用户协同过滤节省了大规模用户两两比较的存储开销。如果后续数据量继续增长,可以把离线计算部分替换为 Spark 平台上的 ALS 算法,在线服务架构保持不变。”

这个回答既交代了现状,也给出了扩展思路,比盲目吹“我用过Hadoop”务实得多。毕业设计评价的核心是“理解到位、能自圆其说”,而不是真的让你搭建生产级集群。

6. 调试验证与毕设交付避坑实录

6.1 环境配置的经典坑

带过太多远程调试的案例,十有八九先把时间耗在环境问题上。最常见的几个:

  • 本地 Python 版本太高/太低,装不了对应版本的 Django,比如 Python 3.12 配旧版 Django 偶尔会报一些第三方库兼容问题。
  • 忘记创建虚拟环境,依赖包全装到系统环境,后面一换电脑又跑不起来。
  • 没有生成requirements.txt,换机器不知道要装什么。建议用pip freeze > requirements.txt,顺便把核心版本号手工写上。
  • 数据库迁移问题,改完模型后忘记python manage.py makemigrations和python manage.py migrate,一运行就报表不存在。

再给一个建议:交付的压缩包里除了源码,一定要放一份 README,写清楚 Python 版本、Django 版本、启动步骤、数据导入命令、测试账号。你写的文档越清晰,远程调试时沟通成本越低,你晚上能省的觉也越多。

6.2 推荐结果为空或效果极差的排查

如果协同过滤算出来全是空列表,按优先级排查:

  • 是不是没有导入评分数据?评分表还是空的,推荐函数当然什么都拿不到。
  • 用户当前是否没有评分记录?新用户命中冷启动分支了吗?
  • 相似度计算里是否只扫描了共同评分项?如果分母算出 NaN,Python 排序时会直接翻车。
  • 隐私问题:相似度矩阵里保存的是“从库中读出的 ID”和“循环时的 ID”是否混淆?我犯过几次,把 movie_id 当成列表下标用,结果全是默认值。

建议在管理后台或命令行里,选一个真实的已知用户手动算一遍推荐结果,再把算法打印出来逐行对比。别相信“看着应该没问题”,程序从没按你的预期跑过。

6.3 远程调试的协作经验

但凡做过毕设定制的同学都知道,交付不只是“把源码打包发过去”。如果你帮别人远程调试,建议按这个顺序来:

先让对方把项目压缩包和requirements.txt发过来,你在自己的机器上先跑通一遍,确认不是缺失代码问题。然后让对方法用远程协助软件把电脑画面共享给你,你实时看报错信息,比自己猜要高效得多。改完代码后,一定要让对方在本地重新跑一遍完整流程,确保不是只有你的机器能跑通。

我还习惯给每个阶段标好版本号,每改一个稳定版本就压缩存一份。因为这些项目往往改着改着就崩了,回滚需要一个干净的历史版本。

6.4 论文结构和答辩演示的加分点

论文结构其实有固定套路:需求分析、系统设计、数据库设计、算法设计、系统实现、系统测试、总结与展望。重点是把协同过滤的数学公式推导写清楚,把相似度计算的例子算出来,把冷启动的解决方案单独列小节。

演示的时候有个我自己觉得很实用的小技巧:准备一份小数据量版本。比如只保留 100 个用户、200 部电影的数据集,专门放在演示环境里,这样页面秒开,算法秒出,评委体验很好。等老师问“大数据集怎么处理”,你再切到带完整数据的后台截图或者离线计算结果页面,讲一讲大数据批量导入和离线生成推荐的过程。这个“小数据集演示 + 大数据集分析”的组合,我实测下来几乎不会冷场。

再说一句交付层面的话:字幕和截图一定要提前做好,很多同学远程调试时说“我这边看得好好的”,结果一录屏就黑屏或窗口溢出。录好一段 3 分钟的完整演示视频,在关键时刻绝对能挽回局面。

我个人带项目的体会是:电影推荐系统这个题目,上限可以很高,下限也足够保平安。如果你能把 ItemCF 的原理讲透彻,把 Django 项目工程结构写得干净,再把冷启动、大数据扩展这些高频问题提前准备好,就已经超过了大半的同期项目。这个项目真正吸引人的地方不在于“协同过滤这个概念有多新”,而在于它迫使你把 Web 框架、数据库设计和算法工程串在一起,形成一套完整的思考路径。

最后再分享一个小经验:代码不要写到最后一晚才开始联调。先把最小闭环跑通——用户登录、看列表、评个分、推荐页能出结果,哪怕结果是假的也行。核心链路通了,后面加相似度算法、加海报展示、加数据分析都是匀速往前推进;核心链路不通,再炫酷的页面也只是空壳。希望这篇记录能帮你少熬几个夜,祝你的毕设顺利上线。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询