大家好,这里是小罗毕设工作室。今天给大家带来了一套完整的保举体系: “基于Jaccard算法的用户欣赏历史保举商品体系”。 体系源码后端实现是springboot,前端是vue3。
视频演示
基于Jaccard算法的用户欣赏历史保举商品体系实战
图片截图
算法原理先容
假设我们有两个商品:商品A和商品B。商品A被用户{小明,小红,小张}欣赏过,商品B被用户{小红,小张,小李}欣赏过。
得到两个用户群体集合 : {小明,小红,小张} {小红,小张,小李} 。
我们通过Jaccard算法,将两个集合先取交集得到 {小红,小张} ,大小是2 , 在取并集得到 {小明,小红,小张,小李},大小是4。
因此Jaccard相似度=2/4=0.5,这个值表现两个商品的相似程度,值越靠近1表现两个商品越相似(被雷同用户群体欣赏),体系会优先保举相似度高的商品。
实现流程
根本数据预备
起首我们需要预备用户的行为数据表:
- CREATE TABLE `user_behaviors` (
- `id` bigint NOT NULL AUTO_INCREMENT COMMENT '行为ID,自增主键',
- `user_id` bigint NOT NULL COMMENT '用户ID',
- `item_id` bigint NOT NULL COMMENT '物品ID',
- `rating` decimal(3,1) DEFAULT NULL COMMENT '评分(1-5分)',
- `behavior_type` varchar(20) NOT NULL COMMENT '行为类型(VIEW-浏览,LIKE-喜欢,PURCHASE-购买)',
- `timestamp` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '行为发生时间',
- PRIMARY KEY (`id`)
- ) ENGINE=InnoDB COMMENT='用户行为记录表';
复制代码
之以是这里有评分字段,是因为后续要扩展 基于协同过滤算法的用户评分保举体系。
固然这里还要有用户表和商品表:
- CREATE TABLE `items` (
- `id` bigint NOT NULL AUTO_INCREMENT COMMENT '物品ID,自增主键',
- `name` varchar(200) NOT NULL COMMENT '物品名称',
- `description` text COMMENT '物品描述',
- `category` varchar(50) DEFAULT NULL COMMENT '物品类别',
- `price` decimal(10,2) DEFAULT NULL COMMENT '物品价格',
- PRIMARY KEY (`id`)
- ) ENGINE=InnoDB COMMENT='商品表';
- CREATE TABLE `users` (
- `id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID,自增主键',
- `username` varchar(50) NOT NULL COMMENT '用户名',
- `email` varchar(100) NOT NULL COMMENT '用户邮箱',
- `password` varchar(100) NOT NULL COMMENT '用户密码',
- PRIMARY KEY (`id`)
- ) ENGINE=InnoDB COMMENT='用户表';
复制代码
表创建好之后,我们需要写好基本的用户登录,商品列表,商品详情等根本接口,然后保证有丰富的行为数据落库,这样保举体系才能发挥作用。
这些基本的接口我就不讲了,主要讲解一下用户保举接口的实现。
完整体系源码我已经整理清除:
- gitcode巅抗目/hadluo2/springboot_vue.git
复制代码 用户保举接口的实现
我将代码的流程整理成了一个流程图,起首我们需要构造一个Map结构数据:

这些数据就是查询用户行为表,将全部的行为数据,按照商品分类,每一个商品都对应了一个用户群体集合。
然后还是查询用户行为表 , 将当前用户的行为数据查询出来,进行下面流程:

关键的核心点就是:将当前保举用户买过的商品id对应其他也买过的这个商品的用户作为一个用户集合,然后取全部商品对应的用户群体集合(许多个),然后一个一个经过Jaccard算法盘算相似度,把这个相似度最为评分,最后取出评分高的商品最为保举商品。
讲到这里,你应该能懂了,下面看下关键代码实现:
- /**
- 完整代码实现:gitcode巅抗目/hadluo2/springboot_vue.git
- * 基于用户浏览历史推荐物品
- * 主要步骤如下:
- * 1. 获取用户最近的浏览记录,按时间倒序排列
- * 2. 获取所有用户的浏览记录,构建物品-用户倒排索引
- * 3. 计算用户最近浏览物品与其他物品的相似度
- * 4. 考虑时间衰减因素,为每个候选物品计算最终得分
- * 5. 返回得分最高的N个物品作为推荐结果
- * @param userId 用户ID
- * @param numRecommendations 推荐数量
- * @return 推荐的物品列表
- */
- public List<Item> recommendBasedOnBrowsingHistory(Long userId, int numRecommendations) {
- // 1. 获取用户最近的浏览记录
- // 创建查询条件:匹配用户ID和浏览行为,按时间戳降序排序
- LambdaQueryWrapper<UserBehavior> wrapper = new LambdaQueryWrapper<>();
- wrapper.eq(UserBehavior::getUserId, userId)
- .eq(UserBehavior::getBehaviorType, "VIEW")
- .orderByDesc(UserBehavior::getTimestamp);
- List<UserBehavior> userViews = userBehaviorRepository.selectList(wrapper);
-
- // 如果用户没有浏览记录,返回空列表
- if (CollectionUtils.isEmpty(userViews)) {
- return Collections.emptyList();
- }
- // 2. 获取所有用户的浏览记录
- // 查询所有用户的浏览行为,用于后续计算物品相似度
- List<UserBehavior> allViews = userBehaviorRepository.selectList(
- new LambdaQueryWrapper<UserBehavior>()
- .eq(UserBehavior::getBehaviorType, "VIEW")
- );
- // 3. 构建物品-用户的倒排索引
- // key: 物品ID, value: 浏览过该物品的用户ID集合
- Map<Long, Set<Long>> itemUserMap = new HashMap<>();
- for (UserBehavior view : allViews) {
- // computeIfAbsent: 如果key不存在,则创建一个新的HashSet
- itemUserMap.computeIfAbsent(view.getItemId(), k -> new HashSet<>())
- .add(view.getUserId());
- }
- // 4. 计算物品相似度和推荐得分
- // 存储每个候选物品的最终得分
- Map<Long, Double> itemScores = new HashMap<>();
- // 获取用户已浏览过的物品ID集合,用于排除推荐
- Set<Long> userViewedItemIds = userViews.stream()
- .map(UserBehavior::getItemId)
- .collect(Collectors.toSet());
- // 遍历用户的每个浏览记录
- for (UserBehavior userView : userViews) {
- // 获取 浏览该物品的 所有用户
- Set<Long> itemUsers = itemUserMap.get(userView.getItemId());
- if (itemUsers == null) continue;
- // 遍历所有物品,计算与当前浏览物品的相似度
- for (Map.Entry<Long, Set<Long>> entry : itemUserMap.entrySet()) {
- Long otherItemId = entry.getKey();
- // 该用户浏览过了这个商品id, 直接排除
- if (userViewedItemIds.contains(otherItemId)) continue;
- // 浏览过这个商品的 其他用户
- Set<Long> otherItemUsers = entry.getValue();
- // 使用Jaccard相似度计算物品相似度
- // Jaccard相似度 = 两个集合的交集大小 / 并集大小
- //完整代码请见:gitcode巅抗目/hadluo2/springboot_vue.git
- double similarity = calculateJaccardSimilarity(itemUsers, otherItemUsers);
-
- // 考虑时间衰减因素 相当于一个优化因子
- // 浏览时间越近,权重越大(1/1, 1/2, 1/3, ...)
- //完整代码请见:gitcode巅抗目/hadluo2/springboot_vue.git
- // 累加相似度得分(考虑时间权重)
- itemScores.merge(otherItemId, similarity * timeWeight, Double::sum);
- }
- }
- // 5. 生成推荐结果
- // 对候选物品按得分降序排序,选择前N个
- return itemScores.entrySet().stream()
- .sorted(Map.Entry.<Long, Double>comparingByValue().reversed())
- .limit(numRecommendations)
- .map(e -> itemRepository.selectById(e.getKey()))
- .filter(Objects::nonNull)
- .collect(Collectors.toList());
- }
复制代码
代码只贴了部分,原理已经教给大家。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |