ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于Django的电影推荐系统实战:从协同过滤到混合策略

基于Django的电影推荐系统实战:从协同过滤到混合策略 简介这是一套面向Python Web开发初学者与推荐系统入门者的完整电影推荐系统实战项目基于Django框架实现协同过滤算法解决用户个性化观影需求匹配问题适用于课程设计、毕业设计及全栈开发能力训练。资源共725个文件包含39个核心Python源码含数据处理、推荐逻辑与Django视图、41个Vue组件实现前端交互与动态渲染、164个JavaScript脚本支撑评分、搜索与实时推荐、53个CSS样式文件及大量SVG/GIF/图片资源整体压缩包37.65MB结构清晰前后端分离特征明显。已有447人学习下载项目附带可直接运行的批处理脚本install.bat/run.bat/build.bat和备份文件便于快速部署与版本回溯目录中保留了多个.bak备份文件及完整静态资源链路有助于理解开发迭代过程与工程化组织方式。1. 项目缘起为什么从零构建一个电影推荐系统如果你对Python和Web开发感兴趣想找一个能串联起前后端、数据库、算法和实际业务逻辑的实战项目那么基于Django的电影推荐系统绝对是一个绝佳的选择。这不仅仅是一个“增删改查”的CRUD应用它迫使你去思考数据如何流动、算法如何落地、用户体验如何优化是一个能让你技术栈得到全方位锻炼的“毕业设计级”项目。我之所以选择这个方向是因为在带新人或者自己技术复盘时发现很多朋友对Django的理解停留在表单和模板层面对如何将机器学习模型集成到Web服务中如何设计一个可扩展的推荐架构感到迷茫。这个项目恰好能填补这块空白。从技术生态来看Django在国内Web开发领域尤其是中后台、内容管理类应用上拥有非常广泛和稳定的开发者基础。它的“开箱即用”特性对于快速构建原型和稳健的业务系统优势明显。而Python作为数据科学和机器学习领域的事实标准语言其丰富的库如pandas, scikit-learn, numpy让推荐算法的实现变得异常高效。将两者结合你就能搭建一个从数据预处理、模型训练到最终通过Web界面提供个性化服务的完整管道。这个项目源码的价值在于它展示的是一套“生产就绪”的思考框架和实现路径而不仅仅是几段孤立的代码。2. 系统架构全景从数据到推荐的一站式设计在动手写第一行代码之前我们必须把整个系统的骨架搭清楚。一个完整的电影推荐系统远不止一个算法模型它是一系列组件协同工作的结果。基于Django的特性我设计了一个典型的分层架构这能确保代码清晰、易于维护和扩展。2.1 核心数据模型设计数据是推荐系统的血液。在models.py中我们需要精心设计几个核心实体。用户模型Django自带的User模型通常不够用我们需要扩展它。一个常见的做法是使用OneToOneField关联一个Profile模型。这个Profile模型会存储用于推荐的关键元数据。from django.contrib.auth.models import User from django.db import models class UserProfile(models.Model): user models.OneToOneField(User, on_deletemodels.CASCADE) # 用于协同过滤的向量可先留空由后续计算填充 cf_vector models.TextField(nullTrue, blankTrue) # 用户注册时选择的兴趣标签用于冷启动 preferred_genres models.CharField(max_length255, blankTrue) # 其他统计信息如平均评分 average_rating models.FloatField(default0.0)电影模型这是系统的核心。除了基本信息我们需要存储能被算法利用的特征。class Movie(models.Model): title models.CharField(max_length200) overview models.TextField(blankTrue) # 简介用于内容分析 release_date models.DateField(nullTrue, blankTrue) # 关键将类型存储为逗号分隔的字符串如“Action,Adventure,Sci-Fi” genres models.CharField(max_length500) # 用于内容推荐的向量如基于简介的TF-IDF向量或嵌入向量 content_vector models.TextField(nullTrue, blankTrue) # 用于协同过滤的向量物品向量 cf_vector models.TextField(nullTrue, blankTrue) poster_url models.URLField(blankTrue) # 海报链接评分模型连接用户和电影的桥梁是协同过滤算法的基石。class Rating(models.Model): user models.ForeignKey(User, on_deletemodels.CASCADE) movie models.ForeignKey(Movie, on_deletemodels.CASCADE) rating models.FloatField() # 评分如1-5分 created_at models.DateTimeField(auto_now_addTrue) class Meta: unique_together (user, movie) # 防止同一用户对同一电影重复评分这个设计的关键在于为UserProfile和Movie预留了向量字段cf_vector,content_vector。这是一种务实的设计将预先计算好的、可能很长的数值向量列表以JSON字符串的形式存入TextField。虽然这不是最优的数据库设计违背了第一范式但在项目初期或数据量不大时它能极大地简化开发复杂度避免引入专门的向量数据库。当系统规模扩大时可以平滑地将这些字段迁移到Redis或专业的向量数据库中。2.2 前后端交互与业务逻辑分层Django的MTVModel-Template-View模式在这里得到了完美体现。我建议采用以下分层数据访问层即上述的Models由Django ORM负责。业务逻辑层这是核心。我通常会创建一个独立的Python包例如recommendation/在里面放置algorithms/存放各种推荐算法类如CollaborativeFiltering,ContentBased。services/存放推荐服务类如RecommendationService。它负责调用算法、处理缓存、组装最终给前端的数据。这是协调一切的中枢。utils/存放数据预处理、向量计算等工具函数。视图层Django的Views。它们应该非常“薄”主要职责是接收HTTP请求调用RecommendationService获取结果然后返回响应JSON或渲染模板。表现层Templates。用于渲染HTML页面。对于现代开发你也可以考虑将Django纯粹作为后端API使用Django REST framework前端使用Vue.js或React但这会增加项目的复杂度。本方案采用Django模板以保持项目自包含。注意很多初学者会把复杂的算法逻辑直接写在View里导致View函数臃肿不堪难以测试和维护。务必坚持“胖模型瘦视图”或“服务层”的理念将业务逻辑剥离出去。3. 推荐算法实战协同过滤与内容推荐的混合策略单一的推荐算法往往有局限性。一个健壮的系统通常采用混合策略。这里我实现两种最经典、最有效的算法并讲解如何将它们结合起来。3.1 基于用户的协同过滤实现协同过滤的核心思想是“物以类聚人以群分”。我们需要计算用户之间的相似度。这里采用余弦相似度。首先我们需要一个函数来构建用户-物品评分矩阵。由于数据可能稀疏我们使用scipy.sparse中的csr_matrix来高效存储。# recommendation/utils/matrix_builder.py import numpy as np from scipy.sparse import csr_matrix from django.contrib.auth.models import User from .models import Rating, Movie def build_user_item_matrix(): 构建用户-物品评分矩阵。 返回: (csr_matrix, user_id_to_index_map, movie_id_to_index_map) ratings Rating.objects.all().select_related(user, movie) users User.objects.all() movies Movie.objects.all() user_id_to_index {user.id: idx for idx, user in enumerate(users)} movie_id_to_index {movie.id: idx for idx, movie in enumerate(movies)} data [] row_indices [] col_indices [] for rating in ratings: row_indices.append(user_id_to_index[rating.user_id]) col_indices.append(movie_id_to_index[rating.movie_id]) data.append(rating.rating) matrix csr_matrix((data, (row_indices, col_indices)), shape(len(users), len(movies))) return matrix, user_id_to_index, movie_id_to_index接下来实现协同过滤推荐器# recommendation/algorithms/collaborative_filtering.py from sklearn.metrics.pairwise import cosine_similarity import numpy as np from django.core.cache import cache class UserBasedCF: def __init__(self, k20): :param k: 选取最相似的K个邻居 self.k k self.sim_matrix None self.user_index_map None def fit(self, user_item_matrix, user_index_map): 训练模型计算用户相似度矩阵 # 计算余弦相似度 self.sim_matrix cosine_similarity(user_item_matrix) self.user_index_map user_index_map # 可以将相似度矩阵缓存避免每次请求都重复计算 cache.set(cf_user_sim_matrix, self.sim_matrix, timeout60*60*24) cache.set(cf_user_index_map, self.user_index_map, timeout60*60*24) def recommend(self, target_user_id, top_n10): 为目标用户生成推荐 if self.sim_matrix is None: # 尝试从缓存加载 self.sim_matrix cache.get(cf_user_sim_matrix) self.user_index_map cache.get(cf_user_index_map) if self.sim_matrix is None: raise ValueError(Model not fitted and no cache found.) if target_user_id not in self.user_index_map: # 新用户无法使用协同过滤退回热门推荐或基于内容的推荐 return [] target_idx self.user_index_map[target_user_id] # 获取目标用户对所有电影的评分原始矩阵中的一行 # 注意在实际中我们需要重新加载矩阵或从数据库获取目标用户的评分 # 这里为简化假设我们能直接拿到目标用户的评分向量 target_ratings # 更务实的做法是在服务层处理数据获取 # 计算预测评分相似度加权平均 user_similarities self.sim_matrix[target_idx] # 找到最相似的K个用户排除自己 similar_user_indices np.argsort(user_similarities)[-self.k-1:-1][::-1] # 这里需要实际的评分矩阵来计算预测分代码略复杂核心公式如下 # pred_rating mean_target_rating sum(sim * (neighbor_rating - mean_neighbor_rating)) / sum(|sim|) # 具体实现需要仔细处理矩阵运算和缺失值。 # 简化返回假设我们计算出了每个未评分电影的预测分 pred_scores # movie_indices np.argsort(pred_scores)[-top_n:][::-1] # 再将 movie_indices 转换回 movie_id # recommended_movie_ids [index_to_movie_id[i] for i in movie_indices] # 由于完整实现较长此处返回一个示意流程 return self._generate_recommendations_impl(target_idx, similar_user_indices, top_n)实操心得纯内存的协同过滤在用户和物品数量上万时计算相似度矩阵会非常耗时且占用内存。在实际项目中我通常不会在每次请求时计算而是将其作为一个离线任务。例如使用Celery定时如每天凌晨计算所有用户的相似度并将结果如每个用户最相似的K个用户ID列表存入缓存或数据库。在线推荐时直接读取这些预计算的结果进行加权评分预测速度极快。3.2 基于内容的推荐实现当用户行为数据评分很少时冷启动问题基于内容的推荐就派上用场了。它通过分析用户过去喜欢的物品的属性如电影类型、简介来推荐具有相似属性的新物品。我们利用电影的genres和overview字段。对于类型我们可以进行多热编码。对于简介我们使用TF-IDF将其转化为向量。# recommendation/algorithms/content_based.py from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.metrics.pairwise import linear_kernel import numpy as np from django.core.cache import cache class ContentBasedRecommender: def __init__(self): self.tfidf_matrix None self.movie_index_map None self.vectorizer None def fit(self, movies): 训练TF-IDF模型并计算电影相似度矩阵 # 准备文本数据将类型和简介结合 text_data [] self.movie_index_map {} for idx, movie in enumerate(movies): self.movie_index_map[movie.id] idx # 组合文本类型 “ ” 简介 text f{movie.genres} {movie.overview} text_data.append(text) self.vectorizer TfidfVectorizer(stop_wordsenglish, max_features5000) self.tfidf_matrix self.vectorizer.fit_transform(text_data) # 计算电影之间的余弦相似度基于内容 self.content_sim_matrix linear_kernel(self.tfidf_matrix, self.tfidf_matrix) cache.set(cb_sim_matrix, self.content_sim_matrix, timeout60*60*24) cache.set(cb_movie_index_map, self.movie_index_map, timeout60*60*24) def recommend(self, liked_movie_ids, top_n10): 根据用户喜欢的电影推荐相似的电影 if self.content_sim_matrix is None: self.content_sim_matrix cache.get(cb_sim_matrix) self.movie_index_map cache.get(cb_movie_index_map) if not liked_movie_ids: return [] # 计算用户画像将所有喜欢电影的向量平均 liked_indices [self.movie_index_map[mid] for mid in liked_movie_ids if mid in self.movie_index_map] if not liked_indices: return [] user_profile np.mean(self.tfidf_matrix[liked_indices].toarray(), axis0).reshape(1, -1) # 计算用户画像与所有电影的相似度 # 注意这里我们直接用用户画像向量去和TF-IDF矩阵算相似度 similarity_scores linear_kernel(user_profile, self.tfidf_matrix).flatten() # 排除已经喜欢的电影 for idx in liked_indices: similarity_scores[idx] -1 # 设为负值确保不被选中 # 获取最相似的前N个电影索引 top_indices np.argsort(similarity_scores)[-top_n:][::-1] # 将索引转换回电影ID index_to_movie_id {v: k for k, v in self.movie_index_map.items()} recommended_ids [index_to_movie_id[i] for i in top_indices if i in index_to_movie_id] return recommended_ids3.3 混合推荐策略与服务层封装有了两种算法我们需要一个策略来混合它们。一个简单有效的加权混合方法如下# recommendation/services/recommendation_service.py from django.core.cache import cache from ..algorithms.collaborative_filtering import UserBasedCF from ..algorithms.content_based import ContentBasedRecommender from ..models import Rating, Movie class RecommendationService: def __init__(self): self.cf_model UserBasedCF(k20) self.cb_model ContentBasedRecommender() self._load_or_train_models() def _load_or_train_models(self): 加载或训练模型。这是一个简化示例生产环境应使用离线任务。 # 尝试从缓存加载协同过滤的相似度矩阵 cf_sim_cache cache.get(cf_user_sim_matrix) # ... 类似地加载其他缓存 if cf_sim_cache is None: # 如果没有缓存则进行训练数据量大时这应该在后台异步完成 matrix, user_map, _ build_user_item_matrix() self.cf_model.fit(matrix, user_map) # 内容模型同理 ... def get_hybrid_recommendations(self, user, top_n20): 获取混合推荐。 策略 1. 如果用户有足够评分10协同过滤权重高如0.7内容推荐权重低0.3。 2. 如果用户评分少冷启动则主要依赖内容推荐并加入热门电影作为兜底。 user_ratings_count Rating.objects.filter(useruser).count() liked_movie_ids list(Rating.objects.filter(useruser, rating__gte4.0).values_list(movie_id, flatTrue)) cf_recommendations [] cb_recommendations [] # 获取协同过滤推荐 if user_ratings_count 5: try: cf_recommendations self.cf_model.recommend(user.id, top_ntop_n*2) # 多取一些用于混合 except ValueError: # 模型未就绪或用户不在矩阵中 pass # 获取基于内容的推荐 if liked_movie_ids: cb_recommendations self.cb_model.recommend(liked_movie_ids, top_ntop_n*2) # 混合与去重 all_recs {} # 给协同过滤推荐赋予基础权重 for idx, movie_id in enumerate(cf_recommendations): all_recs[movie_id] all_recs.get(movie_id, 0) 0.7 * (1 - idx/len(cf_recommendations)) # 排名衰减 # 给内容推荐赋予基础权重 for idx, movie_id in enumerate(cb_recommendations): all_recs[movie_id] all_recs.get(movie_id, 0) 0.3 * (1 - idx/len(cb_recommendations)) # 如果用户评分很少调整权重偏向内容推荐 if user_ratings_count 5: for movie_id in all_recs: if movie_id in cb_recommendations: all_recs[movie_id] * 1.5 # 提升内容推荐项的权重 # 按最终权重排序取前top_n个 sorted_recommendations sorted(all_recs.items(), keylambda x: x[1], reverseTrue)[:top_n] final_movie_ids [item[0] for item in sorted_recommendations] # 如果最终结果不足用热门电影补足兜底策略 if len(final_movie_ids) top_n: popular_movies Movie.objects.annotate(rating_countCount(rating)).order_by(-rating_count)[:top_n] popular_ids [m.id for m in popular_movies if m.id not in final_movie_ids] final_movie_ids.extend(popular_ids[:top_n - len(final_movie_ids)]) return Movie.objects.filter(id__infinal_movie_ids)这个RecommendationService类就是系统的大脑。它在__init__中初始化模型提供了get_hybrid_recommendations这个核心接口。View层只需要调用这个接口传入当前用户对象就能拿到混合了多种策略的、排好序的电影推荐列表。4. Django视图与前端展示将推荐结果送达用户有了强大的后端服务我们需要通过Django的视图和模板将其呈现给用户。这里设计两个核心页面主页显示个性化推荐和电影详情页提供评分入口和相似电影推荐。4.1 核心视图实现首先在views.py中创建主页视图。这个视图需要处理登录和未登录两种状态。# movie_rec/views.py from django.contrib.auth.decorators import login_required from django.shortcuts import render from recommendation.services.recommendation_service import RecommendationService def home(request): 系统主页。为登录用户展示个性化推荐为未登录用户展示热门电影。 context {} if request.user.is_authenticated: # 调用推荐服务 rec_service RecommendationService() recommended_movies rec_service.get_hybrid_recommendations(request.user, top_n12) context[recommended_movies] recommended_movies context[section] personalized else: # 未登录用户展示热门电影或最新电影 from django.db.models import Count from .models import Movie popular_movies Movie.objects.annotate(num_ratingsCount(rating)).order_by(-num_ratings)[:12] context[recommended_movies] popular_movies context[section] popular return render(request, movie_rec/home.html, context)电影详情页视图除了展示电影信息还应该显示基于内容的相似电影推荐。def movie_detail(request, movie_id): 电影详情页。展示电影信息、用户评分并提供相似电影推荐。 from .models import Movie, Rating from recommendation.services.recommendation_service import RecommendationService movie get_object_or_404(Movie, idmovie_id) user_rating None if request.user.is_authenticated: try: user_rating Rating.objects.get(userrequest.user, moviemovie) except Rating.DoesNotExist: pass # 获取基于内容的相似电影推荐不依赖用户行为 rec_service RecommendationService() # 这里我们直接使用内容推荐模型传入当前电影ID作为“喜欢”的电影 similar_movie_ids rec_service.cb_model.recommend([movie_id], top_n6) similar_movies Movie.objects.filter(id__insimilar_movie_ids) context { movie: movie, user_rating: user_rating.rating if user_rating else None, similar_movies: similar_movies, } return render(request, movie_rec/movie_detail.html, context)处理用户评分的视图。这是一个POST请求需要更新数据库并可能触发用户推荐向量的更新异步任务。from django.http import JsonResponse from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_POST require_POST csrf_exempt login_required def rate_movie(request): 处理用户评分。AJAX调用。 movie_id request.POST.get(movie_id) rating_value float(request.POST.get(rating)) # 验证评分范围如1-5 if not (1 rating_value 5): return JsonResponse({success: False, error: Invalid rating value.}) movie get_object_or_404(Movie, idmovie_id) rating_obj, created Rating.objects.update_or_create( userrequest.user, moviemovie, defaults{rating: rating_value} ) # 评分后可以触发一个异步任务来更新该用户的协同过滤向量如果采用增量更新策略 # 例如celery_task_update_user_vector.delay(request.user.id) # 对于简单项目也可以选择在下次离线计算时统一更新。 return JsonResponse({success: True, created: created})4.2 前端模板与交互主页模板home.html需要优雅地展示电影网格。这里使用Bootstrap进行快速布局。!-- templates/movie_rec/home.html -- {% extends base.html %} {% block content %} div classcontainer mt-4 h2 {% if section personalized %} 为您推荐的电影 {% else %} 热门电影 {% endif %} /h2 div classrow {% for movie in recommended_movies %} div classcol-lg-3 col-md-4 col-sm-6 mb-4 div classcard h-100 img src{{ movie.poster_url|default:https://via.placeholder.com/300x450 }} classcard-img-top alt{{ movie.title }} styleheight: 300px; object-fit: cover; div classcard-body h5 classcard-title{{ movie.title }}/h5 p classcard-text text-muted small{{ movie.release_date|date:Y }} | {{ movie.genres|truncatewords:3 }}/small /p p classcard-text{{ movie.overview|truncatewords:20 }}/p /div div classcard-footer a href{% url movie_detail movie.id %} classbtn btn-outline-primary btn-sm查看详情/a /div /div /div {% empty %} div classcol-12 p classtext-center暂无推荐。请先给一些电影评分吧/p /div {% endfor %} /div /div {% endblock %}电影详情页movie_detail.html需要集成评分功能。!-- templates/movie_rec/movie_detail.html -- {% extends base.html %} {% block content %} div classcontainer mt-4 div classrow div classcol-md-4 img src{{ movie.poster_url|default:https://via.placeholder.com/300x450 }} classimg-fluid rounded alt{{ movie.title }} /div div classcol-md-8 h1{{ movie.title }}/h1 pstrong上映日期:/strong {{ movie.release_date|date:Y-m-d }}/p pstrong类型:/strong {{ movie.genres }}/p hr h4简介/h4 p{{ movie.overview }}/p hr {% if user.is_authenticated %} h4我的评分/h4 div classrating-section p当前评分: span idcurrent-rating{{ user_rating|default:尚未评分 }}/span/p div classstar-rating {% for i in 12345|make_list %} span classstar># movie_rec/management/commands/import_movielens.py import pandas as pd from django.core.management.base import BaseCommand from django.contrib.auth.models import User from movie_rec.models import Movie, Rating, UserProfile class Command(BaseCommand): help Import MovieLens dataset (movies.csv, ratings.csv) def add_arguments(self, parser): parser.add_argument(movies_file, typestr, helpPath to movies.csv) parser.add_argument(ratings_file, typestr, helpPath to ratings.csv) def handle(self, *args, **options): # 1. 导入电影 movies_df pd.read_csv(options[movies_file]) self.stdout.write(fImporting {len(movies_df)} movies...) movie_map {} # 存储MovieLens ID到我们数据库ID的映射 for _, row in movies_df.iterrows(): # MovieLens数据中类型是Action|Adventure|Sci-Fi格式 genres row[genres].replace(|, , ) movie, created Movie.objects.get_or_create( titlerow[title], defaults{ overview: , # MovieLens没有简介可以留空或后续从TMDB补充 genres: genres, } ) movie_map[row[movieId]] movie.id if created: self.stdout.write(f Created: {movie.title}) # 2. 导入用户和评分 ratings_df pd.read_csv(options[ratings_file]) self.stdout.write(fImporting {len(ratings_df)} ratings...) user_map {} for _, row in ratings_df.iterrows(): user_id row[userId] if user_id not in user_map: # 创建Django用户使用虚拟用户名和邮箱 user, _ User.objects.get_or_create( usernamefuser_{user_id}, defaults{email: fuser_{user_id}example.com} ) UserProfile.objects.get_or_create(useruser) user_map[user_id] user movie_id movie_map.get(row[movieId]) if movie_id: Rating.objects.update_or_create( useruser_map[user_id], movie_idmovie_id, defaults{rating: row[rating]} ) self.stdout.write(self.style.SUCCESS(Data import completed successfully!))运行命令python manage.py import_movielens /path/to/movies.csv /path/to/ratings.csv。导入后你就有了一个充满评分数据的数据库可以立即用于训练协同过滤模型。5.2 模型预热与缓存策略在首次启动服务或数据更新后我们需要“预热”推荐模型即预先计算好相似度矩阵并存入缓存。我们可以创建另一个管理命令或使用Django的ready()信号在启动时执行注意对于大数据集这会阻塞启动最好用异步任务。# recommendation/management/commands/warmup_recommendations.py from django.core.management.base import BaseCommand from django.core.cache import cache from recommendation.utils.matrix_builder import build_user_item_matrix from recommendation.algorithms.collaborative_filtering import UserBasedCF from recommendation.algorithms.content_based import ContentBasedRecommender from movie_rec.models import Movie class Command(BaseCommand): help Warm up recommendation models and cache def handle(self, *args, **options): self.stdout.write(Warming up Collaborative Filtering model...) matrix, user_map, movie_map build_user_item_matrix() cf_model UserBasedCF(k20) cf_model.fit(matrix, user_map) self.stdout.write(self.style.SUCCESS(CF model warmed up.)) self.stdout.write(Warming up Content-Based model...) movies Movie.objects.all() cb_model ContentBasedRecommender() cb_model.fit(movies) self.stdout.write(self.style.SUCCESS(CB model warmed up.)) # 也可以预计算一些热门推荐存入缓存 from django.db.models import Count popular_movies list(Movie.objects.annotate(rating_countCount(rating)).order_by(-rating_count)[:50].values_list(id, flatTrue)) cache.set(global_popular_movies, popular_movies, timeoutNone) # 永不过期除非手动更新 self.stdout.write(self.style.SUCCESS(Popular movies cached.))将这个命令添加到服务器的启动脚本中或者设置为一个定时的Celery任务例如每小时运行一次增量更新。5.3 部署注意事项与性能优化将Django项目部署到生产环境如Nginx Gunicorn时针对推荐系统需要特别关注以下几点缓存无处不在除了Django自带的缓存框架可配置为Redis在推荐服务内部也要积极缓存。例如RecommendationService.get_hybrid_recommendations方法可以为每个用户生成一个缓存键如f”recs:{user.id}”并将推荐结果缓存10-30分钟。这能极大减轻数据库和计算压力。异步任务处理用户评分、模型重新训练、向量更新等耗时操作务必使用Celery等异步任务队列。不要让HTTP请求线程等待这些操作完成。数据库优化为Rating表的(user, movie)复合字段建立唯一索引我们已经用unique_together做了。为Rating表的user_id和movie_id分别建立外键索引。如果电影数量巨大考虑对Movie表的title和genres字段建立索引以加速搜索和过滤。向量存储分离当用户和物品数量达到十万、百万级别时将cf_vector和content_vector这类大字段从MySQL/PostgreSQL中移出存入Redis或专门的向量数据库如Milvus, Weaviate。这能大幅提升相似度计算和检索的效率。API响应优化在返回推荐列表时使用select_related或prefetch_related来避免N1查询问题一次性获取电影的所有关联信息。# 在视图中优化查询 recommended_movies Movie.objects.filter(id__inmovie_ids).select_related(...)这个项目从设计到实现的每一步都贯穿着对性能、可维护性和用户体验的考量。它不仅仅是一个Demo更是一个展示了如何将机器学习算法与成熟的Web框架进行工程化整合的范本。当你按照这个流程走下来你会对Django的项目结构、业务分层、缓存策略、异步处理以及推荐系统的核心逻辑有一个非常扎实和深入的理解。本文还有配套的精品资源点击获取
返回列表