4、经典召回策略(下):基于内容的召回、基于图的召回(PersonalRank)、热点与冷启动召回

好,咱们接着聊召回策略的下半场。上一章讲了协同过滤和向量化召回,这一章我重点说说另外三个实战中绕不开的策略:基于内容的召回、基于图的PersonalRank,以及热点与冷启动召回。

说实话,这三个策略在工业界的使用频率非常高。尤其是冷启动,几乎每个新上线的推荐系统都会遇到。我个人习惯把这三类策略称为「兜底三件套」——当你的协同过滤和向量召回效果不佳时,它们往往能救场。

4.1 基于内容的召回:从物品本身找线索

基于内容的召回,说白了就是「物以类聚」。我们不看用户行为,只看物品本身的属性。比如一篇新闻,它的标题、正文、标签、分类,这些就是它的「内容特征」。

我在项目中遇到过这样一个场景:一个新用户刚注册,没有任何行为记录。协同过滤直接歇菜。但基于内容的召回可以工作——只要用户选了「我喜欢科技类文章」,我们就能把科技类的内容推给他。

核心思路

  • 为每个物品构建特征向量(TF-IDF、Word2Vec、BERT embedding等)
  • 为用户构建兴趣画像(根据历史行为物品的特征加权平均)
  • 计算用户画像与候选物品的相似度,取TopN召回

举个简单的例子,假设我们有一篇电影《盗梦空间》,它的特征向量可能是:

{
  "genre": ["科幻", "悬疑", "动作"],
  "director": "诺兰",
  "actors": ["小李子", "渡边谦"],
  "keywords": ["梦境", "潜意识", "时间"]
}

用户A看过《星际穿越》和《记忆碎片》,那他的画像里「诺兰」这个导演的权重就会很高。基于内容的召回就会把《盗梦空间》排到前面。

关键点:基于内容的召回有个天然优势——可解释性强。你可以直接告诉用户「因为您喜欢诺兰导演的作品,所以推荐了《盗梦空间》」。这在很多业务场景下非常重要。

但缺点也很明显:它容易陷入「信息茧房」。用户只看过科幻片,你就一直推科幻片,用户永远看不到其他类型的好内容。所以它通常只作为召回路之一,不能独占。

4.2 基于图的召回:PersonalRank算法

接下来这个算法,我个人非常喜欢。PersonalRank,本质上是在用户-物品二部图上做随机游走。

你想想看,用户和物品之间天然就是一个图结构。用户点击了物品,就形成了一条边。PersonalRank的思路是:从一个用户节点出发,在图上随机游走,游走过程中经过的物品节点,就是该用户可能感兴趣的物品。

算法流程

  1. 初始化:给目标用户u的节点赋初始分1.0,其他节点赋0
  2. 开始迭代:每个节点以概率α继续游走,以概率(1-α)回到起点
  3. 游走规则:从当前节点随机选择一条边,走到邻居节点
  4. 收敛后,所有物品节点的得分就是该用户的推荐分数

代码实现其实不复杂,我贴一个简化版:

def personal_rank(graph, user_id, alpha=0.85, max_iter=100):
    """
    graph: 用户-物品二部图,格式 {node: {neighbor: weight}}
    user_id: 目标用户
    alpha: 游走概率
    """
    rank = {node: 0 for node in graph}
    rank[user_id] = 1.0
    
    for _ in range(max_iter):
        new_rank = {node: 0 for node in graph}
        for node in graph:
            if rank[node] == 0:
                continue
            # 以alpha概率游走
            for neighbor, weight in graph[node].items():
                new_rank[neighbor] += alpha * rank[node] * weight / len(graph[node])
            # 以(1-alpha)概率回到起点
            new_rank[user_id] += (1 - alpha) * rank[node]
        rank = new_rank
    
    # 只返回物品节点的得分
    return {node: score for node, score in rank.items() if node.startswith('item_')}

