news 2026/10/21 13:07:37

基于Python和Django的双协同过滤电影推荐系统设计与实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于Python和Django的双协同过滤电影推荐系统设计与实现

毕业设计做到推荐系统这一挂,我个人的看法是:选题选得好不好,直接决定后面几个月是轻松还是折磨。我当时选的就是“基于Python和Django的双协同过滤电影推荐系统”,说白了就是把爬虫抓数据、协同过滤算相似、Django做web展示这一整条链路跑通,最后既有能演示的界面,又有能写进论文里的算法分析。这个题目能覆盖机器学习、大数据处理、Web开发多个模块,导师一看就觉得工作量够,答辩时也有的聊。如果你正好在纠结毕设方向,或者已经选了类似题目,这篇就把我当时踩过的坑、总结出来的经验全部拆开揉碎了讲。

这篇内容适合几类人:一是准备选推荐系统方向做毕业设计的同学,二是想快速上手Django框架并整合机器学习算法的初学者,三是想搞清楚协同过滤里那些细节到底怎么落地的人。我会从整体架构、爬虫实现、算法细节、Django集成、常见问题这几个方面,把我当时怎么做的一步步还原出来,包括核心代码逻辑和参数选择的原因。没有废话,直接开始。

1. 项目背景与整体方案设计

1.1 为什么选择协同过滤推荐系统

推荐系统是机器学习里应用最广、最容易讲清楚效果的方向之一,尤其适合毕业设计。对比其他的比如图像识别、自然语言处理,推荐系统不需要特别强的算力,数据量可控,评价指标(比如准确率、召回率)也直观。更关键的是,它能把“数据采集→数据处理→算法建模→系统实现”这一整条工程链路串起来,正符合毕设要体现“完整项目”的要求。

当时我选基于协同过滤而不是基于内容推荐,主要考虑两点。第一,协同过滤是推荐算法里最经典的一类,理论成熟,论文里好写相关工作和原理分析。第二,它不需要复杂的特征工程,只需要用户对物品的评分或者行为数据,数据源容易获取。基于内容的推荐虽然也可以做,但需要额外提取电影的类型、导演、演员等特征,数据搜集和存储成本更高,对新手不太友好。

关键是“双协同过滤”这个点。单纯只做基于用户的协同过滤(UserCF)或者只做基于物品的协同过滤(ItemCF),会显得工作单薄。如果把两者都实现,再做一个加权融合,既有算法对比,又有混合策略,这就成了答辩时的加分项。我当时就是先分别实现UserCF和ItemCF,再通过一个权重参数把它们的结果结合起来,然后调权重看效果变化,这部分内容直接写成了论文里的一章“融合策略实验”。

1.2 技术选型考量:Python、Django、爬虫、大数据

技术栈当时经过反复比较,最终确定是Python + Django + 爬虫 + SQLite/MySQL。

Python不用多说,机器学习算法生态最好,写爬虫也快。Django作为Web框架,自带Admin后台、ORM、模板系统,开发效率高,适合快速做一个展示页面。相比Flask,Django集成了更多东西,比如用户认证、表单处理,写毕设可以少折腾很多。如果用Flask,很多功能要自己拼,反而浪费时间。

爬虫部分我用的Requests + BeautifulSoup(或者Scrapy),因为目标网站结构相对稳定,不需要分布式爬虫,单机多线程就够了。数据量不需要上千万条,我当时抓了大约两万条评分数据、三千部电影、两百个用户,已经足够做协同过滤演示了。

大数据这个关键词在毕设里怎么体现?其实不用铺真的大数据集群。可以用Pandas做数据处理,把数据量调整到十万级,通过优化算法和数据结构(比如用稀疏矩阵存储),在论文里讨论一下大数据场景下的性能瓶颈和优化思路,这样就够了。真让一个本科毕设去搭Hadoop集群,既复杂又没必要。

1.3 系统整体架构

整个系统分三层:数据层、算法层、应用层。

数据层负责爬取数据、清洗、存储。我设计了三个核心表:用户表(user_id, 用户名等)、电影表(movie_id, 电影名, 类型, 地区等)、评分表(user_id, movie_id, rating, timestamp)。评分表是协同过滤的输入,也是整个系统的核心。

算法层负责读取评分数据,构建用户-物品评分矩阵,计算相似度矩阵,生成推荐结果。我单独写了一个算法模块,不跟Django的视图函数耦合,这样方便在Jupyter Notebook里单独调试算法,跑通了再接进Web系统。

