基于用户的协同过滤算法个性化推荐系统的设计与实现+springboot+vue源码
协同过滤(Collaborative Filtering, CF)是推荐系统中最经典的算法之一,其核心思想是通过用户的历史行为数据(如评分、点击、购买等)发现用户或物品的相似性,并基于这种相似性进行推荐。协同过滤分为两大类:基于用户的协同过滤和基于物品的协同过滤。
本文将介绍 基于用户的协同过滤 原理, 并设计一个简单的商品推荐系统。

算法的步骤
1. 获取所有用户行为数据,构建用户-物品评分矩阵。
2. 目标用户与其它用户的相似度计算: 将用户对商品的评分视为向量,计算余弦相似度。
3. 选取与目标用户相似度最高的 k 个用户作为邻居 。
4. 通过邻居用户的评分进行加权平均预测(权重为用户相似度)。
5. 将预测评分按降序排序,选择评分最高的N个物品作为推荐结果。
如果你还是不懂,我这里举一个通俗易懂的实例:
假设我们有3个用户(小明、小红、小张)和4个商品(手机、电脑、平板、耳机)。
小明给手机和电脑评分分别是4分和5分,小红给手机和平板评分是5分和3分,小张给电脑和耳机评分是4分和2分。
如下图描述:

现在要为小明推荐商品,系统执行的步骤如下:
1. 构建用户-物品评分矩阵,每行代表一个用户,每列代表一个商品;
2. 计算小明与其他用户的余弦相似度,比如与小红的相似度是0.8(因为都给手机较高分),与小张的相似度是0.6;
3. 对于小明未评分的商品(平板和耳机),通过相似用户的评分进行加权预测,如平板的预测分数=(0.8×3)/(0.8)=3分,耳机的预测分数=(0.6×2)/(0.6)=2分;
4. 最后按预测分数降序排序,推荐平板给小明。
代码实现
数据库表准备
首先我们需要准备用户的行为数据表:
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='用户行为记录表';
之所以这里有评分字段,是因为后续要扩展 基于协同过滤算法的用户评分推荐系统。
当然这里还要有用户表和商品表:
表建立好之后,我们需要写好基本的用户登录,商品列表,商品详情等基础接口,然后保证有丰富的行为数据落库,这样推荐系统才能发挥作用。
这些基本的接口我就不讲了,主要讲解一下关键的算法代码。
完整系统源码我已经整理清楚:
gitcode巅抗目/hadluo2/springboot_vue.git
用户评分矩阵的构建
需要借助 是 Apache Commons Math 库中的一个类,用于表示二维实数矩阵,并提供矩阵运算功能。Array2DRowRealMatrix算法工具, Array2DRowRealMatrix
maven依赖如下:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-math3</artifactId>
<version>3.6.1</version>
</dependency>
构建评分矩阵的代码:
// 获取所有用户的行为数据,用于构建用户-物品评分矩阵 List<UserBehavior> allBehaviors = userBehaviorRepository.selectList(null); if(CollectionUtils.isEmpty(allBehaviors)) { return Collections.emptyList(); } // 构建用户和物品的索引映射,方便后续构建评分矩阵 Map<Long, Integer> userIndex = new HashMap<>(); Map<Long, Integer> itemIndex = new HashMap<>(); // 提取用户id List<Long> users = allBehaviors.stream().map(UserBehavior::getUserId).distinct().collect(Collectors.toList()); // 提取物品id List<Long> items = allBehaviors.stream().map(UserBehavior::getItemId).distinct().collect(Collectors.toList()); for (int i = 0; i < users.size(); i++) { userIndex.put(users.get(i), i); } for (int i = 0; i < items.size(); i++) { itemIndex.put(items.get(i), i); } // 初始化评分矩阵,行表示用户,列表示物品 一个 users.size() x items.size() 大小的矩阵 RealMatrix ratingMatrix = new Array2DRowRealMatrix(users.size(), items.size()); // 根据用户行为数据填充评分矩阵 for (UserBehavior behavior : allBehaviors) { if (behavior.getRating() != null) { int uIndex = userIndex.get(behavior.getUserId()); int iIndex = itemIndex.get(behavior.getItemId()); // 设置 矩阵的 行,列 值 为 评分 ratingMatrix.setEntry(uIndex, iIndex, behavior.getRating()); } }
余弦相似度计算
/** * 计算两个向量的余弦相似度 * 余弦相似度用于衡量两个用户的评分模式的相似程度 * @param vector1 第一个用户的评分向量 * @param vector2 第二个用户的评分向量 * @return 相似度值,范围[-1,1],值越大表示越相似 */ private double calculateCosineSimilarity(double[] vector1, double[] vector2) { double dotProduct = 0.0; double norm1 = 0.0; double norm2 = 0.0; for (int i = 0; i < vector1.length; i++) { dotProduct += vector1[i] * vector2[i]; norm1 += vector1[i] * vector1[i]; norm2 += vector2[i] * vector2[i]; } if (norm1 == 0 || norm2 == 0) return 0; return dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2)); }
根据余弦相似度计算取5个相似的用户作为邻居
用户uhgnoy// 计算目标用户与其他用户的相似度 int userIdx = userIndex.get(user.getId()); Map<Integer, Double> userSimilarities = new HashMap<>(); for (int i = 0; i < users.size(); i++) { if (i != userIdx) { // 计算 当前用户 与其他的每一个用户的评分向量的 余弦相似度 double similarity = calculateCosineSimilarity(ratingMatrix.getRow(userIdx), ratingMatrix.getRow(i)); userSimilarities.put(i, similarity); } } // 选择最相似的5个用户作为邻居用户 List<Integer> similarUsers = userSimilarities.entrySet().stream() // 按相似度值降序排序 .sorted(Map.Entry.<Integer, Double>comparingByValue().reversed()) // 取前5个最相似用户 .limit(5) // 提取用户索引 .map(Map.Entry::getKey) .collect(Collectors.toList());
最后是计算加权平均,当中还需要进行 归一化处理, 来避免了因用户群体整体相似度偏高/偏低导致的预测偏差,使得推荐结果更贴近用户的真实偏好。
整体代码较长,我就不贴了。
最后是controller接口提供API
/** * 推荐系统API控制器 * 提供个性化推荐、热门物品推荐等功能 */ @RestController @RequestMapping("/recommendations") public class RecommendationController { @Autowired private RecommendationService recommendationService; @Autowired private UserRepository userRepository; @Autowired private ItemRepository itemRepository; @Autowired private UserBehaviorRepository userBehaviorRepository; /** * 获取用户个性化推荐列表 * @return 推荐的物品列表 */ @PostMapping("/user") public V getRecommendationsForUser(@RequestBody UserBehavior item) { User user = userRepository.selectById(item.getUserId()); if (user == null) { return V.fail("必须登录"); } List<Item> recommendations = recommendationService.recommendItemsForUser(user, 10); return V.success(recommendations); } /** * 记录用户行为数据 * @param behavior 用户行为信息(如:浏览、评分、收藏等) * @return 操作结果 */ @PostMapping("/behavior") public V recordUserBehavior(@RequestBody UserBehavior behavior) { // 验证用户ID User user = userRepository.selectById(behavior.getUserId()); if (user == null) { return V.fail("用户不存在"); } // 验证物品ID Item item = itemRepository.selectById(behavior.getItemId()); if (item == null) { return V.fail("物品不存在"); } // 验证行为类型 if (behavior.getBehaviorType() == null || (!behavior.getBehaviorType().equals("VIEW") && !behavior.getBehaviorType().equals("LIKE") && !behavior.getBehaviorType().equals("PURCHASE"))) { return V.fail("无效的行为类型"); } // 验证评分范围(如果提供了评分) if (behavior.getRating() != null && (behavior.getRating() < 1 || behavior.getRating() > 5)) { return V.fail("评分必须在1-5之间"); } // 设置关联实体和时间戳 behavior.setTimestamp(LocalDateTime.now()); // 检查是否存在相同的行为记录 LambdaQueryWrapper<UserBehavior> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(UserBehavior::getUserId, behavior.getUserId()) .eq(UserBehavior::getItemId, behavior.getItemId()) .eq(UserBehavior::getBehaviorType, behavior.getBehaviorType()); UserBehavior existingBehavior = userBehaviorRepository.selectOne(wrapper); try { if (existingBehavior != null) { // 更新现有记录 behavior.setId(existingBehavior.getId()); userBehaviorRepository.updateById(behavior); } else { // 创建新记录 userBehaviorRepository.insert(behavior); } return V.success(behavior); } catch (Exception e) { return V.fail("保存用户行为失败:" + e.getMessage()); } } /** * 获取商品列表,支持分页查询 * @param page 页码,从1开始 * @param size 每页数量 * @return 分页的商品列表 */ @GetMapping("/items") public V getItems( @RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "10") int size) { Page<Item> pageRequest = new Page<>(page, size); IPage<Item> items = itemRepository.selectPage(pageRequest, null); return V.success(items); } /** * 商品详情 */ @GetMapping("/item-details/{id}") public V details(@PathVariable Long id) { Item item = itemRepository.selectById(id); return V.success(item); } }

浙公网安备 33010602011771号