推荐系统召回层如何筛选不相关的物品

本文介绍了推荐系统中的召回层和排序层的特点,召回层负责快速筛选大量候选物品,强调速度和粗粒度的准确性,而排序层则注重精细化的精确排序。文中探讨了单路召回策略的简单高效但局限性,多路召回策略的灵活性与平衡,以及基于Embedding的召回策略,利用物品和用户Embedding相似性进行召回。这些策略在实际应用中需要根据场景和需求进行调整和优化。
摘要由CSDN通过智能技术生成

召回层和排序层的特点

  • 召回层处于推荐系统的线上服务模块之中,推荐服务器从数据库或内存中拿到所有候选物品集合后,会依次经过召回层→排序层→再排序层(补充算法层),才能够产生用户最终看到的推荐列表。
  • 召回层月排序层总结图:在这里插入图片描述
  • 召回层应该满足特点:候选集规模大(百万级)、模型复杂度低、所用特征少、处理速度快、排序精度低。
  • 排序层应该满足特点:候选集规模小(几百级)、模型复杂度高、所用特征多、处理速度慢、排序精度高。
  • 要注意的是,在我们设计召回层时,计算速度和召回率其实是两个矛盾的指标。这个就有点类似于CV中的目标检测精度与召回率。为了提高计算速度,召回策略就得尽量简单;而为了提高召回率或者说召回精度,召回策略就得把用户感兴趣的物品囊括在内,这又要求召回策略不能过于简单,否则召回物品就无法满足排序模型的要求。

单路召回策略

  • 单策略召回是指通过制定一条规则或者利用一个简单模型来快速地召回可能的相关物品
  • 在Sparrow RecSys项目中,我就制定了这样一条召回策略:如果用户对电影 A 的评分较高,比如超过 4 分,那么我们就将与 A 风格相同,并且平均评分在前 50 的电影召回,放入排序候选集中。
  • 相关代码如下:

//详见SimilarMovieFlow class
public static List<Movie> candidateGenerator(Movie movie){
    ArrayList<Movie> candidates = new ArrayList<>();
    //使用HashMap去重
    HashMap<Integer, Movie> candidateMap = new HashMap<>();
    //电影movie包含多个风格标签
    for (String genre : movie.getGenres()){
        //召回策略的实现
        List<Movie> oneCandidates = DataManager.getInstance().getMoviesByGenre(genre, 100, "rating"); 
        for (Movie candidate : oneCandidates){
            candidateMap.put(candidate.getMovieId(), candidate);
        }
    }
    //去掉movie本身
    if (candidateMap.containsKey(movie.getMovieId())){
        candidateMap.remove(movie.getMovieId());
    }
    //最终的候选集
    return new ArrayList<>(candidateMap.values());
}
  • 单策略召回是非常简单直观的,并且它的计算速度一定是非常快的。缺点就是它有很强的局限性。因为大多数时候用户的兴趣是非常多元的,他们不仅喜欢自己感兴趣的,也喜欢热门的,当然很多时候也喜欢新上映的。这时候,单一策略就难以满足用户的潜在需求了。

多路召回策略

  • 多路召回策略就是指采用不同的策略、特征或简单模型,分别召回一部分候选集,然后把候选集混合在一起供后续排序模型使用的策略。各简单策略保证候选集的快速召回,从不同角度设计的策略又能保证召回率接近理想的状态,不至于损害排序效果。所以,多路召回策略是在计算速度和召回率之间进行权衡的结果。
  • 在电影推荐中,我们一般可以通过以下的多路召回策略来设计。
    在这里插入图片描述
  • 以Sparrow Recsys项目为例,风格类型、高分评价、最新上映,这三路召回策略组成的多路召回方法。
  • 相关代码如下:

public static List<Movie> multipleRetrievalCandidates(List<Movie> userHistory){
    HashSet<String> genres = new HashSet<>();
    //根据用户看过的电影,统计用户喜欢的电影风格
    for (Movie movie : userHistory){
        genres.addAll(movie.getGenres());
    }
    //根据用户喜欢的风格召回电影候选集
    HashMap<Integer, Movie> candidateMap = new HashMap<>();
    for (String genre : genres){
        List<Movie> oneCandidates = DataManager.getInstance().getMoviesByGenre(genre, 20, "rating");
        for (Movie candidate : oneCandidates){
            candidateMap.put(candidate.getMovieId(), candidate);
        }
    }
    //召回所有电影中排名最高的100部电影
    List<Movie> highRatingCandidates = DataManager.getInstance().getMovies(100, "rating");
    for (Movie candidate : highRatingCandidates){
        candidateMap.put(candidate.getMovieId(), candidate);
    }
    //召回最新上映的100部电影
    List<Movie> latestCandidates = DataManager.getInstance().getMovies(100, "releaseYear");
    for (Movie candidate : latestCandidates){
        candidateMap.put(candidate.getMovieId(), candidate);
    }
    //去除用户已经观看过的电影
    for (Movie movie : userHistory){
        candidateMap.remove(movie.getMovieId());
    }
    //形成最终的候选集
    return new ArrayList<>(candidateMap.values());
}
  • 优化召回效率改进方法:多线程并行、建立标签 / 特征索引、建立常用召回集缓存。
  • 多路召回策略缺点:确定每一路的召回物品数量时,往往需要大量的人工参与和调整,具体的数值需要经过大量线上 AB 测试来决定。此外,因为策略之间的信息和数据是割裂的,所以我们很难综合考虑不同策略对一个物品的影响。

基于Embedding的召回策略

  • 利用物品和用户 Embedding 相似性来构建召回层,是深度学习推荐系统中非常经典的技术方案。

  • Embedding优势:

    • 多路召回中使用的“兴趣标签”、“热门度”、“流行趋势”、“物品属性”等信息都可以作为Embedding方法中的附加信息(Side Information),融合进最终的Embedding向量中 。
    • Embedding 召回的评分具有连续性。
    • 在线上服务的过程中,Embedding相似性的计算也相对简单和直接。
  • 相关代码如下:


public static List<Movie> retrievalCandidatesByEmbedding(User user){
    if (null == user){
        return null;
    }
    //获取用户embedding向量
    double[] userEmbedding = DataManager.getInstance().getUserEmbedding(user.getUserId(), "item2vec");
    if (null == userEmbedding){
        return null;
    }
    //获取所有影片候选集(这里取评分排名前10000的影片作为全部候选集)
    List<Movie> allCandidates = DataManager.getInstance().getMovies(10000, "rating");
    HashMap<Movie,Double> movieScoreMap = new HashMap<>();
    //逐一获取电影embedding,并计算与用户embedding的相似度
    for (Movie candidate : allCandidates){
        double[] itemEmbedding = DataManager.getInstance().getItemEmbedding(candidate.getMovieId(), "item2vec");
        double similarity = calculateEmbeddingSimilarity(userEmbedding, itemEmbedding);
        movieScoreMap.put(candidate, similarity);
    }
   
    List<Map.Entry<Movie,Double>> movieScoreList = new ArrayList<>(movieScoreMap.entrySet());
    //按照用户-电影embedding相似度进行候选电影集排序
    movieScoreList.sort(Map.Entry.comparingByValue());


    //生成并返回最终的候选集
    List<Movie> candidates = new ArrayList<>();
    for (Map.Entry<Movie,Double> movieScoreEntry : movieScoreList){
        candidates.add(movieScoreEntry.getKey());
    }
    return candidates.subList(0, Math.min(candidates.size(), size));
}
  • 通过代码不难看出三步生成了最终的候选集。
    • 第一步,我们获取用户的 Embedding。
    • 第二步,我们获取所有物品的候选集,并且逐一获取物品的 Embedding,计算物品 Embedding 和用户 Embedding 的相似度。
    • 第三步,我们根据相似度排序,返回规定大小的候选集。

召回层总结

在这里插入图片描述

  • 一个特点,三个方案:
  • 特点:召回层要快速准确地过滤出相关物品,缩小候选集
  • 方案1:简单快速的单策略召回
  • 方案2:业界主流的多路召回
  • 方案3:深度学习推荐系统Embedding召回
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值