应用层就是Django项目,负责接收请求、调用算法、渲染页面。用户在网页上选择“基于用户的推荐”或者“基于物品的推荐”,就可以看到推荐结果。另外我还做了一个热门榜,作为冷启动时的兜底推荐。

2. 数据采集:爬虫设计与实现

2.1 确定数据来源与字段

数据来源是某知名电影评分网站。这里不直接说具体名字,因为我之前吃过亏,网站改版后爬虫就废了。我在毕设里用的是公开的模拟数据包,结构跟真实网站一致,但避免了实时爬虫的不稳定性。你如果也想用它,可以找一些开源的数据集,比如某开源社区提供的“ml-100k”或者“ml-1m”电影评分数据,这比爬虫更省事。但毕设题目里写了“爬虫”,我就自己抓了一部分,再结合公开数据,凑够了完整度。

我抓的字段包括:电影ID、电影名、类型、导演、上映年份、评分人数、平均评分。用户ID我伪造了100个模拟用户,每个用户对随机的一部分电影打分,分数在1到5之间。这样虽然数据是模拟的,但算法验证不受影响,因为协同过滤只关心用户-物品的评分关系。

2.2 请求与解析:注意事项

如果你一定要写实时的爬虫,需要注意几个点。

第一,请求头要伪装成浏览器,不然很容易被拒绝。我当时用的是Requests库,设置了User-Agent和Referer,还做了随机的延时(1到3秒)来避免被封。

第二,解析网页时优先选择CSS选择器或者XPath,别只用正则。因为网页结构会有微调,正则写死了很容易失效。BeautifulSoup配合lxml解析器,用soup.select()定位目标元素,容错率更高。

第三,分页和反爬。很多网站会限制单页条数,并且可能有动态加载。我当时直接找了一个JSON接口,用requests.get()拿到JSON数据再解析,比解析HTML省力得多。这一步算是个小技巧,建议你在做爬虫之前先看看目标网站有没有暴露内部的接口。

清理数据的时候,注意字段的缺失值和重复值。比如有些电影没有导演信息,评分量为0,这些记录我直接删掉,否则后面计算相似度时会出现NaN或者除零错误。

2.3 数据清洗与存储

清洗工作我放在Pandas里做。首先把爬下来的原始数据转换成DataFrame,然后去重、去空白、转换类型。评分列的数值型强制转换,日期列统一格式。对于评分记录,我只保留分数在1到5之间的数据,超出范围的丢弃。

存储方面,Django默认用的是SQLite,小数据量完全够用。但我为了后面算法处理更高效,把评分数据导出成CSV文件,用Pandas直接读取,避免每次都要查数据库。在Django的ORM里也同步保存一份,用于在网页上展示统计信息。两边通过批量导入脚本同步,避免数据不一致。

我当时犯过一个错:爬虫抓到的评分数据量一大,直接用ORM一条条插入数据库,慢得离谱。后来改成批量bulk_create(),速度快了好多倍。如果你的数据量还在十万级,这个优化效果很明显。

3. 协同过滤算法:从原理到实现

3.1 基于用户的协同过滤(UserCF)

基于用户的协同过滤核心思想是:给你推荐那些和你兴趣相似的其他用户喜欢的电影。算法分三步。

第一步,建立用户-物品评分矩阵。行是用户,列是物品,值是评分。

第二步,计算用户之间的相似度。我用的皮尔逊相关系数,公式是:

sim(u, v) = Σ(r_ui - mean_u)(r_vi - mean_v) / sqrt(Σ(r_ui - mean_u)^2 * Σ(r_vi - mean_v)^2)

皮尔逊相关系数能消除用户评分尺度的影响,比如有人习惯打3分,有人习惯打5分,直接算欧氏距离会被这个偏差带偏。实际操作中,我用Pandas的corrwith()方法直接算出来了,效率很高。

第三步,为目标用户找到K个最相似的用户,然后把这些用户喜欢的物品中,目标用户没有交互过的物品,按“加权评分”排序推荐。加权评分公式是我用python自己实现的:

def recommend_by_user(user_id, top_n=10): target_ratings = user_item_matrix.loc[user_id] similar_users = user_sim_matrix[user_id].drop(user_id).sort_values(ascending=False)[:K] scores = {} for sim_user, sim_score in similar_users.items(): user_rated = user_item_matrix.loc[sim_user].dropna() for movie, rating in user_rated.items(): if pd.isna(target_ratings[movie]): # 目标用户没看过 scores[movie] = scores.get(movie, 0) + sim_score * rating # 按权重排序,返回TopN

