行业资讯
多路召回融合:向量召回、协同过滤和热门召回的权重分配
多路召回融合向量召回、协同过滤和热门召回的权重分配一、单路召回的天花板精准度、多样性和新颖性不可兼得推荐系统的召回阶段决定了最终给精排模型喂什么数据。只依赖单一召回通道就像只用一种感官理解世界。纯协同过滤召回ItemCF/UserCF的问题是马太效应——热门物品越来越热长尾物品永无出头之日。数据驱动下这是必然结果热门物品交互多、特征充分、相似度计算准确长尾物品数据稀疏协同过滤根本算不出合理的相似度。纯向量召回双塔模型产出的 Embedding 做 ANN 检索在语义理解上有优势但它的问题是缺乏行为信号的强约束。向量空间里距离近的两个物品在内容上确实相似但用户就是不会连续点击它们——因为推荐不是内容理解是行为预测。纯热门召回最简单粗暴按过去 24 小时点击量排序Top 100 直接推。问题是千人一面——所有用户看到的都一样个性化程度为零。在垂直领域如新闻资讯可以接受但在电商和短视频领域就是灾难。多路召回的核心不是把几个通道的结果拼一起而是不同通道的侧重点互补。协同过滤提供行为协同信号和你相似的人也买了向量召回提供内容语义信号这个物品和你看过的语义接近热门召回提供群体统计信号大家都在看。三路信息各司其职才是多路召回的意义。二、权重不是拍脑袋AB 实验里的数据真相权重分配是整个多路召回中最容易拍板定案的环节。常见做法是经验分配协同过滤 50%、向量召回 30%、热门召回 20%上线就行。但实际效果往往和直觉相反。在某内容推荐场景的 AB 实验中三组不同权重对比的数据是这样的方案协同过滤向量召回热门召回点击率变化多样性(Entropy)新颖性基准50%30%20%基准基准基准加重协同70%20%10%3.2%-8.5%-12.3%加重向量30%60%10%-1.8%15.7%22.1%加重热门30%20%50%8.1%-18.2%-25.4%数据很诚实如果想冲点击率多给热门召回权重是最立竿见影的——这在逻辑上也成立因为热门内容被验证过用户点击概率更高。但多样性指标惨不忍睹——用户看到的内容会越来越窄。如果追求长期的内容生态和用户留存向量召回权重要提上去因为 ANN 检索天然具备多样性——向量空间里每个方向都能找到相似但不相同的内容。代价是短期点击率的稀释。真实场景下权重不是固定的应该是动态的。新用户注册 3 天内热门召回 40%、协同过滤 30%、向量召回 30%——新用户缺乏行为数据协同信号弱先用热门兜底。老用户注册 30 天以上协同过滤 50%、向量召回 35%、热门召回 15%——行为数据充分个性化信号为主。这是一个用户生命周期权重衰减策略。三、召回合并去重不是简单的 union多路结果合并时同一个物品可能出现在多路召回中。直接去重取前 N 个是最简单的做法但会丢失召回来源信息——这个物品是被协同过滤找到的还是被向量召回挖掘的来源信息对精排模型有重要价值。实践中采用带来源标记的加权合并每个物品除了物品 ID 外附带回召通道标签和通道内评分。精排模型可以根据来源通道配置不同的特征权重。Fan-in 的架构下合并流程如下type RecallItem struct { ItemID string Score float64 // 通道内归一化分数 Channel string // cf | vec | hot RawScore float64 // 通道内原始分数 } func MergeMultiRecall(cfItems, vecItems, hotItems []RecallItem, totalQuota int) []RecallItem { seen : make(map[string]*RecallItem, totalQuota*2) // 按通道权重交错插入而非通道间排序插入 // 这样保证了每个通道的结果都有机会进入候选池 cfIdx, vecIdx, hotIdx : 0, 0, 0 for len(seen) totalQuota { // 交替从三个通道取出一个未重复的物品 if len(seen) totalQuota cfIdx len(cfItems) { if _, exists : seen[cfItems[cfIdx].ItemID]; !exists { seen[cfItems[cfIdx].ItemID] cfItems[cfIdx] } cfIdx } if len(seen) totalQuota vecIdx len(vecItems) { if _, exists : seen[vecItems[vecIdx].ItemID]; !exists { seen[vecItems[vecIdx].ItemID] vecItems[vecIdx] } vecIdx } if len(seen) totalQuota hotIdx len(hotItems) { if _, exists : seen[hotItems[hotIdx].ItemID]; !exists { seen[hotItems[hotIdx].ItemID] hotItems[hotIdx] } hotIdx } // 全部耗尽则退出 if cfIdx len(cfItems) vecIdx len(vecItems) hotIdx len(hotItems) { break } } result : make([]RecallItem, 0, len(seen)) for _, item : range seen { result append(result, *item) } return result }交错插入而非排序插入是这个算法的关键。如果用全局分数排序合并热门通道的高分物品会霸占 Top 位置向量召回的潜力股根本挤不进候选池。交错插入保证了每个通道的配额——协同过滤占 50% 的位置向量召回占 35%热门召回占 15%与权重策略对齐。四、多路召回的边界更多通道不等于更好多路召回本质是在计算成本和召回覆盖之间做交易。加一路召回服务延迟增加一次 RPC 调用的时间约 5-10ms召回池扩大但精排压力也增大。三路召回是比较均衡的方案。超过五路边际收益急剧下降——第六路、第七路召回能多捞出来的有效物品极少通常不超过 1%但延迟和复杂度持续增长。某内容平台实测从 3 路加到 5 路点击率提升 2.1%从 5 路加到 7 路点击率仅提升 0.3%但 P99 延迟增加 18ms。另一个陷阱是比例失调导致的覆盖偏斜。如果协同过滤召回 500 个、向量召回 200 个、热门召回 500 个合并去重后热门通道的物品会占据超过一半的候选池——因为热门通道产生 500 个候选已经覆盖了大量常见物品。解决方案是控制每路召回的配额上限而非产出数量。每路都产出上限量的候选但只取最大配额。五、总结多路召回融合的核心不是把各种算法结果拼在一起而是让不同通道各司其职协同过滤提供行为信号、向量召回提供内容语义、热门召回提供先验统计。权重分配必须有 AB 实验数据支撑不同用户生命周期阶段使用不同的权重策略而非一刀切。合并策略采用交错插入而非全局排序确保每个通道的召回结果都能进入候选池。带来源标记的召回结果把通道信息传递到精排层让精排模型可以差异化地利用不同来源的信号。三路召回是投入产出比最优的组合。超过五路边际收益极低。多路召回的本质是一场精确度、多样性和计算成本三者之间的持续博弈——没有一劳永逸的权重公式只有跟着数据持续迭代的调优过程。
郑州网站建设
网页设计
企业官网