Java推荐引擎案例

wen java案例 2

本文目录导读:

Java推荐引擎案例

  1. 基于协同过滤的电影推荐引擎
  2. 基于内容的商品推荐引擎
  3. 使用Mahout的推荐引擎(集成示例)
  4. 混合推荐引擎(综合多种算法)
  5. 推荐系统评估与优化
  6. 使用示例
  7. 优化建议

我来为您介绍几个Java推荐引擎的实战案例,涵盖不同的实现思路和技术栈。

基于协同过滤的电影推荐引擎

import java.util.*;
import java.util.stream.Collectors;
// 用户-电影评分实体
class UserMovieRating {
    private String userId;
    private String movieId;
    private double rating;
    // getter/setter 省略
}
// 基于用户的协同过滤推荐
public class CollaborativeFilteringRecommender {
    // 用户-电影评分数据
    private Map<String, Map<String, Double>> userRatings;
    public CollaborativeFilteringRecommender(List<UserMovieRating> ratings) {
        this.userRatings = new HashMap<>();
        for (UserMovieRating r : ratings) {
            userRatings.computeIfAbsent(r.getUserId(), k -> new HashMap<>())
                      .put(r.getMovieId(), r.getRating());
        }
    }
    // 计算皮尔逊相关系数
    private double pearsonCorrelation(Map<String, Double> user1, Map<String, Double> user2) {
        Set<String> commonMovies = new HashSet<>(user1.keySet());
        commonMovies.retainAll(user2.keySet());
        if (commonMovies.size() < 2) return 0;
        double sum1 = 0, sum2 = 0, sum1Sq = 0, sum2Sq = 0, pSum = 0;
        int n = commonMovies.size();
        for (String movie : commonMovies) {
            double r1 = user1.get(movie);
            double r2 = user2.get(movie);
            sum1 += r1;
            sum2 += r2;
            sum1Sq += r1 * r1;
            sum2Sq += r2 * r2;
            pSum += r1 * r2;
        }
        double num = pSum - (sum1 * sum2 / n);
        double den = Math.sqrt((sum1Sq - sum1*sum1/n) * (sum2Sq - sum2*sum2/n));
        return den == 0 ? 0 : num / den;
    }
    // 为目标用户推荐电影
    public List<String> recommendMovies(String userId, int topN) {
        Map<String, Double> targetUserRatings = userRatings.get(userId);
        if (targetUserRatings == null) return Collections.emptyList();
        // 计算相似用户
        Map<String, Double> userSimilarities = new HashMap<>();
        for (String otherUser : userRatings.keySet()) {
            if (!otherUser.equals(userId)) {
                double similarity = pearsonCorrelation(targetUserRatings, userRatings.get(otherUser));
                if (similarity > 0) {
                    userSimilarities.put(otherUser, similarity);
                }
            }
        }
        // 获取未评分的电影及其加权评分
        Map<String, Double> movieScores = new HashMap<>();
        for (Map.Entry<String, Double> similarUser : userSimilarities.entrySet()) {
            Map<String, Double> similarUserRatings = userRatings.get(similarUser.getKey());
            for (Map.Entry<String, Double> movieRating : similarUserRatings.entrySet()) {
                String movie = movieRating.getKey();
                if (!targetUserRatings.containsKey(movie)) {
                    movieScores.merge(movie, 
                        movieRating.getValue() * similarUser.getValue(), 
                        Double::sum);
                }
            }
        }
        // 排序并返回TopN推荐
        return movieScores.entrySet().stream()
                .sorted(Map.Entry.<String, Double>comparingByValue().reversed())
                .limit(topN)
                .map(Map.Entry::getKey)
                .collect(Collectors.toList());
    }
}

的商品推荐引擎

