简介:面向Java方向毕业设计及SSM框架初学者,这份个性化影片推荐系统资料包提供从项目源码到论文答辩的全套内容。系统基于SSM(Spring+SpringMVC+MyBatis)架构,结合JSP前端与MySQL 5.7数据库,涵盖用户管理、电影类型管理、热门电影推荐、新闻资讯、个人中心与我的收藏等核心功能模块,管理员与普通用户权限划分清晰,适合用于课程设计、毕业设计或二次开发练习。包内共1297个文件,主要包含Java源码、JSP页面、JS交互脚本、CSS样式、SQL数据库脚本及图片素材等,另附毕业论文文档与PPT演示文件,可帮助读者快速部署并理解项目结构。压缩包整体约47.38MB,目录组织规范,检索和迁移方便。资源已有92人浏览学习,配有数据库初始化脚本与完整说明文档。对于需要快速搭建推荐系统、撰写毕业设计论文或准备答辩演示的用户而言,这份资料能直接提供可运行的项目原型和文档支撑,也保留定制扩展空间。
1. 为什么这个题目比“图书管理系统”更适合当Java毕业设计
先给结论:同样是Java毕业设计,“个性化影片推荐系统”这个题,难度比“XX管理系统”高一个档次,但天花板也更高。管理系统的核心是CRUD,把增删改查写好就能过;推荐系统的核心是推荐结果怎么算出来的,需要靠SSM框架搭起的Java后端承载一个能算、能解释的协同过滤算法。这个区别直接决定了论文有没有东西可写、答辩时是讲业务还是讲算法。传统做法是直接从网上找一份“附源代码”的项目,导入IDE、改数据库连接、跑起来截图,最后把代码粘进论文——这条路能走通,但容易翻车在我后面要讲的数据与参数上。真正适合这个题的,是三类人:Java基础还行、想靠算法亮点冲个高分的;对推荐系统好奇、想弄明白协同过滤凭什么“猜你喜欢”的;以及已经写好管理系统、想在选题上补一块差异化的人。如果只是想水一个毕设,这个题反而会比管理系统更累,这是先说在前面的大实话。
2. SSM框架搭后台、协同过滤做推算:这套选型为什么合理
2.1 SSM三层在影片推荐项目里的实际分工
一个常见误区是觉得推荐系统必须用Python写,Java后端只能做网页。其实SSM框架完全能撑起这个题的推荐计算,因为本科毕业设计的数据量和计算量都可控。Spring管理service组件的生命周期,SpringMVC处理浏览器发来的页面请求,MyBatis把影片、评分、用户数据从MySQL取出来。推荐算法本身不需要分布式,也不需要GPU,它发生在service层的Java方法里。
我会把一次推荐请求拆成七个步骤,写论文画时序图时也照这个来:
- 前端页面请求 /recommend/list,携带当前登录用户id。
- SpringMVC的Controller接收参数,封装成userId传给service。
- Service调用RatingMapper.selectAll(),把全量评分记录一次性查出。
- Service在内存中构建“用户-电影”评分矩阵,也就是Map<Long, Map<Long, Double>>。
- 计算当前用户与其他每个用户的相似度,取TopK邻居。
- 邻居看过而当前用户没看过的电影,做加权评分预测,按预测分排序取TopN。
- 返回List ,页面渲染成电影卡片列表。
扎实一点说,这就是SSM框架的标准分层:Controller不写业务、Mapper不写逻辑、推荐算法收敛在Service层。这一步想清楚,论文里的架构图和源代码的包结构就能一一对应上,答辩被问到“框架怎么用的”时,回答起来会很顺。
2.2 推荐算法不选深度学习,选协同过滤的三个理由
选深度学习当毕设方向看起来很高级,但周期撑不住。训练模型需要标注数据、需要调参、如果跑GPU还要折腾环境,任何一步卡住两周,进度就悬了。协同过滤没有训练过程,它的核心是一个数学公式:把用户对电影的评分看成向量,向量夹角越小,说明两个人口味越像;找到最像的K个邻居,用他们对某部电影的评分预测当前用户会不会喜欢。
第二个理由是数据量。本科毕设没有真实平台的海量埋点,只有自己灌的几百条到几千条评分数据。深度学习在这个数据规模下基本学不出有效特征,协同过滤反而是小数据集上最稳的算法,几百条评分就能看出明显的推荐差异。第三个理由是论文好写:相似度公式、邻居选择、评分预测、推荐列表,每一节都有确定内容可以写,不会被追问到没法答。
我见过不少同学硬上神经网络,最后交出来的系统演示效果不如简单协同过滤,论文里还写不清损失函数为什么这样设。对毕设来说,能把一个经典算法讲透、跑通、说出边界,比堆一个说不清的黑盒模型更实在。
2.3 UserCF还是ItemCF:影片场景下的取舍
基于用户(UserCF)算人跟人的相似度,基于物品(ItemCF)算电影跟电影的相似度。影片推荐我默认选UserCF,原因有三:影片兴趣是长尾分布,热门电影大家都看过,但小众电影才是区分口味的关键,基于用户算相似度更贴近“找同好”的直觉;UserCF的推荐结果可以解释成“和你口味相似的人也喜欢这部电影”,这句解释能直接写进论文的需求分析;ItemCF在电商场景更适合,因为用户量远大于商品量,但在电影评分数据稀疏时,物品相似度矩阵几乎全是弱关联,演示效果不好。
所谓“用户-电影评分矩阵”,通俗理解就是一张Excel表:行是用户,列是电影,单元格是评分。大多数人没看过某一列,所以矩阵是稀疏的。推荐系统的任务就是在空白格子里填“如果让你打分,你大概会给几分”。这句话建议写进论文绪论,几秒钟就能让非技术背景的答辩老师听懂这个题目在做什么。
UserCF也不是没有缺点:每次都要在内存里全量计算用户之间的相似度,用户量大了性能会明显下降。但这个缺点恰恰是答辩的加分点,后面第六章我会专门讲怎么回答“用户量上去了怎么办”。
3. 源代码落地:先验收,再建表,最后把推荐代码跑起来
3.1 拿到附源代码的包,先验收这三样东西
网上流传的“附源代码”项目质量参差不齐,我的血泪经验是:不管哪里下载的,第一步不是点运行,而是先验收。要是缺了关键文件,导入IDE之后报错一堆,查了半天才发现是工程本身不完整,那才是最亏的。
| 验收项 | 看什么 | 缺了会发生什么 |
|---|---|---|
| pom.xml | 依赖是否完整,有没有spring-webmvc、mybatis、mysql-connector-java | 无法编译,缺包报错 |
| sql脚本 | 有没有建库建表语句,有没有初始评分数据 | 能启动,但推荐列表全为空 |
| resources配置 | jdbc.properties、spring-mvc.xml、mybatis-config.xml路径是否与代码一致 | 启动直接报错或Mapper扫描不到 |
配置里最需要动手改的就是数据库连接。SSM项目一般把连接信息放在src/main/resources/jdbc.properties:
# src/main/resources/jdbc.properties jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/movie_recommend?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false jdbc.username=root jdbc.password=你的数据库密码这里的参数别乱删。characterEncoding=utf8解决中文电影名乱码;serverTimezone=Asia/Shanghai解决MySQL 8连接时报时区错误;useSSL=false避免本地连接时弹出SSL警告。如果pom里的驱动是MySQL 8,驱动类必须写com.mysql.cj.jdbc.Driver,老写法com.mysql.jdbc.Driver在MySQL 8下会直接启动失败。这三个参数是这类项目最常改、也最常改错的地方。
3.2 数据库设计:四张表把用户、影片、评分、偏好落盘
影片推荐系统的表结构比普通管理系统多一层设计思考。常见做法是四张表:用户表、影片表、评分表、类型表。评分表是核心,它把“谁给什么电影打了多少分”这个关系单独存下来,而不是把评分字段塞进用户表或影片表,因为用户和电影是多对多关系。
-- 1. 用户表:prefer_tags 存注册时勾选的兴趣标签,按逗号分隔 CREATE TABLE tb_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, prefer_tags VARCHAR(200) COMMENT '兴趣标签,如 喜剧,科幻' ); -- 2. 影片表:genre 存电影类型,release_year 用于后续扩展年份过滤 CREATE TABLE tb_movie ( id BIGINT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(200) NOT NULL, genre VARCHAR(100) NOT NULL, release_year INT, cover_url VARCHAR(500), avg_rating DOUBLE DEFAULT 0 COMMENT '用于冷启动阶段的热门榜' ); -- 3. 评分表:唯一约束防止同一用户对同一电影重复评分 CREATE TABLE tb_rating ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL, movie_id BIGINT NOT NULL, score DOUBLE NOT NULL COMMENT '评分范围 1-5', create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_user_movie (user_id, movie_id) ); -- 4. 类型表:用于注册页多选兴趣标签 CREATE TABLE tb_genre ( id BIGINT PRIMARY KEY AUTO_INCREMENT, genre_name VARCHAR(50) NOT NULL UNIQUE );表结构设计是答辩必问内容,这四张表的关系要在论文里画清楚:用户表和评分表是一对多,影片表和评分表是一对多,用户和影片通过评分表建立多对多联系。兴趣标签用逗号分隔存到tb_user的一列里,简单直接,对毕设来说不需要拆第三张关联表。
初始数据直接影响演示效果。我一般至少灌20个用户、50部影片、200条评分。评分数据要按“口味分组”造:一部分用户偏爱喜剧,一部分偏爱科幻,这样协同过滤才能算出明显的相似用户。如果所有评分都是随机生成的,推荐结果和热门榜没区别,演示时没有说服力。
3.3 UserCF核心代码:相似度计算与TopN推荐
这是整个项目最关键的Java代码,放在service层,对应前面时序图的第5到第7步。代码逻辑要能跑、能讲、能拆给答辩老师看。
// service/RecommendService.java — 基于用户协同过滤的核心方法 public List<MovieVO> recommendForUser(Long userId, int topN, int k) { // 1. 一次查出全部评分记录,避免在循环里反复查库 List<Rating> ratings = ratingMapper.selectAll(); // 2. 构建 用户 -> (电影 -> 评分) 的二级Map Map<Long, Map<Long, Double>> userRatings = new HashMap<>(); for (Rating r : ratings) { userRatings .computeIfAbsent(r.getUserId(), u -> new HashMap<>()) .put(r.getMovieId(), r.getScore().doubleValue()); } // 3. 当前用户没有任何评分时,直接返回热门影片兜底 Map<Long, Double> currentUserRatings = userRatings.get(userId); if (currentUserRatings == null || currentUserRatings.isEmpty()) { return hotMovieMapper.selectTopN(topN); } // 4. 计算当前用户与其他每个用户的余弦相似度 Map<Long, Double> simMap = new HashMap<>(); for (Map.Entry<Long, Map<Long, Double>> entry : userRatings.entrySet()) { Long otherUserId = entry.getKey(); if (otherUserId.equals(userId)) { continue; } Map<Long, Double> otherRatings = entry.getValue(); double dot = 0.0, normA = 0.0, normB = 0.0; for (Map.Entry<Long, Double> e : currentUserRatings.entrySet()) { Double otherScore = otherRatings.get(e.getKey()); if (otherScore == null) { continue; // 只看两个用户共同评过分的电影 } dot += e.getValue() * otherScore; normA += e.getValue() * e.getValue(); normB += otherScore * otherScore; } double similarity = dot / (Math.sqrt(normA) * Math.sqrt(normB) + 1e-9); simMap.put(otherUserId, similarity); } // 5. 取相似度最高的K个邻居 List<Map.Entry<Long, Double>> sorted = new ArrayList<>(simMap.entrySet()); sorted.sort((a, b) -> Double.compare(b.getValue(), a.getValue())); List<Map.Entry<Long, Double>> neighbors = sorted.subList(0, Math.min(k, sorted.size())); // 6. 邻居看过而当前用户没看过的电影,用相似度加权预测评分 Map<Long, Double> scoreMap = new HashMap<>(); Map<Long, Double> weightSumMap = new HashMap<>(); for (Map.Entry<Long, Double> neighbor : neighbors) { Map<Long, Double> neighborRatings = userRatings.get(neighbor.getKey()); for (Map.Entry<Long, Double> e : neighborRatings.entrySet()) { if (currentUserRatings.containsKey(e.getKey())) { continue; // 当前用户已经看过,不需要推荐 } double weight = neighbor.getValue(); scoreMap.merge(e.getKey(), weight * e.getValue(), Double::sum); weightSumMap.merge(e.getKey(), weight, Double::sum); } } // 7. 预测分 = 加权分 / 权重和,按预测分降序取topN List<MovieVO> result = new ArrayList<>(); for (Map.Entry<Long, Double> entry : scoreMap.entrySet()) { Long movieId = entry.getKey(); double predictedScore = entry.getValue() / weightSumMap.get(movieId); result.add(movieMapper.selectByIdWithScore(movieId, predictedScore)); } result.sort((a, b) -> Double.compare(b.getPredictedScore(), a.getPredictedScore())); return result.subList(0, Math.min(topN, result.size())); }这段代码要和论文里的公式一一对应。第4步算余弦相似度,对应公式里的向量点积除以模长乘积;第5步选K个邻居;第6步用相似度作为权重、对未看过的电影评分做加权求和;最终预测分除以权重和,得到1到5分区间内的预测值。答辩时能指着代码讲出每一步在算什么,比背公式有用得多。
两个参数要关注:topN是最终推荐条数,一般设10到12;k是邻居数量,默认10,第四章我会详细讲怎么调。另外分母加1e-9是为了防止两个用户完全没有共同评分时除零得到NaN,这个细节在答辩现场提一句,老师会认为你踩过坑、考虑过边界。
4. 推荐参数与演进:这四个动作把“能跑”变成“论证充分”
4.1 K值怎么调:邻居数决定“准”还是“稳”
K值在很多人眼里是玄学,但它其实可以直接通过演示效果来判断。K太小时,推荐只看身边一小撮相似用户,口味尖锐但容易偏;K太大时,大众口味把个性化稀释掉,推荐列表会越来越像热门榜。按经验,K取5到20之间:
| K值 | 推荐效果倾向 | 什么时候用 |
|---|---|---|
| 5 | 口味尖锐,但可能只推同一类型 | 评分数据充足,相似度区分度高 |
| 10-15 | 默认,均衡 | 大多数毕设场景 |
| 20以上 | 偏向热门影片 | 评分数据稀疏,需要兜底 |
我的习惯是默认K=10,论文测试章节里做一组K=5、K=10、K=20的对比,截图展示推荐列表的变化。这一组对比就是“参数实验”,比单纯写“本系统采用K=10”有说服力得多。演示时也可以现场改K值,刷新页面看推荐结果差异,这是答辩现场最能调动老师注意力的动作。
4.2 余弦相似度还是皮尔逊:稀疏评分下的选择
余弦相似度的缺点在于它不看评分习惯。一个用户习惯打3到5分,另一个用户习惯打1到3分,两个人对同一批电影的评价方向一致,但余弦会把这种系统性偏差算成“口味不合”。皮尔逊相关系数本质上是对评分做了中心化再算余弦,先减去各自的平均分,再比较波动趋势。
既然皮尔逊理论上更合理,为什么我还要用余弦?因为评分矩阵太稀疏。皮尔逊的分母是“各评分与均值的偏差平方和”,当两个用户共同评过的电影只有一两部时,分母会非常小,算出来的相关系数被异常放大,一个共同评分就能把相似度顶到接近1,参考价值很低。落地建议是:如果评分数据充足,可以换成皮尔逊;如果初始数据只有几百条,用余弦加1e-9防除零更稳。或者做一层保护:只有共同评过3部以上电影的用户才参与相似度计算,这个阈值写在论文里也是加分项。
4.3 Z-score评分归一化:一枚手滑1分毁掉整张矩阵
用户打分习惯差异是协同过滤的隐形杀手。有人看到烂片也打3分,有人看到神作只打2分。如果不做归一化,评分向量里包含的不仅是口味,还混入了“这个人手松还是手紧”的信息。更极端的情况是,一个用户误操作给某部电影打了1分,这个离群值会显著改变他和所有人的相似度。
解决办法是在构建评分矩阵时做Z-score标准化,让每个用户的评分都变成相对自己均值的偏差:
// 构造评分矩阵时的预处理:把每个用户的打分习惯拉平 private Map<Long, Double> zscore(Map<Long, Double> rawRatings) { double mean = rawRatings.values().stream() .mapToDouble(Double::doubleValue).average().orElse(3.0); double variance = rawRatings.values().stream() .mapToDouble(v -> (v - mean) * (v - mean)).average().orElse(0.0); double std = Math.sqrt(variance); if (std < 1e-6) { return rawRatings; // 该用户评分全部相同,不标准化,避免除零 } Map<Long, Double> normalized = new HashMap<>(); rawRatings.forEach((movieId, score) -> normalized.put(movieId, (score - mean) / std)); return normalized; }注意这里有个隐藏坑:Z-score之后评分会变成负数,预测分也可能不在1到5范围内。对排序推荐来说这不影响结果,但如果要把预测分展示到页面,需要再映射回2到5区间,否则页面会出现负分,看起来像bug。另外,如果一个用户所有评分完全相同,标准差为0,必须先判断再返回原值,不然整个相似度计算都会出NaN。
4.4 冷启动三件套:热门兜底、标签初始化、随机探索
新用户没有任何评分时,协同过滤算不出相似用户。常见的兜底方案是直接返回热门影片榜,这个在前面代码里已经写过。但只有热门榜太单调,我会在用户注册时让他勾选兴趣标签,把标签转成初始伪评分:
// 用户注册时勾选了 ["喜剧","科幻"],把命中类型的电影初始化为4.0分 public Map<Long, Double> initProfileByTags(List<String> userTags) { Map<Long, Double> init = new HashMap<>(); for (Movie movie : movieMapper.selectAll()) { double score = 2.5; // 默认中等偏好 for (String tag : userTags) { if (movie.getGenre().contains(tag)) { score = 4.0; // 命中兴趣标签的类型给高偏好 } } init.put(movie.getId(), score); } return init; }这里的参数设置有一条经验:初始偏好分不要给5.0满分,否则冷启动阶段的推荐列表清一色全是同一个类型,观众会觉得系统没有“个性”。给4.0可以保证该类型的影片排在前面,同时又不至于完全扼杀其它类型的曝光机会。更完整的冷启动还可以在首次推荐列表里随机掺入一两部冷门片,制造探索机会,这段逻辑可以在论文测试章里作为“冷启动策略”单独节写。
5. 避坑与排查:这个项目最容易翻车的五个细节
5.1 推荐结果全部为空,页面白屏
现象:登录后点推荐页,页面能打开但一个电影卡片都渲染不出来。
原因:最常见的是movieMapper.selectByIdWithScore按movieId查影片时返回了null,而scoreMap里存在已经下架或漏插的影片ID。另一种可能是造数据时tb_movie主键不连续,前端遍历时遇到null对象直接跳过。
解决:排查时先看service返回的result大小。如果result为空,打印scoreMap的key集合,和tb_movie表的id集合做差集,找出缺失的影片ID。一道简单的SQL就能确认:SELECT id FROM tb_rating WHERE movie_id NOT IN (SELECT id FROM tb_movie);。这段SQL可以直接写进论文的数据校验小节。
5.2 新注册用户登录后推荐页依然空白
现象:注册完账号、勾选了兴趣标签,进入推荐页还是一张白板。
原因:冷启动兜底逻辑只判断了currentUserRatings == null,但新用户即使有初始伪评分,也可能因为某条评分没写入数据库而拿到一个空Map。空Map不是null,isEmpty()判断被跳过,代码继续走相似度计算,结果自然为空。
解决:判空条件必须同时覆盖null和isEmpty,写成if (currentUserRatings == null || currentUserRatings.isEmpty())。养成习惯:所有从数据库查出来的集合,两个判空都要写,这是Java空指针防护的基础意识,面试八股里也常考。
5.3 相似度全是NaN,推荐列表排序失效
现象:日志里打印相似度,发现很多NaN,推荐结果顺序完全不对。
原因:两个用户没有共同评过的电影时,点积为0、模长为0,分母除零。虽然推荐时加了1e-9,但如果评分数据本身存在null值,Java的double unboxing会在计算处直接抛NullPointerException,或者把null变成NaN参与运算。
解决:两处下手。第一,SQL层过滤,查询评分时加WHERE score IS NOT NULL;第二,相似度计算分母加1e-9。还要注意MyBatis映射时,如果数据库score字段是DOUBLE类型且允许NULL,实体类的score要用Double而不是double,否则结果集里遇到null会映射失败。
5.4 演示现场内存溢出
现象:本地跑得好好的,答辩演示时刷新几次页面,控制台报OutOfMemoryError。
原因:每次推荐请求都执行selectAll(),把整张评分表加载进内存再全量构建矩阵。几百个用户没问题,但如果你为了演示效果灌了上万条评分,又连续刷新页面,老年代内存很快被打满。
解决:在Service层加一个简单的本地缓存,相似度结果半小时内不过期。一个ConcurrentHashMap加时间戳就能解决,代码量不大。答辩时老师问高性能问题,还可以补一句“生产环境会把相似度结果放到Redis或离线预计算”,显示你考虑过扩展性。不要在演示现场解释内存溢出,那会打断节奏。
5.5 MySQL 8连接失败与中文乱码
现象:项目在别人电脑上能跑,到你本地启动时直接报Communications link failure,或者电影标题显示成问号。
原因:本地MySQL版本和代码里依赖的驱动版本不匹配,最常见是MySQL 8却用了老驱动;连接串缺少serverTimezone参数;缺characterEncoding=utf8导致中文乱码。
解决:连接串在第三章已经写全。特别提醒一点:如果本地装的是MySQL 8,pom里mysql-connector-java版本不要低于8.0.x,驱动类名要写com.mysql.cj.jdbc.Driver。这些排查动作按“驱动版本 -> 连接串参数 -> 数据库字符集”的顺序检查,基本五分钟内能定位。
提示:遇到任何诡异问题,第一步永远是看日志里第一条异常,而不是搜报错关键字。这条习惯能省下大量排错时间。
6. 毕业论文与PPT:把源码变成答辩现场的底气
6.1 论文章节与源代码的对照编排
拿到代码后不要直接把源码贴进论文,而是按“论文章 -> 源码位置 -> 答辩一句话”的结构重新组织。论文第5章系统实现,对应的是SSM三层里各层的关键类;论文第6章系统测试,对应的是评分数据量、K值对比、冷启动效果截图。
我的习惯是先用一张表把自己手上的源码整理清楚,再动笔写论文:
| 论文章节 | 对应源码内容 | 答辩时的一句话 |
|---|---|---|
| 绪论与背景 | 选题意义 | 影片信息冗余,需要个性化过滤 |
| 相关技术 | SSM框架与UserCF选型 | 用SSM做分层,UserCF做核心算法 |
| 系统设计 | 数据库表与流程图 | 四张表存数据,算法在service内存计算 |
| 系统实现 | RecommendService | 相似度、邻居、加权预测、TopN |
| 系统测试 | 冷启动兜底与K值对比 | 归一化后推荐质量明显提升 |
这张表本身就是论文目录的雏形。每写一章,先看对应代码在做什么,再写文字描述,保证论文和源码不脱节。
6.2 PPT的结构:每一页对应一个能被追问的源码点
PPT建议做六页,不是六十页:选题背景一页,技术选型一页,系统架构图一页,数据库设计一页,核心推荐代码一页,测试效果一页。每页只讲一个能被追问的点。核心推荐算法那一页,只放相似度公式和推荐流程图,不要堆大段代码。
讲的时候按“问题 -> 方案 -> 实现 -> 效果”四步走。比如讲冷启动:先抛出“新用户没有评分怎么办”,再给出热门兜底加标签初始化的方案,然后切到代码,最后展示新用户注册后立刻能看到个性化推荐的截图。每一步都在引导老师顺着你的思路问,而不是等他随便指个地方追问。
6.3 三个必被追问的问题与回答框架
第一个问题:为什么不用深度学习。回答思路是数据规模不支持、效果在本科数据量下不一定比协同过滤好、可解释性差。重点是不要贬低深度学习,而是说它适合更大的数据场景,这样既谦虚又展示了知识边界。
第二个问题:推荐结果怎么评估。回答用三种指标:准确率、召回率、覆盖率。可以把评分数据按8:2切分,80%用来计算相似度,20%用来验证预测评分是否接近真实评分。覆盖率指推荐列表里不同影片类型的分布,这个指标专门检验是否只推荐热门片。
第三个问题:用户量上来了怎么办。回答方向是相似度结果缓存、离线预计算、引入Spark或Flink做分布式计算。这个扩展性回答是答辩加分项,说明你不是只会跑通一个demo。
我现在的习惯是拿到任何一份SSM推荐系统的源代码,第一步永远先跑一段数据看相似度输出,再去看页面功能——先信数据,再信界面。这是被坑过之后养成的习惯,把这个习惯教给你,希望帮到你。
本文还有配套的精品资源,点击获取