这里要注意,如果K取太小,推荐结果可能太单一;K取太大,又会引入不相似的用户。我试过K=10到K=50,最后选了K=20,效果比较平衡。

3.2 基于物品的协同过滤(ItemCF)

基于物品的协同过滤核心思想是:推荐给你那些和你喜欢过的物品相似的其他物品。算法逻辑跟UserCF对称。

第一步还是建立评分矩阵,但相似度计算换成了物品之间的相似度。我用余弦相似度,因为物品的属性向量一般是评分向量,余弦相似度可以高效处理高维稀疏向量。

公式:

sim(i, j) = (r_ui * r_uj)的和 / (sqrt(∑(r_ui)^2) * sqrt(∑(r_uj)^2))

Python示例:

item_sim = user_item_matrix.T.corr(method='pearson') # 快速算物品相关性

当然,物品之间的相关性直接用皮尔逊也可以,但要注意评分矩阵转置之后,列的均值计算方式会变。

第二步也是计算加权推荐。当用户对电影A打了高分,系统就找到和电影A最相似的电影B,把B推荐给用户。推荐分 = 用户对A的评分 × A和B的相似度,多个相近电影加权求和。

ItemCF有一个好处:物品的相似度可以离线算好,存起来。用户来请求推荐时只用查表,不用实时计算,响应速度很快。我实际测了一下,3000部电影算一次物品相似度矩阵,在我当时的笔记本上花了不到10秒,完全能接受。

3.3 双协同过滤的融合策略

“双协同过滤”名字听起来很唬人,其实做法不复杂。就是把UserCF和ItemCF两个算法的推荐列表做一个融合。我用的方法是:设定一个权重参数α,最终推荐分 = α * UserCF评分 + (1-α) * ItemCF评分。

这里的关键是两者评分的量纲要一致。UserCF给的是“用户相似度 × 评分”的加权和,ItemCF也是类似的量级,但数值范围不一定完全对得上。所以我先对两个评分做归一化处理,把分数压到0到1之间,再按α融合。

Python伪代码:

def hybrid_recommend(user_id, alpha=0.6): user_scores = recommend_by_user(user_id) item_scores = recommend_by_item(user_id) all_movies = set(user_scores.keys()) | set(item_scores.keys()) final_scores = {} for movie in all_movies: s_user = user_scores.get(movie, 0) s_item = item_scores.get(movie, 0) final_scores[movie] = alpha * s_user + (1 - alpha) * s_item return sorted(final_scores.items(), key=lambda x: x[1], reverse=True)[:top_n]

α怎么选?我做了几组对照实验,分别在α=0.4、0.5、0.6、0.8时算了推荐结果的命中率(看用户实际会给多高的分),最终α=0.6效果最好。这一部分实验数据直接成了论文里“融合策略效果对比”那节。答辩时导师对这里很感兴趣,问了几个问题,因为这是体现“自己思考过”的地方,而不只是照搬课本。

3.4 相似度计算与性能优化

相似度计算是协同过滤最耗时的地方。如果是十万级评分、几千部电影,直接双重循环计算复杂度高,我毕业设计的demo级别数据还能承受。但如果数据真的到了“大数据”的量级,比如几百万评分,就必须做优化。

我当时用了几个优化手段:

一是使用稀疏矩阵,用scipy.sparse的csr_matrix存储评分矩阵,避免大量0维浪费内存。二是用向量化操作配合numpy,不用Python原生循环算相似度。三是相似度计算做预计算,存到数据库或者缓存文件里,用户请求时直接读取。

实际测试:用稀疏矩阵后,内存占用降低了约80%。我还在论文里简单讨论了既然是基于大数据,就要把可扩展性考虑进去,提了一下可以使用分布式计算框架比如Spark,但毕设主体不涉及平台部署,点到为止即可。

4. Django集成与系统实现

4.1 Django项目结构设计

Django项目我用了一个标准的MVT结构。app命名为recommender,里面分几个子模块:models.py定义用户、电影、评分表;views.py处理推荐请求;algorithm.py放协同过滤算法;utils.py放数据处理辅助函数。

目录大致是这样的:

manage.py recommender/ settings.py urls.py movie_rec/ models.py views.py algorithm.py urls.py templates/ index.html recommend.html movie_detail.html

为什么要把算法单独拆成一个文件?因为Django的视图函数不适合放复杂逻辑,不然调试不方便。我当时就是先在这个文件里用命令行直接运行,验证算法输出没问题,再丢给视图调用。在视图中我只做请求接收、调用算法、准备上下文数据这三个动作。

4.2 核心功能模块实现