import java.util.*;
import java.util.stream.Collectors;
// 商品特征
class Product {
    private String id;
    private String name;
    private String category;
    private List<String> tags;
    private double price;
    private double rating;
    public Product(String id, String name, String category, List<String> tags, double price, double rating) {
        this.id = id;
        this.name = name;
        this.category = category;
        this.tags = tags;
        this.price = price;
        this.rating = rating;
    }
    // getter/setter
}
// 用户行为
class UserBehavior {
    private String userId;
    private String productId;
    private String action;  // view, purchase, add_to_cart
    private long timestamp;
    // getter/setter
}
的推荐引擎
public class ContentBasedRecommender {
    private Map<String, Product> productCatalog;
    private Map<String, List<UserBehavior>> userBehaviors;
    public ContentBasedRecommender(List<Product> products, List<UserBehavior> behaviors) {
        this.productCatalog = products.stream()
                .collect(Collectors.toMap(Product::getId, p -> p));
        this.userBehaviors = behaviors.stream()
                .collect(Collectors.groupingBy(UserBehavior::getUserId));
    }
    // 构建用户兴趣画像
    private Map<String, Double> buildUserProfile(String userId) {
        Map<String, Double> profile = new HashMap<>();
        List<UserBehavior> userActions = userBehaviors.getOrDefault(userId, Collections.emptyList());
        for (UserBehavior behavior : userActions) {
            Product product = productCatalog.get(behavior.getProductId());
            if (product != null) {
                double weight = getActionWeight(behavior.getAction());
                // 增加标签权重
                for (String tag : product.getTags()) {
                    profile.merge(tag, weight, Double::sum);
                }
                // 增加分类权重
                profile.merge("category_" + product.getCategory(), weight * 0.5, Double::sum);
            }
        }
        // 归一化
        double max = profile.values().stream().mapToDouble(Double::doubleValue).max().orElse(1.0);
        profile.replaceAll((k, v) -> v / max);
        return profile;
    }
    private double getActionWeight(String action) {
        switch (action) {
            case "purchase": return 1.0;
            case "add_to_cart": return 0.7;
            case "view": return 0.3;
            default: return 0.1;
        }
    }
    // 计算内容相似度
    private double calculateContentSimilarity(Map<String, Double> userProfile, Product product) {
        double similarity = 0;
        // 标签匹配度
        for (String tag : product.getTags()) {
            similarity += userProfile.getOrDefault(tag, 0.0);
        }
        // 分类匹配度
        similarity += userProfile.getOrDefault("category_" + product.getCategory(), 0.0) * 2;
        return similarity;
    }
    // 为用户推荐商品
    public List<Product> recommendProducts(String userId, int topN) {
        Map<String, Double> userProfile = buildUserProfile(userId);
        if (userProfile.isEmpty()) {
            return getPopularProducts(topN);
        }
        // 获取用户已购买的商品
        Set<String> purchasedProducts = userBehaviors.getOrDefault(userId, Collections.emptyList())
                .stream()
                .filter(b -> "purchase".equals(b.getAction()))
                .map(UserBehavior::getProductId)
                .collect(Collectors.toSet());
        // 计算推荐分数
        return productCatalog.values().stream()
                .filter(p -> !purchasedProducts.contains(p.getId()))
                .map(p -> new AbstractMap.SimpleEntry<>(p, calculateContentSimilarity(userProfile, p)))
                .sorted(Map.Entry.<Product, Double>comparingByValue().reversed())
                .limit(topN)
                .map(Map.Entry::getKey)
                .collect(Collectors.toList());
    }
    // 热门商品推荐
    private List<Product> getPopularProducts(int topN) {
        return productCatalog.values().stream()
                .sorted(Comparator.comparingDouble(Product::getRating).reversed())
                .limit(topN)
                .collect(Collectors.toList());
    }
}

使用Mahout的推荐引擎(集成示例)