避坑指南:我曾经在线上部署PersonalRank时踩过一个坑——图太大,迭代收敛太慢。后来我做了两个优化:一是只保留用户最近30天的行为构建子图;二是用矩阵运算替代循环迭代。效果立竿见影,耗时从秒级降到了毫秒级。

PersonalRank的好处是能捕捉到「间接关联」。比如用户A和用户B都看过《星际穿越》,用户B还看过《肖申克的救赎》,那PersonalRank就有可能把《肖申克的救赎》推荐给用户A。这种「二度关联」是协同过滤做不到的。

4.3 热点召回:流量分发的基本盘

热点召回,听起来简单,但做起来门道不少。说白了就是把当前最热的内容推给用户。但「热」怎么定义?

我个人习惯用综合热度分

hot_score = 0.4 * log(click_7d + 1) + 0.3 * log(like_7d + 1) + 0.2 * log(share_7d + 1) + 0.1 * log(comment_7d + 1)

这里用log是为了防止某个指标爆炸性增长。比如一个视频突然爆火,点击量从100涨到100万,如果不做log处理,其他指标就完全被淹没了。

热点召回的几个要点

  • 时间衰减:7天前的热度要打折,我一般用指数衰减
  • 去重:同一个热点事件可能有多个相似内容,只保留一个
  • 多样性:不能全是娱乐热点,要兼顾科技、体育、财经等类别

注意:热点召回虽然简单,但千万别把它当成主力。我见过一些团队把热点召回放在第一位,结果用户看到的全是千篇一律的爆款内容,用户留存反而下降了。热点召回更适合作为「补充策略」,占比控制在10%-20%比较合理。

4.4 冷启动召回:新用户和新物品的救星

冷启动,这是推荐系统里最头疼的问题之一。没有之一。

冷启动分两种:用户冷启动物品冷启动

用户冷启动:新用户来了,没有任何行为记录。怎么办?

  • 基于人口统计学:年龄、性别、地域、设备型号等
  • 基于注册信息:用户选择的兴趣标签
  • 基于社交关系:如果允许,可以看用户的好友喜欢什么
  • 基于设备指纹:同一设备上的历史用户行为可以借鉴

我在项目中用过一种比较巧妙的方法——冷启动探索池。把新内容按类别均匀分成若干组,新用户注册时随机分配一组。等用户产生了一些行为后,再切换到个性化推荐。这样既保证了新用户有内容可看,又为后续的个性化积累了数据。

物品冷启动:新内容上线了,没人看过,怎么推?

  • 基于内容特征:用新物品的内容特征匹配用户画像
  • 基于创作者历史:如果创作者之前的内容表现好,新内容也值得一试
  • 基于探索策略:给新物品一定的「曝光配额」,比如前1000次曝光免费

核心原则:冷启动的本质是「用探索换信息」。你给新用户推一些内容,用户点击或不点击,都是在给你提供信息。所以冷启动策略要设计得足够「宽容」——允许试错,允许低点击率,只要信息能快速积累起来。

4.5 三种策略的对比与组合

最后,我整理了一张对比表,方便你快速理解:

策略 核心思想 适用场景 优点 缺点
基于内容 物品属性相似 新用户、长尾物品 可解释性强、无冷启动问题 信息茧房、特征工程复杂
PersonalRank 图上随机游走 用户行为稀疏 能捕捉间接关联 计算量大、图构建复杂
热点召回 全局热度排序 新用户、时效性内容 简单高效、覆盖面广 个性化弱、容易同质化

在实际的推荐系统中,这三种策略通常是多路召回的关系。每一路召回一个TopN列表,然后通过排序模型融合。我个人习惯的做法是:

  • 基于内容召回:占30%
  • PersonalRank召回:占20%
  • 热点召回:占15%
  • 协同过滤/向量召回:占35%

当然,这个比例不是固定的。不同业务、不同阶段,比例要动态调整。比如新用户多的时候,冷启动和热点的比例就要调高。

嗯,这一章的内容就到这里。下一章我们聊聊排序模型——从粗排到精排,再到重排,整个排序链路怎么设计。到时候见。