核心的视图函数逻辑是:用户输入要推荐的用户ID,选择推荐方式(UserCF、ItemCF还是混合),然后算法模块返回推荐电影列表,包括电影ID、名字、预测分数。

视图示例:

def recommend_view(request): if request.method == 'POST': user_id = request.POST.get('user_id') method = request.POST.get('method') # 'user', 'item', 'hybrid' rec_movies = get_recommendations(user_id, method) return render(request, 'recommend.html', {'movies': rec_movies}) return render(request, 'recommend.html', {})

这里的get_recommendations函数我封装在algorithm.py里,对三种模式做了分支判断。返回的每个电影,我还从电影表里查出类型、导演这些字段,方便页面展示。

为了演示推荐效果,我特意在首页放了一个“用户历史评分记录”表格,让用户可以选一个已有用户ID查看他看过的电影,然后点击“推荐”看结果。这样现场演示的时候,能明显看出推荐结果跟他历史的关联,也方便写进演示视频里。

4.3 前端页面展示

前端我没有用特别复杂的框架,就是Bootstrap + Django模板。首页有用户选择器、推荐方式选择按钮、推荐结果表格。表格里列出电影名、类型、推荐评分排序。再搞一个折叠区域,显示算法的计算结果或者处理耗时,直观展示“从提交请求到生成结果”的过程。

模板示例(略缩):

<form method="post" action="{% url 'recommend' %}"> {% csrf_token %} <select name="user_id"> {% for user in users %} <option value="{{ user.id }}">{{ user.id }}</option> {% endfor %} </select> <button name="method" value="user">UserCF推荐</button> <button name="method" value="item">ItemCF推荐</button> <button name="method" value="hybrid">混合推荐</button> </form> <table> <thead> <tr> <th>排名</th> <th>电影名</th> <th>类型</th> <th>预测评分</th> </tr> </thead> <tbody> {% for movie in movies %} <tr> <td>{{ forloop.counter }}</td> <td>{{ movie.title }}</td> <td>{{ movie.genre }}</td> <td>{{ movie.score }}</td> </tr> {% endfor %} </tbody> </table>

另外我还做了一个“用户相似度可视化”页面,用柱状图显示指定用户最相近的10个用户以及相似度分数。这部分用ECharts画图,不大需要后端处理,把JSON传给前端就行。答辩演示时一眼就能看出算法的内在逻辑。

5. 常见问题与bug排查实录

5.1 数据量过大导致内存溢出

第一次把全量数据放进算法跑,直接内存飙升到我怀疑人生。原因是rating DataFrame里的用户ID和电影ID是字符串,占内存大,而且我一开始用密集矩阵存储。后来我把ID全部转成整数索引,再用scipy稀疏矩阵,内存问题一下子解决了。

具体操作是:创建两个字典,分别把用户ID和电影ID映射到从0开始的整数编号,然后构造稀疏评分矩阵:

from scipy.sparse import csr_matrix row = ratings['user_idx'] col = ratings['movie_idx'] data = ratings['rating'] sparse_matrix = csr_matrix((data, (row, col)))

这个矩阵参与向量化运算,比用Pandas DataFrame的密集形式快了不止一个量级。

5.2 冷启动问题处理

协同过滤最怕冷启动,也就是老用户或者老物品没有行为数据。新用户刚注册,没有历史评分,算法没办法给你推荐。我当时做了个“热门兜底”:推荐全站评分人数最多的前10部电影。这个方案虽然简单,但在实际演示时确实有用。论文里可以写“针对冷启动场景采用基于流行度的推荐策略,作为协同过滤的补充”。

新电影也一样,没有评分,没有交互数据,协同过滤推荐不出来。我同样做了时间衰减逻辑:只要新电影一上线,就把它加入到“最新上架”榜单里,保证它至少能被看到。

5.3 爬虫被封IP的对策

如果你真的爬实时数据,最怕遇到封IP。我当时的应对策略有三个:一是降低请求频率,用random sleep;二是更换User-Agent,每次请求随机选一个;三是用代理池,但那个成本比较高,而且毕设规模用不上。我建议你优先用公开数据集,把爬虫部分简单做一个demo,不要依赖实时数据,免得答辩前几天突然挂了,心态直接崩。

