简介:这份资源是面向大数据与推荐系统方向学习者、毕业设计选题学生的完整项目资料,围绕基于Spark的电影推荐系统展开,重点解决海量影片场景下个性化筛选与精准推荐的问题。内容涵盖绪论、开发技术介绍、系统分析与设计、系统实现及测试等章节,涉及Spark、MongoDB与Web技术,并详细讲解基于人口统计学、基于内容以及协同过滤三类推荐算法的原理与设计思路,同时包含用户登录注册、个性化推荐、电影搜索、用户评分等功能模块的实现与功能、性能测试环节。资源包共1个docx文件,约7.46MB,为论文与源码合一的文档型资料,结构完整、章节清晰,便于按模块查阅与参考。目前已有206人学习下载,适合需要完成推荐系统课程设计、毕业设计或希望理解协同过滤与Spark离线实时推荐流程的读者,可据此梳理系统架构、算法选型与测试验证的完整思路。
1. 从信息过载到精准触达:基于 Spark 的电影推荐系统要解决什么
打开任何一个影视平台,首页堆着几百部片子,翻了三屏还是不知道看什么——这不是用户挑剔,是信息过载下的选择瘫痪。传统搜索只能解决“我知道我要看什么”的场景,但大多数时候用户自己都不清楚想看什么。这就是推荐系统要填的坑:在用户没有明确意图时,把可能感兴趣的内容主动推到眼前。这套基于 Spark 的电影推荐系统,面向的正是这个场景。它适合两类人参考:一是正在做大数据方向毕设或课设的学生,需要一套完整可跑通的推荐系统方案;二是想了解工业级推荐链路怎么搭的开发者,从离线统计到实时推荐,从算法设计到前后端联调,这套方案给出了一个可复现的工程骨架。核心思路不复杂:用 Spark 做分布式计算引擎扛住数据量,用协同过滤加内容推荐做算法兜底,用 MongoDB 存结果、Redis 做缓存、Elasticsearch 撑搜索,最后通过 Web 端把推荐结果呈现给用户。
2. 需求拆解与技术选型:为什么是 Spark + 混合推荐
2.1 功能需求:四类推荐场景缺一不可
电影推荐系统看起来只是“猜你喜欢”,但落到工程上,用户在不同页面、不同时机需要的是不同类型的推荐。这套方案把需求拆成四块:
- 离线个性化推荐:基于用户历史评分行为,用协同过滤算出每个用户的专属推荐列表,每天定时跑一次,写入数据库供前端读取。这是推荐系统的基本盘,解决“老用户看什么”的问题。
- 实时推荐:用户刚评完一部电影,系统立刻调整推荐结果。比如刚给《星际穿越》打了五星,马上在首页推《盗梦空间》。这需要 Spark Streaming 消费实时评分流,增量更新推荐结果。
- 热门推荐:统计历史热门和近期热门影片,解决新用户冷启动问题。新注册用户没有行为数据,协同过滤跑不出结果,但“最近一周评分最高的 20 部电影”这种统计推荐可以直接用。
- 详情页相似推荐:用户点进某部电影详情页,根据该片的类型、导演、主演等标签,推荐内容相似的影片。这是基于内容的推荐算法的主场,不依赖用户评分数据。
这四类推荐覆盖了用户从进入首页到浏览详情页的完整路径,少了任何一块,推荐体验都会有断档。
2.2 技术选型:Spark 做计算,MongoDB 存结果,Redis 扛缓存
选 Spark 不是跟风。推荐系统的核心计算是迭代式的——协同过滤要反复计算用户相似度矩阵、物品相似度矩阵,MapReduce 每轮迭代都落磁盘,I/O 开销扛不住。Spark 把中间结果放内存,迭代计算速度能快一个数量级。而且 Spark 生态里有 Spark SQL 做统计、Spark Streaming 做实时、MLlib 做机器学习,一套框架覆盖离线到实时的全链路,不用在多个引擎之间倒数据。
存储层选 MongoDB 而不是 MySQL,原因在于推荐结果的数据结构不固定。不同推荐场景产出的字段不一样:离线推荐有 userId、movieId、score,热门推荐有 movieId、count、averageRating,详情页推荐有 movieId、similarMovieIds。MongoDB 的文档模型天然适配这种半结构化数据,加字段不用改表结构。Redis 用来缓存热门推荐和实时推荐结果,前端请求直接读缓存,不用每次都查 MongoDB。Elasticsearch 负责电影搜索,支持模糊匹配和分词检索,比 MongoDB 的正则查询效率高得多。
后端用 Spring 框架搭 RESTful 接口,前端用 AngularJS 做响应式页面。这套组合不算新,但胜在稳定、资料多、踩坑成本低。
2.3 推荐算法:三种策略各管一段
系统里用了三种推荐算法,不是随便选的,每种对应不同的数据条件和推荐场景:
基于人口统计学的推荐用在离线统计模块。它不需要用户的历史行为数据,只依赖用户的基本属性(年龄、性别、职业等)做分组,同组用户喜欢的东西互相推荐。这个算法准确率一般,但胜在简单、可解释性强,而且完全没有冷启动问题。在本系统里,它主要用来做热门影片统计和评分排行,属于兜底策略。
基于内容的推荐用在详情页推荐和实时推荐。核心逻辑是给每部电影打标签(类型、导演、演员、年代),计算电影之间的相似度。用户喜欢《教父》,系统就找标签相似的黑帮片推给他。这个算法不依赖用户评分数据,新电影上线就能被推荐,解决了物品冷启动问题。缺点是推荐结果容易局限在用户已有的兴趣范围内,缺乏惊喜感。
基于协同过滤的推荐用在离线个性化推荐。这是推荐系统里最经典的算法,分 UserCF 和 ItemCF 两条路线。UserCF 找相似用户,把相似用户喜欢的电影推给目标用户;ItemCF 找相似物品,把用户喜欢的物品的相似物品推给他。本系统采用 ItemCF 为主,因为电影的数量远小于用户数量,物品相似度矩阵更稳定,更新频率也更低。
三种算法混合使用,协同过滤负责个性化,内容推荐负责实时和详情页,人口统计学推荐负责冷启动和兜底。单用一种算法都有明显短板,混合推荐是工程上的务实选择。
3. 总体架构设计:分层结构与模块职责划分
3.1 四层架构:数据层、计算层、服务层、展示层
系统整体采用分层架构,从上到下依次是展示层、服务层、计算层和数据层。展示层是 AngularJS 写的前端页面,负责渲染推荐结果、搜索框、评分组件。服务层是 Spring 框架搭的 Web 服务,对外提供 RESTful 接口,对内调用计算层产出的推荐结果。计算层是 Spark 集群,跑离线统计、协同过滤、实时流处理三类任务。数据层包括 MongoDB(存业务数据和推荐结果)、Redis(缓存热点数据)、Elasticsearch(支撑搜索)。
分层的意义在于职责隔离。前端改版不影响推荐算法,算法调参不影响数据库结构,每层可以独立迭代。对于毕设或课设来说,这种分层结构在论文里也好写——每层单独一章,逻辑清晰。
3.2 离线推荐模块:Spark SQL + 协同过滤的批处理链路
离线推荐是系统的核心模块,每天凌晨跑一次,产出全量用户的个性化推荐列表。整个链路分四步:
第一步,从 MongoDB 读取用户评分表(ratings)和电影信息表(movies)。评分表包含 userId、movieId、score、timestamp 四个字段,电影表包含 movieId、title、genres 等字段。
第二步,用 Spark SQL 做统计推荐。包括历史热门影片 Top20、近期热门影片 Top20、电影平均评分表、各类型电影评分 Top10。这些统计结果直接写入 MongoDB 的对应集合,供前端热门推荐模块读取。
第三步,用 Spark MLlib 的 ALS 算法做协同过滤。ALS 是交替最小二乘法,专门用于矩阵分解,把用户-物品评分矩阵分解成用户隐向量矩阵和物品隐向量矩阵,通过隐向量内积预测缺失评分。核心代码如下:
// 读取评分数据,构建 DataFrame val ratings = spark.read .option("uri", mongoConfig.uri) .option("collection", "Rating") .format("com.mongodb.spark.sql") .load() .select("userId", "movieId", "score") .as[Rating] // 用 ALS 训练模型,rank=10 表示隐向量维度,maxIter=10 表示迭代次数 val als = new ALS() .setMaxIter(10) .setRank(10) .setRegParam(0.01) .setUserCol("userId") .setItemCol("movieId") .setRatingCol("score") val model = als.fit(ratings) // 为所有用户生成推荐,每个用户取 Top10 val userRecs = model.recommendForAllUsers(10)这里 rank 控制隐向量维度,值越大模型越复杂,但容易过拟合;regParam 是正则化系数,防止过拟合;maxIter 是迭代次数,一般 10 到 20 次就能收敛。训练完成后,recommendForAllUsers为每个用户生成推荐列表,结果写入 MongoDB 的 UserRecs 集合。
第四步,把离线推荐结果同步到 Redis,设置过期时间为 24 小时。前端请求推荐数据时,优先读 Redis,缓存未命中再查 MongoDB。
3.3 实时推荐模块:Spark Streaming 消费评分流
实时推荐解决的是“用户刚评完分,推荐结果立刻变”的需求。实现方式是 Spark Streaming 消费 Kafka 里的评分事件流,每来一条新评分,就触发一次增量推荐计算。
具体逻辑是:用户 u 对电影 m 打了分,系统从 Redis 读取用户 u 最近的 K 次评分记录,从 MongoDB 读取电影 m 的相似电影列表,找出与 m 最相似的 N 部电影,结合用户历史评分预测用户对这些电影的感兴趣程度,取 Top10 作为实时推荐结果,更新到 Redis 的实时推荐缓存中。
// 消费 Kafka 评分流 val stream = KafkaUtils.createDirectStream[String, String]( ssc, PreferConsistent, Subscribe[String, String](Array("rating-topic"), kafkaParams) ) stream.foreachRDD { rdd => rdd.foreach { record => val rating = parseJson(record.value()) // 获取该电影的相似电影列表 val similarMovies = getSimilarMovies(rating.movieId) // 结合用户历史评分计算推荐分数 val recommendations = computeRecommendations(rating.userId, similarMovies) // 更新 Redis 中的实时推荐结果 updateRedisRecommendations(rating.userId, recommendations) } }实时推荐的计算量不大,因为只针对当前评分用户做增量更新,不需要全量重算。但要注意 Kafka 的消费速度与 Spark Streaming 的批处理间隔要匹配,批处理间隔太短会导致频繁的小批量任务,调度开销大;太长则实时性打折扣。一般设 5 到 10 秒比较合适。
3.4 数据存储设计:MongoDB 集合划分与 Redis 缓存策略
MongoDB 里建了六张核心表:
| 集合名 | 用途 | 写入时机 |
|---|---|---|
| User | 用户注册信息 | 用户注册时 |
| Rating | 用户评分记录 | 用户评分时 |
| AverageMovies | 电影平均评分 | 离线任务每天更新 |
| GenresTopMovies | 各类型电影 Top10 | 离线任务每天更新 |
| RateMoreMovies | 历史热门电影 | 离线任务每天更新 |
| RateMoreRecentlyMovies | 近期热门电影 | 离线任务每天更新 |
| UserRecs | 用户个性化推荐 | 离线任务每天更新 |
Redis 缓存三类数据:热门推荐列表(过期时间 1 小时)、实时推荐结果(过期时间 30 分钟)、电影详情页缓存(过期时间 2 小时)。缓存过期时间根据数据更新频率设定,热门推荐变化慢,过期时间长一些;实时推荐变化快,过期时间短一些。
4. 核心模块实现:从算法到接口的落地细节
4.1 协同过滤推荐:ALS 模型训练与参数调优
协同过滤是整个系统里最核心也最耗时的模块。ALS 模型的训练效果直接决定推荐质量,参数调优是绕不过去的坎。
关键参数有三个:rank(隐向量维度)、regParam(正则化系数)、maxIter(迭代次数)。rank 一般取 10 到 200 之间,太小模型欠拟合,推荐结果趋同;太大模型过拟合,对新用户泛化能力差。regParam 控制正则化强度,值越大模型越简单,防止过拟合,但太大又会导致欠拟合。maxIter 一般 10 到 20 次,可以通过观察 RMSE(均方根误差)随迭代次数的变化曲线来确定是否收敛。
// 用交叉验证选择最优参数 val paramGrid = new ParamGridBuilder() .addGrid(als.rank, Array(10, 50, 100)) .addGrid(als.regParam, Array(0.01, 0.1, 1.0)) .build() val evaluator = new RegressionEvaluator() .setMetricName("rmse") .setLabelCol("score") .setPredictionCol("prediction") val cv = new CrossValidator() .setEstimator(als) .setEvaluator(evaluator) .setEstimatorParamMaps(paramGrid) .setNumFolds(3) val cvModel = cv.fit(trainingData)交叉验证把训练集分成 3 份,轮流用其中 2 份训练、1 份验证,选 RMSE 最低的参数组合。这个过程比较耗时,但比手动调参靠谱。实际跑下来,rank=50、regParam=0.1、maxIter=10 这组参数在多数数据集上表现稳定。
训练完成后,用model.recommendForAllUsers(10)为每个用户生成 Top10 推荐。注意这个方法返回的是所有用户的推荐列表,如果用户量很大,结果集会非常大,写入 MongoDB 时要分批写,避免内存溢出。
4.2 基于内容的推荐:TF-IDF 特征提取与相似度计算
基于内容的推荐用在详情页和实时推荐模块,核心是计算电影之间的相似度。每部电影用一组标签表示,标签来自类型、导演、主演、年代等字段。把这些标签拼接成一个文本串,用 TF-IDF 做特征提取,再算余弦相似度。
// 把电影标签拼接成文本 val movieTags = movies.map { movie => val genres = movie.genres.replace("|", " ") val directors = movie.directors.mkString(" ") val actors = movie.actors.take(5).mkString(" ") (movie.movieId, s"$genres $directors $actors") } // 用 TF-IDF 提取特征向量 val tokenizer = new Tokenizer().setInputCol("tags").setOutputCol("words") val hashingTF = new HashingTF().setInputCol("words").setOutputCol("rawFeatures").setNumFeatures(1000) val idf = new IDF().setInputCol("rawFeatures").setOutputCol("features") val pipeline = new Pipeline().setStages(Array(tokenizer, hashingTF, idf)) val model = pipeline.fit(movieTagsDF) // 计算电影之间的余弦相似度 val features = model.transform(movieTagsDF).select("movieId", "features") val similarityMatrix = features.rdd.cartesian(features.rdd).map { case (a, b) => val sim = cosineSimilarity(a.getAs[Vector]("features"), b.getAs[Vector]("features")) ((a.getAs[Int]("movieId"), b.getAs[Int]("movieId")), sim) }TF-IDF 的核心思想是:一个词在文档中出现次数越多,同时在所有文档中出现次数越少,它对该文档的区分度就越高。比如“动作”这个标签在很多电影里都有,区分度低;“诺兰”只在少数电影里出现,区分度高。用 TF-IDF 加权后,导演和主演标签的权重会高于类型标签,推荐结果更精准。
相似度矩阵计算完后,每部电影取相似度最高的 20 部作为相似电影列表,存入 MongoDB 的 MovieRecs 集合。详情页推荐时直接查这个集合,不用实时计算。
4.3 实时推荐:Kafka + Spark Streaming 的流处理链路
实时推荐的链路是:用户评分 → 前端发请求 → 后端写 Kafka → Spark Streaming 消费 → 更新 Redis → 前端读 Redis。
Kafka 的 topic 设计为单分区,保证评分事件按时间顺序消费。Spark Streaming 的批处理间隔设为 5 秒,每 5 秒消费一批评分事件,逐条处理。
// Spark Streaming 处理逻辑 stream.foreachRDD { rdd => val ratings = rdd.map(_.value()).map(parseRating) ratings.foreach { rating => // 从 Redis 获取用户最近 10 次评分 val recentRatings = redisClient.lrange(s"user:${rating.userId}:ratings", 0, 9) // 从 MongoDB 获取该电影的相似电影 val similarMovies = mongoClient.getCollection("MovieRecs") .find(equal("movieId", rating.movieId)) .first() .getList("similarMovies", classOf[Int]) // 计算推荐分数:相似度 × 用户对相似电影的平均评分 val recommendations = similarMovies.map { simMovieId => val simScore = getSimilarityScore(rating.movieId, simMovieId) val userAvgScore = getUserAverageScore(rating.userId, simMovieId) (simMovieId, simScore * userAvgScore) }.sortBy(-_._2).take(10) // 更新 Redis redisClient.setex(s"user:${rating.userId}:realtimeRecs", 1800, serialize(recommendations)) } }实时推荐的计算逻辑不复杂,但要注意几个细节:一是 Redis 读取要加连接池,避免频繁创建连接;二是相似电影列表如果太大,计算量会膨胀,一般限制在 20 部以内;三是推荐结果要设过期时间,防止用户长时间不活跃后推荐结果过时。
4.4 前后端接口:RESTful API 设计与数据交互
后端用 Spring 框架暴露 RESTful 接口,前端用 AngularJS 的$http服务调用。核心接口有六个:
// 用户登录 @PostMapping("/api/login") public Response login(@RequestBody LoginRequest request) { User user = userService.findByUsername(request.getUsername()); if (user != null && passwordEncoder.matches(request.getPassword(), user.getPassword())) { String token = jwtUtil.generateToken(user.getUserId()); return Response.success(token); } return Response.error("用户名或密码错误"); } // 获取离线推荐 @GetMapping("/api/recommend/offline") public Response getOfflineRecommendations(@RequestParam int userId) { List<Recommendation> recs = recommendService.getOfflineRecs(userId); return Response.success(recs); } // 获取实时推荐 @GetMapping("/api/recommend/realtime") public Response getRealtimeRecommendations(@RequestParam int userId) { List<Recommendation> recs = recommendService.getRealtimeRecs(userId); return Response.success(recs); } // 获取热门推荐 @GetMapping("/api/recommend/hot") public Response getHotRecommendations() { List<Movie> movies = recommendService.getHotMovies(); return Response.success(movies); } // 获取相似电影 @GetMapping("/api/movie/{movieId}/similar") public Response getSimilarMovies(@PathVariable int movieId) { List<Movie> movies = movieService.getSimilarMovies(movieId); return Response.success(movies); } // 提交评分 @PostMapping("/api/rating") public Response submitRating(@RequestBody RatingRequest request) { ratingService.saveRating(request.getUserId(), request.getMovieId(), request.getScore()); kafkaProducer.sendRatingEvent(request); return Response.success(); }接口设计遵循 RESTful 风格,用 HTTP 方法区分操作类型,用 URL 路径表示资源。返回格式统一为{code, message, data}结构,前端根据 code 判断请求是否成功。评分接口在写数据库的同时发一条 Kafka 消息,触发实时推荐更新。
5. 避坑与排查:这套系统最容易翻车的五个点
5.1 坑一:Spark 集群启动后 Worker 节点注册不上
现象:start-all.sh执行后,Web UI 上只看到一个 Worker 节点,另外两个节点状态显示为 DEAD 或根本不出现。
原因:最常见的是主机名解析问题。Spark 配置里写的是主机名(如 hadoop103),但/etc/hosts里没有配对应的 IP 映射,Worker 启动后无法向 Master 注册。其次是防火墙没关,Worker 和 Master 之间的心跳端口被拦截。
解决:检查每台机器的/etc/hosts文件,确保所有节点的主机名和 IP 映射都写全了。然后systemctl stop firewalld关掉防火墙,或者开放 Spark 需要的端口(Master 默认 7077,Worker 随机端口)。最后确认spark-env.sh里的SPARK_MASTER_HOST写的是 Master 节点的主机名,不是 localhost。
5.2 坑二:ALS 模型训练报内存溢出
现象:Spark 任务跑到 ALS 训练阶段,Executor 报OutOfMemoryError,任务失败。
原因:ALS 训练时需要把用户-物品评分矩阵加载到内存,如果评分数据量大、rank 值设得高,内存占用会急剧膨胀。另外,recommendForAllUsers返回的结果集如果太大,也会撑爆内存。
解决:三个方向调。一是降低 rank 值,从 100 降到 50 甚至 10,内存占用能降一个数量级。二是增加 Executor 内存,在spark-submit时加--executor-memory 4g。三是分批处理,不要一次性为所有用户生成推荐,按 userId 范围分片,每片跑一次,结果分批写入 MongoDB。
5.3 坑三:实时推荐结果延迟高
现象:用户评完分后,首页推荐结果要等十几秒甚至更久才更新。
原因:Spark Streaming 的批处理间隔设得太长,比如设了 60 秒,那最坏情况下要等 60 秒才处理到这条评分。另外 Kafka 的消费速度如果跟不上生产速度,消息会积压,延迟越来越大。
解决:把批处理间隔降到 5 到 10 秒,同时检查 Kafka 的消费者配置,确保max.poll.records设得合理,不要一次拉太多消息导致处理不过来。如果数据量确实大,可以增加 Kafka 分区数,Spark Streaming 并行消费多个分区。
5.4 坑四:MongoDB 写入速度慢
现象:离线推荐任务跑完后,往 MongoDB 写推荐结果时速度很慢,几万条数据要写十几分钟。
原因:默认情况下每条写入都是一次单独的请求,网络往返开销大。另外,如果没有建索引,写入时维护索引也会拖慢速度。
解决:用 MongoDB 的批量写入接口bulkWrite,每 1000 条一批,速度能提升一个数量级。索引方面,在 userId 和 movieId 上建索引是必要的,但不要在写入频繁的字段上建太多索引。如果写入量特别大,可以考虑先写临时集合,写完后再原子性地重命名。
5.5 坑五:前端跨域请求被拦截
现象:前端页面调用后端接口时,浏览器控制台报 CORS 错误,请求被拦截。
原因:前端跑在 localhost:4200,后端跑在 localhost:8088,端口不同,浏览器同源策略拦截了跨域请求。
解决:在后端加 CORS 配置,允许前端域名的跨域请求。Spring 里可以用@CrossOrigin注解,或者全局配置WebMvcConfigurer的addCorsMappings方法。开发阶段可以直接允许所有来源,生产环境再收紧。
@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") .allowedOrigins("http://localhost:4200") .allowedMethods("GET", "POST", "PUT", "DELETE") .allowCredentials(true) .maxAge(3600); } }6. 验收与答辩:怎么证明这套推荐系统真的能跑
6.1 功能验收:逐模块走查清单
验收的第一步是确认每个功能模块都能正常工作。我一般会按这个清单逐项走查:
| 验收项 | 操作步骤 | 预期结果 |
|---|---|---|
| 用户注册 | 填写用户名密码,提交 | 数据库 User 表新增记录,密码加密存储 |
| 用户登录 | 用注册账号登录 | 返回 token,跳转首页 |
| 离线推荐 | 登录后查看首页推荐 | 显示 10 部推荐电影,与 UserRecs 集合数据一致 |
| 实时推荐 | 给某部电影打分,刷新首页 | 推荐列表在 10 秒内更新 |
| 热门推荐 | 查看热门榜单 | 显示历史热门和近期热门各 10 部 |
| 详情页推荐 | 点击某部电影进入详情页 | 显示 10 部相似电影 |
| 电影搜索 | 输入关键词搜索 | 返回匹配的电影列表 |
| 用户评分 | 给电影打分 | 评分写入 Rating 表,Kafka 收到消息 |
走查时要注意边界情况:新注册用户没有评分记录,离线推荐应该返回热门推荐兜底;搜索关键词为空时应该返回全部电影或提示输入;评分超出 1 到 5 的范围应该被拦截。
6.2 性能验收:响应时间与并发能力
性能验收主要看两个指标:接口响应时间和系统并发能力。
接口响应时间用 Postman 或 JMeter 测。离线推荐接口因为直接读 Redis 缓存,响应时间应该在 100 毫秒以内;实时推荐接口读 Redis,也在 100 毫秒以内;搜索接口走 Elasticsearch,响应时间在 200 毫秒以内;评分接口要写 MongoDB 和 Kafka,响应时间在 500 毫秒以内算合格。
并发能力用 JMeter 模拟 100 个用户同时请求推荐接口,观察平均响应时间和错误率。如果平均响应时间超过 1 秒,或者错误率超过 1%,说明系统扛不住这个并发量,需要加缓存或者优化查询。
Spark 任务的性能看任务执行时间。离线推荐任务在 10 万条评分数据下,ALS 训练加推荐生成应该在 5 分钟内跑完。如果超过 10 分钟,要检查是不是数据倾斜或者资源分配不合理。
6.3 答辩技巧:怎么讲清楚推荐算法的选型逻辑
答辩时老师最容易问的问题是:“你为什么用协同过滤而不是其他算法?”或者“三种算法混合推荐,怎么保证结果不冲突?”
回答的思路是:先讲场景,再讲算法匹配。协同过滤适合有用户行为数据的场景,能挖掘出用户自己都没意识到的兴趣;基于内容的推荐适合物品标签丰富的场景,解决新物品冷启动;人口统计学推荐适合新用户冷启动,虽然没有个性化但能兜底。三种算法各管一段,不是互相竞争的关系,而是互补的关系。
如果老师追问“怎么评估推荐效果”,可以讲离线评估和在线评估两个维度。离线评估用 RMSE 和 Recall@K,RMSE 衡量评分预测准确度,Recall@K 衡量 TopK 推荐的召回率。在线评估用 A/B 测试,对比不同算法的点击率和转化率。毕设阶段一般做离线评估就够了,RMSE 控制在 0.8 以内算合格。
还有一个小技巧:答辩前把系统跑一遍,截图保存关键页面和后台数据。答辩时直接展示截图,比现场跑系统稳妥得多。现场跑系统万一遇到环境问题或者网络问题,容易翻车。
从那以后我每次做推荐系统项目,都会在验收前把功能走查清单过一遍,性能测试跑一轮,答辩用的截图和数据提前准备好。这套流程走下来,基本不会出大的纰漏。希望帮到你。
本文还有配套的精品资源,点击获取