ARTICLE DETAIL

资讯详情

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

Java+SpringBoot+SSM校园服务平台:协同过滤推荐系统设计与实现

Java+SpringBoot+SSM校园服务平台:协同过滤推荐系统设计与实现 这个标题我太熟了基本一眼就能看出来是Java方向的毕设或者课程设计标配选题。先别急着觉得“又是老一套”仔细看看这个项目玩法其实不算简单——表面看是一个校园服务平台但真正核心的亮点在于“基于协同过滤算法”这几个字。也就是说这个平台不只是做一个信息发布展示的静态网站而是要能根据用户行为数据给每个人推荐他可能感兴趣的校园服务二手闲置、失物招领、拼车信息、学习资料、活动通知等等。对于准备做毕设的同学来说这个选题的价值在于技术栈跨度合适、业务场景清晰、算法部分有亮点可讲。对于想动手写推荐系统的Java开发者来说这也是一个把算法落到Web工程里的实战案例。整篇我会按照我自己做这类项目的思路一路拆下来从技术选型到算法原理再到实际调试中的坑全是能直接拿去用的经验。1. 项目拆解校园服务平台到底要做什么1.1 从标题反推出来的核心需求很多人看到“校园服务平台”就直接去画登录注册、增删改查页面了这是典型的没读懂题目。这种平台类项目的核心从来不是“信息能发布出去”而是“用户能在海量信息里快速找到想要的东西”。校园服务平台的业务范围一般绕不开这几块二手交易、失物招领、学习资料共享、互帮互助、校园活动发布。每块都是典型的“低频但刚需”场景比如二手交易学生卖个自行车、教材、台灯东西挂上去最怕的事情就是沉底没人看。失物招领更特殊时效性极强丢手机的人恨不得一小时内就刷到认领信息。这些场景天然需要一个“把合适的信息推到合适人面前”的机制这就是协同过滤算法能发挥价值的地方。所以这个项目拆到最底层答案其实很清晰前半部分是传统Web系统后半部分是个性化推荐引擎。前者保证平台能用后者保证平台好用。毕设答辩时前者是基本盘后者是拉开差距的加分项。1.2 SpringBoot SSM 组合的真实含义技术选型是这种项目第一个需要想明白的地方。标题里写着“JavaSpringBootSSM”很多新手一看就懵SpringBoot和SSM是什么关系能放在一起吗其实完全没问题。SSM是Spring、SpringMVC、MyBatis三个框架的缩写而SpringBoot本身就是Spring生态的快速开发框架所以“SpringBootSSM”落到实际工程里通常就是一套以SpringBoot为核心Web层用SpringMVC、持久层用MyBatis的标准架构。换句话讲SpringBoot把原来传统SSM项目里大量繁琐的XML配置干掉了比如数据源、事务管理器、视图解析器这些以前要写一大堆配置现在用起步依赖加自动装配就搞定了。但这并不代表SpringMVC和MyBatis不存在了Controller还是那一套注解开发方式Mapper接口加XML映射也照旧。所以这个组合的好处是既保留了SSM架构清晰的分层思想又享受了SpringBoot开箱即用的便利。选这个组合还有一层现实的考虑答辩时老师大概率会问你“为什么不用传统SSM而用SpringBoot”。你可以直接说SpringBoot内嵌Tomcat项目打成一个jar包就能跑部署演示非常省事。同时SpringBoot的自动装配机制能减少大量模板代码让我能集中精力去实现推荐算法这个核心模块。这套回答逻辑比单纯说“SpringBoot更流行”要扎实得多。1.3 平台模块划分与用户角色设计做平台类系统之前先把角色和模块梳理清楚后面写代码才能不迷路。校园服务平台最常见的角色有三种普通学生用户、平台管理员如果有社团或者商家入驻再加一个运营角色。用户端的功能围绕“发布、浏览、互动”展开登录注册、个人信息维护、发布服务信息、浏览分类列表、搜索筛选、对服务评分和评论、收藏感兴趣的内容以及最重要的“推荐中心”。管理员端负责平台秩序用户管理、信息审核、分类管理、评论管理、数据统计。这里值得多说一句信息审核这个功能千万别省略这是很多同学容易忽略的刚需。任何UGC平台用户发布的内容都需要审核否则测试的时候灌入垃圾数据推荐算法再准也被污染了。而且“信息发布—审核—发布成功”这个流程在论文里也是需求分析的重要一环不写反而显得业务考虑不完整。推荐中心这块我建议单独做一个模块页面里留一个“猜你喜欢”或者“个性化推荐”的区域。一来是功能上突出亮点二来是答辩演示时可以直接点进去展示推荐效果。整个平台的功能结构用一张表就能看明白模块用户端功能管理端功能用户中心注册、登录、个人信息修改用户管理、状态控制服务大厅分类浏览、搜索筛选、服务详情分类管理、服务信息管理互动中心评分、评论、收藏评论审核、数据维护推荐中心个性化推荐列表推荐算法参数配置内容安全举报反馈信息审核、下架处理2. 协同过滤算法推荐模块的核心原理与选型2.1 协同过滤的核心思想与两种主流思路协同过滤这个名词听起来很高大上其实背后的逻辑特别朴素你不是不知道该选什么吗那就看看和你相似的人都在选什么或者直接看看你平时喜欢的东西里有哪些是类似的照着来就行了。身边同学向你推荐二手自行车、给你安利复习资料本质上就是一次协同过滤。算法体系里把它分成两种路线。一种是基于用户的协同过滤也就是UserCF核心是计算用户之间的相似度找到和你兴趣偏好最接近的那批“邻居用户”再把他们喜欢而你还没接触过的物品推荐给你。另一种是基于物品的协同过滤也就是ItemCF核心是计算物品之间的相似度逻辑是“喜欢A的人通常也喜欢B”然后把你喜欢的物品的相似物品推荐出来。两种算法没有绝对的好坏适用场景很不一样。我做校园服务平台的时候仔细对比过给出了这样的判断逻辑对比维度基于用户协同过滤 UserCF基于物品协同过滤 ItemCF算法核心找相似用户找相似物品典型场景社交、新闻、热点推荐电商、视频、购物推荐优点能挖掘新兴趣社交性强推荐结果稳定易解释缺点用户量增大后计算开销大难以推荐全新类型的物品校园场景适配性人际圈有一定重叠好用二手物品、资料类目清晰也好用校园服务平台有一个别的平台没有的特点就是用户之间有真实的人际关系同一栋宿舍楼、同一个专业、同一个社团需求高度重合。所以很多做这类项目的同学偏爱UserCF它确实更贴合校园社交语义。但从工程实现和内容解释的角度讲ItemCF的相似度矩阵更稳定因为用户的行为数据一变用户相似度矩阵就得全量重算而物品相似度相对变化没那么快。怎么选都行关键是要把理由讲清楚而不是随手拍板。我个人做校内信息类平台更建议用UserCF因为它的推荐结果更容易在答辩时讲出让人信服的故事。2.2 为什么选协同过滤而不是其他推荐方式毕设推荐模块也有别的路子可以做最简单的就是基于内容的推荐给每条校园服务信息打上标签比如“二手”“教材”“东区宿舍”然后根据用户历史上收藏过什么标签的内容来推荐。这种做法的优点是容易实现但缺点也非常明显它永远只会推荐用户已经表现过偏好的同类东西没有惊喜感比如一个学生今天收藏了一个二手显示器明天系统只会给他推二手电脑配件完全发现不了他其实也很需要拼车服务。还有一种是基于规则的推荐比如“浏览过A的人也浏览了B”纯靠统计规则硬算逻辑是很清楚但没有个性化可言给所有人的推荐基本一样。协同过滤比这两类方案强在“向量化”的思维上它把用户对物品的偏好放到一个统一的评分空间里通过数学计算挖掘出人类直觉不容易发现的潜在关联。而且毕设阶段实现协同过滤的工作量适中写一个相似度计算工具类加上推荐生成的Service就能跑起来不会出现“想选高大上的深度学习最后根本搞不定”的尴尬。更重要的是协同过滤是推荐系统里最经典、最基础、面试也最爱问的一类算法。把这个算法吃透了后面出去找工作聊到推荐场景可以直接从协同过滤往深了讲包括冷启动、矩阵稀疏、离线计算这些工业界关心的问题都是加分项。2.3 相似度计算与评分预测的数学细节相似度计算是协同过滤最核心的环节这里面的数学其实只有高中和大学线代的基础。最常用的计算方式是余弦相似度把用户对各个物品的评分看成高维空间里的一个向量两个用户越相似向量之间的夹角就越小余弦值就越接近1。皮尔逊相关系数是在余弦相似度基础上做了一层修正计算时会减去每个用户的评分均值抵消了有的人打分普遍偏高、有的人打分普遍偏低带来的影响。实际项目里如果评分数据是1到5分这种显式评分用皮尔逊系数会更准一些但它需要每个用户至少有一组共同评分过的物品才能算出来毕设数据量少的情况下容易算不出来。所以我通常的做法是以余弦相似度为基础再叠加一个均值修正效果比较稳。核心计算逻辑大致是这样public static double cosineSimilarity(MapInteger, Double userRatingsA, MapInteger, Double userRatingsB) { SetInteger commonItems new HashSet(userRatingsA.keySet()); commonItems.retainAll(userRatingsB.keySet()); if (commonItems.isEmpty()) { return 0.0; } double dotProduct 0.0; double normA 0.0; double normB 0.0; for (Integer itemId : commonItems) { dotProduct userRatingsA.get(itemId) * userRatingsB.get(itemId); } for (double value : userRatingsA.values()) { normA value * value; } for (double value : userRatingsB.values()) { normB value * value; } if (normA 0.0 || normB 0.0) { return 0.0; } return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB)); }评分预测这一步如果是基于用户的协同过滤思路就是找到和目标用户最相似的K个用户用他们对该物品的评分做加权平均权重就是相似度。这样写出来的推荐分反映了“相似用户越像你说话越算数”的思想。这里我再补一个实操细节数据计算时一定要考虑“共同评分物品数”这个因素比如两个用户只共同有一个物品的评分时余弦相似度算出来很可能直接是1.0但这其实是虚假的相似参考价值很低。工程上可以设置一个最低共同评分数的阈值比如至少要有3个共同评分的物品才认为这对用户相似度计算有效。这个细节解释了为什么推荐结果有时会莫名其妙也是最能体现你做过实践的地方。2.4 冷启动问题怎么处理协同过滤算法有一个天生缺陷就是冷启动新用户一条行为数据都没有算法根本无从算起新发布的服务信息没有任何人的评分也永远不会被推荐出去。这个坑如果不在设计阶段考虑好系统上线演示时就会非常尴尬注册一个新账号进去推荐页空白一片。我在项目里的处理方案分几层兜底。第一层是最简单也最有效的新用户没有任何行为数据时直接推荐全站热门服务列表热度可以用浏览量、收藏量、评分人数综合计算。第二层是给新用户一个“兴趣选择”流程注册时让用户勾选几个感兴趣的分类比如“二手交易”“拼车出行”“学习资料”系统根据分类匹配做初始推荐。这样既解决了冷启动又能在一开始就为协同过滤采集到了用户偏好信号。第三层用于解决新物品的冷启动新发布的服务在算法里没有评分数据展示上会自动获得一定的初始曝光权重让系统给它随机推送给一小部分用户等积累了一定行为数据后再进入正常推荐流程。这套组合策略下来冷启动虽然不能说完全消除但至少保证演示时不会出现“推荐模块没东西可推”的窘境。答辩的时候被问到冷启动怎么解决直接把这套分层策略讲出来比背教科书上的定义强得多。3. 核心实现从数据库到推荐接口的完整链路3.1 数据表设计与行为建模推荐算法再高大上数据基础烂了全白搭。我见过太多人算法代码写得挺好结果是基于一张空表算出来的推荐结果当然惨不忍睹。所以先把表设计说清楚。核心表至少要有这些。用户表保存账号和基本信息校园服务信息表保存发布的内容和分类这是业务主表。行为表是整个推荐算法的数据命脉它记录用户对每条服务信息的行为类型浏览、收藏、评论、评分、举报。推荐结果缓存表用来存算法算好的推荐结果避免每次用户访问推荐接口都现场跑一遍全量计算。行为这件事情这里我要展开说一下设计思路。协同过滤的输入是“用户对物品的评分矩阵”但真实平台上用户很少会认真打分绝大多数行为是隐性的看到感兴趣的信息点进去收藏了一下评论了一句。所以行为建模的关键是把隐性行为转化成显式评分。我给自己的系统定的规则是浏览记1分收藏记4分评论记3分主动评分按用户实际给的分数记录。如果一个用户对同一个物品发生多种行为分数取最大值而不是累加这样能防止恶意刷分也符合“对一个物品的偏好应该有上限”的直觉。虽然技术实现上是直接算的评分矩阵但我建议数据库里仍然单独存原始行为记录而不是只存算好的分数。因为算法策略一旦调整比如改成浏览2分、收藏5分如果只有汇总分数就改不动了。保留原始行为可以随时重新生成评分矩阵代价仅仅是多存几条记录。这种设计思路在答辩时老师问“行为数据和评分数据怎么分离”就派上用场了。3.2 算法代码的工程化实现算法落地到工程里不能只在测试类里跑通就算完事得把它变成一个真正的服务组件。我的项目里把推荐模块分成了三层数据加载层、相似度计算层、推荐生成层。数据加载层负责从数据库读取评分矩阵组织成Map结构K是用户IDV是该用户对所有物品的评分集合。相似度计算层处理任意两个用户或物品之间的相似度。推荐生成层根据相似度找邻居、预测评分、过滤掉已交互过的物品最后生成TopN推荐列表。这里要说一下为什么要分层。如果写成一个巨型方法算法逻辑和数据访问代码搅在一起调试的时候简直灾难。分层之后的好处是谁出问题查谁数据库查询慢了就去优化SQL相似度计算慢了就去优化算法边界清晰分工明确。而且答辩时老师问架构设计直接拿这个三层结构讲分层设计的意识就有了。生成推荐列表的伪逻辑大概是这样的public ListInteger getRecommendItems(Integer targetUserId, int topN) { // 1. 获取目标用户的所有评分记录 MapInteger, Double targetUserRatings ratingService.getRatingsByUser(targetUserId); // 2. 遍历所有其他用户计算与目标用户的相似度 MapInteger, Double similarityMap new HashMap(); for (Integer otherUserId : allUserIds) { if (otherUserId.equals(targetUserId)) { continue; } MapInteger, Double otherRatings ratingService.getRatingsByUser(otherUserId); double similarity similarityCalculator.cosineWithShrinkage(targetUserRatings, otherRatings); similarityMap.put(otherUserId, similarity); } // 3. 取相似度最高的K个用户作为邻居 ListMap.EntryInteger, Double neighbors similarityMap.entrySet().stream() .filter(entry - entry.getValue() 0.1) .sorted(Collections.reverseOrder(Map.Entry.comparingByValue())) .limit(K_NEIGHBORS) .collect(Collectors.toList()); // 4. 加权预测评分取TopN推荐 MapInteger, Double predictScores new HashMap(); for (Map.EntryInteger, Double neighbor : neighbors) { MapInteger, Double neighborRatings ratingService.getRatingsByUser(neighbor.getKey()); for (Map.EntryInteger, Double itemRating : neighborRatings.entrySet()) { Integer itemId itemRating.getKey(); if (targetUserRatings.containsKey(itemId)) { continue; // 用户已经交互过不再推荐 } predictScores.merge(itemId, neighbor.getValue() * itemRating.getValue(), Double::sum); } } // 最后按预测分倒序过滤掉已下架信息取TopN return predictScores.entrySet().stream() .sorted(Collections.reverseOrder(Map.Entry.comparingByValue())) .limit(topN) .map(Map.Entry::getKey) .collect(Collectors.toList()); }这里有一个容易被新手忽略的细节因为算出来的用户相似度会有负值或者极小值。负值说明两个用户偏好相反这种邻居的数据宁愿不使用也不要让它拉低预测结果所以做了相似度阈值过滤。另外每次用户访问推荐接口都现场做全量计算对数据库压力太大实际项目里我用了Spring的定时任务每天凌晨算一次全量推荐结果存到缓存表里用户访问推荐接口时直接读缓存结果。如果用户当天的行为发生了很大变化第二天的推荐就会自动更新这个时效性在毕设场景里完全够用。3.3 接口设计与前端联动后端算法做完还得通过接口给前端页面用。这里的经验是接口设计一定要简洁统一不要为了炫技搞出一堆花哨的URL。我当时定义为三个核心接口个性化推荐接口、热门服务接口、服务详情接口。推荐接口接收用户ID和数量参数返回推荐的服务信息列表热门接口不做个性化运算直接返回热度排序的数据用于冷启动兜底。统一返回结构的必要性这里值得专门讲一下。有人图省事直接把List扔给前端结果后面前端想拿状态码判断是否登录过期、是否参数错误根本无从下手。我的做法是定义一个Result对象里面放状态码、提示信息和数据三个字段。所有接口都返回这个结构前端拿到数据先看状态码再取data这套模式在前后端对接时能省掉大量沟通成本。项目标题里提到有app方向开发时要注意前后端接口的通用性。因为App端、小程序端和Web管理端很可能同时调用同一套后端接口接口设计就按照RESTful风格走语义清晰、参数明确前端用什么技术栈都能对接。这块实践到后面会发现一个良好的接口层设计直接决定联调阶段的痛苦程度初期花一点时间设计好后面会特别顺。3.4 部署、调试与答辩演示要点到部署这一步常见的问题是环境不一致导致的启动失败。本地跑得好好的到答辩机器上起不来了这种尴尬我见得太多了。最典型的坑是数据库版本和连接配置问题比如本机MySQL用户名密码是root/123456结果服务器上数据库密码根本没改配置文件也没改连接直接报错。我建议开发时就养成用外置配置的习惯把数据库连接、端口这些参数放到SpringBoot的application.yml里管理不要硬编码在代码中。部署的时候直接打成带内置Tomcat的jar包放到装有对应版本JDK的服务器上java -jar启动就行。这里补充一个很重要的点SpringBoot版本和JDK版本要匹配SpringBoot 2.x对Java 8支持良好SpringBoot 3.x要求Java 17起步很多人报错说class文件版本不对就是因为这个原因。另外部署前在服务器上执行SQL脚本初始化数据库再启动应用能少踩很多坑。答辩演示的时候有几个让整个系统显得专业的小技巧。提前准备好一批模拟数据至少包含十几个用户、几十条服务信息和足够的行为记录。这样打开推荐页面的时候不同账号登录看到的推荐结果是不同的这个对比效果可以直观展示算法的个性化特点。另外提前准备一个小号先在后台模拟收藏几个二手物品再打开推荐页面看是否推荐了同类物品这种现场测试的效果非常加分。4. 开发实录踩过的坑与排查方法4.1 数据与算法层面的坑我先说几个我实际开发中遇到并解决的坑这些都是文档里不会写的。第一个坑是评分矩阵太稀疏导致计算结果异常。我的模拟数据里用户数量不多但每个用户只对三五个物品有行为两两用户之间的共同评分物品数常常为零或者一。零共同评分直接返回相似度0倒是没什么毛病但只有一个共同物品评分时余弦相似度算出来常常是1.0满相似度啊这明显不合理。后来我加了一个最低共同评分数的判断要求至少有三个共同评分的物品才算有效相似度这一步加完之后推荐结果一下子靠谱了很多。这个经验比什么调参技巧都值钱因为它是从数据本质上思考问题。第二个坑是推荐列表里出现了一些不正经的内容。比如已经被管理员下架的物品、状态违规的内容居然还能出现在推荐结果里。原因很简单相似度和预测分是基于行为数据算出来的违规物品曾经有用户浏览过、收藏过算法不知道它已经下架了照样给高权重。解决方案是推荐生成之后、返回前端之前增加一道状态过滤把数据状态不是“正常”的全部过滤掉。这个坑提醒我推荐系统的输入数据必须和业务数据的状态保持同步否则会出现严重的“算法推荐了不该推荐东西”的事故。第三个坑是性能问题。用户数量不多时双重循环遍历所有用户算相似度还能忍当数据量上升到几千用户时每次全量计算开始变得卡顿。我在日志里看到一次推荐请求居然耗时两三秒这用户体验完全不能接受。后面改造为定时离线计算把结果缓存到数据库推荐结果表接口只读缓存性能问题立刻解决。这也是一个很好的工程实践案例即时计算和离线计算的选择其实就是时间和空间的权衡。4.2 工程与调试层面的坑毕设项目常见的工程坑也不少第一个就是依赖冲突。SpringBoot整合MyBatis时需要引入mybatis-spring-boot-starter有人习惯自己手动引入一堆mybatis和spring的零散依赖结果版本对不上直接启动报错。排查这种问题有个笨办法看控制台启动日志的报错信息如果是NoSuchBeanDefinitionException多半是包没扫描到如果是NoClassDefFoundError多半是依赖缺失或者版本冲突。Maven里可以用依赖树命令分析具体冲突。第二个坑是MyBatis的XML文件位置配置不对。很多人把Mapper接口放在src/main/java里XML映射文件放在resources目录下然后忘了配置mapper-locations项目启动时不报错但一调用数据访问层就报Invalid bound statement。排查方法也很固定检查application.yml里的mapper-locations配置是否指向了XML文件实际路径。这个坑之所以常见是因为它不报启动错误、只报运行时错误很容易让人误判为SQL写得有问题。第三个坑在JSON序列化上前端展示服务详情时服务信息对象里关联了发布用户对象发布用户对象里又有服务信息列表结果前后端之间返回JSON时死循环了控制台刷出一堆StackOverflowError。解决办法是在关联属性上加上JsonIgnore注解发布用户对象里不序列化服务列表只保留基本信息。这个坑在第一次做关联查询时几乎必踩提前了解能省很多时间。再补充一个关于SpringBoot jar包的问题。有同学在线上部署了项目之后丢掉了源码想通过反编译jar来改代码这种思路其实非常不推荐。SpringBoot的jar里虽然有.class文件但配置文件和依赖都被重新组织了反编译回来的是残缺的字节码根本不能当作工程源码用。真正要维护项目必须保留原始代码和配置并建立版本管理哪怕自己一个人开发也要用Git管理每一版代码这是最基本的职业习惯。4.3 调试文档与LW写作的整理思路毕设交付物不只是代码还有文档和调试说明。很多人答辩翻车不是因为系统没做出来而是文档没有条理、答辩讲不清楚。调试文档首先要把环境写明白JDK版本、Maven版本、数据库版本、Node版本一个都不能漏越详细越好。然后是启动步骤从导入项目到执行SQL脚本再到最终访问地址每一步都写清楚。最后是常见问题列表比如端口被占用怎么办、数据库连接失败怎么办照着这个列表排查问题效率会高很多。论文写作也有一个万能骨架可以用。首先是研究背景和意义讲清楚为什么要做校园服务平台、为什么要做个性化推荐。然后是技术栈介绍SpringBoot、SSM、协同过滤算法依次介绍。再往下是需求分析把功能模块和非功能需求讲清楚。紧接着是系统设计包括架构设计、数据库设计、算法设计。然后是系统实现把各模块的核心代码和实现思路写出来。最后是系统测试包含功能测试和性能测试。这个结构基本可以让论文不会偏题。论文里算法部分一定要注意不能只贴代码必须配合公式和文字说明。相似度公式、预测评分公式、推荐算法流程这些东西手写清楚了论文的学术性就有了。我建议把公式放到算法设计这章代码放到系统实现这章这样逻辑是递进的从设计到落地层次分明。调试文档还有一个容易被忽视的点要写清楚“怎么验证算法效果”。比如准备了哪些测试数据、推荐结果是否符合预期、如何对比推荐结果和用户实际行为。不要笼统地写“推荐准确率高”而是用具体的数据和展示来说明比如“用户A收藏过三本Java教材系统为用户A推荐了另外两本Java教材符合预期”。这种具体的测试实例比一百句空话让答辩老师信服。4.4 后续可做的扩展方向这个项目做完之后想让它继续升值的话还有好几个方向可以走。最直接的是引入Redis做缓存把热门推荐列表和个性化推荐结果放进Redis设置过期时间能进一步减轻数据库压力。然后是给算法加评估指标把数据集分成训练集和测试集用准确率和召回率评估推荐质量这一块加上去整个项目的完整度会提升一个档次。还可以做用户画像把用户对不同分类的偏好权重算出来在用户主页里显示这个用户可能感兴趣的标签。这个功能视觉上非常直观展示出一排标签云而且代码实现上只是把评分矩阵按分类汇总不算复杂。加上之后平台的个性化味道更浓答辩演示也更丰富。如果技术底子不错还可以把算法从协同过滤往前推一步加入基于物品特征的混合推荐或者做简单的矩阵分解。不过这些属于锦上添花核心思路还是先把协同过滤做扎实再考虑玩法升级。毕竟毕设项目讲求的是完整落地而不是算法炫技。最后再分享一点我个人的体会。这类推荐系统项目最容易出现在算法代码里自嗨、忽略数据质量的问题。我一开始也走了弯路自己造了一堆完美评分数据跑出来的推荐结果当然完美但一到真实测试就被打回原形。后来我把测试数据换成更接近真实的稀疏行为数据又补上了状态过滤和兜底逻辑系统才算真正能扛住演示场景。做这个项目最大的收获不是学会了SpringBoot而是理解了算法在工程里怎么样才能落地怎么样处理脏数据、冷启动、性能问题这些经验在以后做任何数据相关的系统都用得上。
返回列表