5.4 其他常见坑

  • 相似度矩阵对角线不为1:我一开始直接用Pandas的corr(),发现有的物品跟自己的相似度不是1,因为有些列全为NaN。解决办法是在计算完后强制把对角线设为1。

  • 推荐结果重复:个别电影同时从多个渠道生成推荐,在混合推荐时会出现重复。我用了字典,最后根据电影ID去重。

  • Django的CSRF问题:如果你直接用表单POST,忘记加{% csrf_token %},会报403错误。这个坑我看了半天才明白。

  • 数据库编码问题:中文电影名存入SQLite后显示乱码,后来统一在settings里设置UTF-8编码,并在连接后加了Django的OPTIONS 'charset': 'utf8mb4'。

6. 写在最后的一些经验

说实话,做完这个毕设,我对推荐系统的理解比之前上课听几百遍都深。原理书上看觉得简单,自己动手才会发现,就算是最基础的UserCF,也有很多当时没想到的细节。要特别强调一点:别让算法一直“独守空房”死在后台,一定要接上Web界面或者做一个API,让人能交互。这样答辩的时候你一边操作一边讲,比单纯放PPT有说服力得多。

我个人的体会是,双协同过滤这个方案不是越复杂越好,其实两项基础算法的融合已经足够体现你对问题的思考。在答辩现场我讲了融合权重的调参过程和效果对比,导师明确表示这点是加分项。如果你有余力,可以在融合算法的基础上再加一个简单的SVD矩阵分解做对比,那论文就会更饱满,但也会多不少工作量,量力而行。

最后给几个终极建议:数据一定要提前准备,不要等到写论文了才去爬数据;算法模块一定要独立出来测试好,再接Django;文档里的图表要画得清楚,推荐流程图、系统架构图这种东西特别加分。希望这篇能帮你少走一些我走过的弯路,毕设一次过。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/21 13:06:48

Aras 11-sp9 汉化补丁:绕过资源绑定的Dojo前端适配方案

简介&#xff1a;本资源是面向中国PLM实施工程师、制造业IT运维人员及Aras Innovator 11 SP9终端用户的官方级汉化补丁包&#xff0c;专为解决英文界面带来的操作理解障碍与本地化适配难题。补丁完整覆盖系统菜单、按钮、提示信息等全部UI元素&#xff0c;显著降低学习成本&…

作者头像 李华
网站建设 2026/10/21 13:06:44

中国智能驾驶出海:从算法方案到全球主理人的角色跃迁

这两年汽车圈里最值得注意的变化&#xff0c;不是某款新车又拿下了多少订单&#xff0c;而是中国智能驾驶方案开始频繁出现在海外车型的配置单上。过去很长一段时间&#xff0c;国内讨论的基调是“追平海外”&#xff0c;看别人定义硬件规格、制定软件接口&#xff0c;我们跟着…

作者头像 李华
网站建设 2026/10/21 13:06:14

AI应用开发工程师学习路线:从调接口到RAG与Agent实战

如果你正在查“AI应用开发工程师学习路线”&#xff0c;大概率已经被市面上各种AI岗位搞晕了&#xff1a;算法工程师、深度学习工程师、模型训练工程师……但真正需求量稳定、转岗门槛相对友好、又能直接落地业务价值的岗位&#xff0c;其实是AI应用开发工程师。这条路线不要求…

作者头像 李华
网站建设 2026/10/21 13:04:41

AI辅助文献综述全攻略:七款工具串起选题到成稿高效工作流

刚带完一组学生的开题综述&#xff0c;我发现一个特别明显的现象&#xff1a;同样是一个方向&#xff0c;有人在文献堆里泡了半个月&#xff0c;综述还没理出头绪&#xff1b;有人用AI工具辅助&#xff0c;一周就能拿出结构完整、参考文献规范的初稿。差别不在阅读量&#xff0…

作者头像 李华
网站建设 2026/10/21 13:04:09

YOLO实战指南:从数据到部署的模型优化全流程

很多人第一次跑通YOLO的时候都挺兴奋的&#xff1a;环境搭好&#xff0c;命令跑起来&#xff0c;摄像头一开&#xff0c;框就跟着人走了。但问题往往出在第二周——当你把数据集换成自己拍的场景、把类别换成自己关心的东西&#xff0c;训练一跑&#xff0c;mAP开始拉胯&#x…

作者头像 李华
网站建设 2026/10/21 13:02:39

机器人地图构建与导航实战:从SLAM建图到AMCL定位全解析

"lingbot-map"这个项目名字一出来&#xff0c;我脑子里第一反应就是&#xff1a;这不是个简单的"画个地图"的Demo。在机器人领域&#xff0c;凡是带"-map"后缀的项目&#xff0c;基本都绕不开地图构建、定位、导航这几个硬骨头。我拿到这个标题后…

作者头像 李华