// 需要添加Maven依赖:
// <dependency>
//     <groupId>org.apache.mahout</groupId>
//     <artifactId>mahout-mr</artifactId>
//     <version>0.12.2</version>
// </dependency>
import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.impl.model.file.FileDataModel;
import org.apache.mahout.cf.taste.impl.neighborhood.ThresholdUserNeighborhood;
import org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender;
import org.apache.mahout.cf.taste.impl.similarity.PearsonCorrelationSimilarity;
import org.apache.mahout.cf.taste.model.DataModel;
import org.apache.mahout.cf.taste.neighborhood.UserNeighborhood;
import org.apache.mahout.cf.taste.recommender.RecommendedItem;
import org.apache.mahout.cf.taste.recommender.Recommender;
import org.apache.mahout.cf.taste.similarity.UserSimilarity;
public class MahoutRecommenderExample {
    private Recommender recommender;
    public MahoutRecommenderExample(String dataFile) throws TasteException, IOException {
        // 1. 加载数据模型
        DataModel model = new FileDataModel(new File(dataFile));
        // 2. 计算用户相似度(皮尔逊相关系数)
        UserSimilarity similarity = new PearsonCorrelationSimilarity(model);
        // 3. 定义用户邻居(相似度阈值)
        UserNeighborhood neighborhood = new ThresholdUserNeighborhood(0.1, similarity, model);
        // 4. 创建推荐器
        recommender = new GenericUserBasedRecommender(model, neighborhood, similarity);
    }
    // 为指定用户推荐商品
    public List<RecommendedItem> recommend(long userId, int howMany) throws TasteException {
        return recommender.recommend(userId, howMany);
    }
    // 评估推荐质量
    public double evaluateRecommender(DataModel model) {
        // 使用各种评估指标
        return 0.0;
    }
    // 使用示例
    public static void main(String[] args) {
        try {
            // 数据格式: userId, itemId, preference
            MahoutRecommenderExample recommender = new MahoutRecommenderExample("ratings.csv");
            // 为用户1推荐10个商品
            List<RecommendedItem> recommendations = recommender.recommend(1, 10);
            for (RecommendedItem item : recommendations) {
                System.out.println("推荐商品: " + item.getItemID() + 
                                 ", 推荐分数: " + item.getValue());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

混合推荐引擎(综合多种算法)

import java.util.*;
import java.util.stream.Collectors;
// 推荐结果
class RecommendationResult {
    private String itemId;
    private double score;
    private String algorithm;  // 使用的算法
    public RecommendationResult(String itemId, double score, String algorithm) {
        this.itemId = itemId;
        this.score = score;
        this.algorithm = algorithm;
    }
    // getter/setter
}
// 混合推荐引擎
public class HybridRecommender {
    private CollaborativeFilteringRecommender cfRecommender;
    private ContentBasedRecommender cbRecommender;
    private Map<String, Double> algorithmWeights;
    public HybridRecommender(List<Product> products, List<UserBehavior> behaviors) {
        // 初始化不同算法
        this.cfRecommender = new CollaborativeFilteringRecommender(products, behaviors);
        this.cbRecommender = new ContentBasedRecommender(products, behaviors);
        // 设置算法权重(可以通过机器学习调整)
        this.algorithmWeights = new HashMap<>();
        algorithmWeights.put("collaborative", 0.6);
        algorithmWeights.put("content_based", 0.4);
    }
    // 混合推荐
    public List<RecommendationResult> hybridRecommend(String userId, int topN) {
        Map<String, Double> combinedScores = new HashMap<>();
        // 1. 协同过滤推荐
        List<RecommendationResult> cfResults = cfRecommender.recommend(userId, topN * 2);
        for (RecommendationResult result : cfResults) {
            combinedScores.merge(result.getItemId(), 
                result.getScore() * algorithmWeights.get("collaborative"), 
                Double::sum);
        }
        // 2. 基于内容推荐
        List<RecommendationResult> cbResults = cbRecommender.recommend(userId, topN * 2);
        for (RecommendationResult result : cbResults) {
            combinedScores.merge(result.getItemId(),
                result.getScore() * algorithmWeights.get("content_based"),
                Double::sum);
        }
        // 3. 排序并返回
        return combinedScores.entrySet().stream()
                .sorted(Map.Entry.<String, Double>comparingByValue().reversed())
                .limit(topN)
                .map(e -> new RecommendationResult(e.getKey(), e.getValue(), "hybrid"))
                .collect(Collectors.toList());
    }
    // 动态调整算法权重(基于A/B测试)
    public void updateWeights(Map<String, Double> newWeights) {
        this.algorithmWeights = newWeights;
    }
}

推荐系统评估与优化

import java.util.*;
import java.util.stream.Collectors;
public class RecommenderEvaluator {
    // 计算推荐准确率
    public static double calculatePrecision(List<String> recommendations, 
                                            List<String> actualItems) {
        if (recommendations.isEmpty()) return 0;
        Set<String> recSet = new HashSet<>(recommendations);
        long hits = actualItems.stream()
                .filter(recSet::contains)
                .count();
        return (double) hits / recommendations.size();
    }
    // 计算召回率
    public static double calculateRecall(List<String> recommendations, 
                                          List<String> actualItems) {
        if (actualItems.isEmpty()) return 0;
        Set<String> recSet = new HashSet<>(recommendations);
        long hits = actualItems.stream()
                .filter(recSet::contains)
                .count();
        return (double) hits / actualItems.size();
    }
    // 计算F1分数
    public static double calculateF1Score(double precision, double recall) {
        if (precision + recall == 0) return 0;
        return 2 * (precision * recall) / (precision + recall);
    }
    // 平均准确率(MAP)
    public static double calculateMeanAveragePrecision(
            Map<String, List<String>> userRecommendations,
            Map<String, List<String>> userActualItems) {
        double sumAP = 0;
        int userCount = 0;
        for (String userId : userRecommendations.keySet()) {
            List<String> recs = userRecommendations.get(userId);
            List<String> actuals = userActualItems.get(userId);
            if (actuals != null && !actuals.isEmpty()) {
                sumAP += calculateAveragePrecision(recs, actuals);
                userCount++;
            }
        }
        return userCount > 0 ? sumAP / userCount : 0;
    }
    private static double calculateAveragePrecision(List<String> recommendations, 
                                                     List<String> actualItems) {
        Set<String> actualSet = new HashSet<>(actualItems);
        double sumPrecision = 0;
        int hitCount = 0;
        for (int i = 0; i < recommendations.size(); i++) {
            if (actualSet.contains(recommendations.get(i))) {
                hitCount++;
                sumPrecision += (double) hitCount / (i + 1);
            }
        }
        return hitCount > 0 ? sumPrecision / Math.min(actualItems.size(), recommendations.size()) : 0;
    }
}

使用示例

public class RecommendationDemo {
    public static void main(String[] args) {
        // 1. 准备数据
        List<Product> products = Arrays.asList(
            new Product("1", "Java编程思想", "编程", Arrays.asList("Java", "编程"), 89.0, 4.5),
            new Product("2", "Spring实战", "编程", Arrays.asList("Java", "框架"), 79.0, 4.2),
            new Product("3", "算法导论", "编程", Arrays.asList("算法", "数据结构"), 99.0, 4.8),
            new Product("4", "微服务设计", "编程", Arrays.asList("微服务", "架构"), 69.0, 4.0)
        );
        // 2. 创建推荐引擎
        ContentBasedRecommender recommender = new ContentBasedRecommender(products, userBehaviors);
        // 3. 生成推荐
        List<Product> recommendations = recommender.recommendProducts("user123", 5);
        // 4. 输出结果
        System.out.println("为您推荐以下商品:");
        for (Product product : recommendations) {
            System.out.println("- " + product.getName() + " (评分: " + product.getRating() + ")");
        }
    }
}

优化建议

  1. 性能优化

    • 使用Redis缓存用户画像和推荐结果
    • 预计算相似度矩阵
    • 使用批量处理而非实时计算
  2. 准确性提升

    • 加入时间衰减因子
    • 考虑用户实时行为
    • 实现冷启动处理
  3. 扩展性

    • 集成Spark进行大规模数据处理
    • 使用Elasticsearch进行向量检索
    • 微服务化架构

这些案例涵盖了推荐系统的主要实现方式,您可以根据实际需求选择合适的技术方案。

上一篇ONNX案例

下一篇Java评分卡案例

抱歉,评论功能暂时关闭!