ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

推荐系统召回模型全解析:从协同过滤到双塔与多路融合

推荐系统召回模型全解析:从协同过滤到双塔与多路融合 做推荐算法这几年被问得最多的一个问题不是“精排模型怎么调参”而是“召回模型到底怎么做”。原因很简单精排跑得再好如果召回根本没把用户喜欢的东西捞进来后面全是白搭。我见过不少刚入行的同学简历上写着“负责召回模型”但问到具体召回路径怎么设计、双塔负样本怎么采、向量索引用什么就开始含糊。这篇文章就把召回模型这条线从头到尾梳理一遍从协同过滤到双塔、从向量检索到多路融合把原理、实操、坑点一次性讲清楚。适合推荐算法工程师、准备校招社招的同学以及刚转行做推荐系统的人。1. 推荐系统漏斗里召回为什么是第一步1.1 从全量物品到候选集算力的现实问题推荐系统面对的物品池少则百万多则上亿。短视频平台每天新增的feed量电商平台的SPU数量新闻资讯的文章总量都在这个量级。如果让精排模型对全量物品逐条打分再用复杂模型比如DeepFM、DIN、Transformer去做推理单用户一次请求的算力消耗就会爆炸。互联网在线服务的时延要求是百毫秒级服务端根本扛不住。这就是召回存在的根本原因它不是为了让结果变好而是先从一个巨大的物品集合里用尽量轻量、尽量快的方法选出几百上千个“可能有用”的候选把问题的规模从百万级压缩到千级甚至百级。很多人会忽略一个事实召回和精排的核心矛盾是不同的。精排要的是精度模型越大、特征越细越好召回要的是“别漏”同时要有足够多的路来保证覆盖面。用一句话概括精排是在给定的几百个东西里挑出最好的几个召回是在几百万个东西里不丢掉那几个可能被挑中的。1.2 召回目标与评价指标Hit Rate、覆盖率、时延因为召回的目标不是“排序准”所以评估它的指标也跟精排不一样。精排看AUC、GAUC召回首先要看的是召回率Recall和命中率Hit Rate。Hit Rate的定义很简单对用户历史上真实发生过的交互物品有没有出现在召回的候选集合里。比如用户今天点击了100个商品你的召回集合里包含其中30个那Hit Rate就是30%。这里的核心含义是“天花板”如果召回阶段就丢掉了70%的点击那精排再怎么优化能把的也只有剩下的30%。Recall在推荐里和Hit Rate类似常用来评估多路召回的整体覆盖能力。除此之外还有一个容易被忽视的指标是候选集多样性。如果几路召回全都返回相似的头部热门内容覆盖率看起来不错但用户真正个性化的长尾需求全都丢了后续精排再强也没用。我见过一个电商项目某路向量召回因为训练数据倾斜导致返回的100个候选里有80个是同品类的后面精排无论怎么调都看不出差异就是这个原因。线上评估召回还有一个硬指标时延。召回方法再漂亮如果单次请求超过20毫秒在大型系统里就很难落地。这也是为什么双塔模型配合向量检索引擎会成为工业界的主流因为它的在线代价就是一次向量相似度检索以现在的硬件水平毫秒级返回是能做到的。2. 协同过滤和矩阵分解召回模型的起点2.1 UserCF与ItemCF老方法为什么还没过时讲召回模型绕不开协同过滤。虽然现在很多团队已经切换到向量召回但协同过滤依然是理解后续所有召回方法的基础。UserCF的核心是“相似的人喜欢同样的东西”先根据用户历史行为计算用户之间的相似度找到和你兴趣最接近的一批用户把他们喜欢过而你没交互过的物品作为候选。ItemCF的核心是“喜欢过这个物品的人也常喜欢那个物品”根据物品之间的共现关系计算相似度然后拿用户历史交互过的物品去拉相似物品。在实际落地时ItemCF往往比UserCF更常用。原因在于用户兴趣变化快UserCF要实时计算用户相似集合维护成本和延迟都偏高。而ItemCF有天然的“可解释性”因为用户点击了A商品所以推荐和A相似的B商品这在电商和内容平台里都讲得通。另外一个细节是ItemCF建立的物品相似度矩阵可以离线算好线上只需要查表聚合性能非常好在高并发场景下特别吃香。但ItemCF有个臭名昭著的缺陷偏向头部热门物品。一个爆款商品和大量商品都有共现关系相似度矩阵中它的热度会盖过长尾物品。这时候如果不做惩罚召回结果会迅速同质化大家都推那几个爆款用户很快会疲劳。常规做法是引入一个比如similarity 共现次数 / (a 共现次数)或者直接对共现次数做log衰减的惩罚项让低频但精准的关联也能浮出水面。2.2 矩阵分解与隐语义模型ALS、BPR背后的思路协同过滤的问题在于稀疏用户和物品的交互矩阵动辄几十亿但每个用户只和其中极小一部分物品产生过行为直接用原始矩阵做相似度计算数据稀疏得吓人。矩阵分解的思路是把用户-物品交互矩阵拆成两个低维矩阵一个用户隐向量矩阵一个物品隐向量矩阵两个矩阵的乘积要尽可能还原原始交互矩阵。换句话说我们假设用户和物品之间的关系可以被少数几个隐含因素解释。这些隐含因素可以粗略理解为“风格”“题材”“价格带”“品牌偏好”等等虽然它的真实含义往往没法直接解释。工业界常用两种训练方式。一种是ALS交替最小二乘适合优化平方误差也就是预测评分和真实评分的差常用于有显式评分的场景。另一种是BPR贝叶斯个性化排序它不直接预测评分而是优化“用户对正样本的偏好大于负样本”这一对关系非常适合只有点击、购买等隐式反馈的场景。BPR的做法是每次同时采样一个用户交互过的正样本和一个没交互过的负样本构造一个pairwise的损失让正样本的得分尽量高于负样本。2.3 协同过滤在召回落地中的坑把协同过滤真正做成召回服务有几个细节必须处理。第一用户的协同过滤结果需要提前离线算好存入缓存否则在线实时计算用户最近邻代价太高。第二物品相似度和用户相似度会随时间变化必须设置定期更新策略否则新出的热点内容很可能因为冷启动而永远进不了候选。第三交互行为需要做降权。用户点击和购买的价值是不一样的购买、完播、收藏这类强反馈行为应该在计算时获得更高的权重。我还踩过一个更隐蔽的坑用户重复消费行为对相似度计算的干扰。比如在音乐场景用户单曲循环一首歌100次这会造成一种虚假的强共现关系让模型误以为这首歌跟任何被听过的歌都高度相似。解决方法是在构造行为序列时对重复行为做去重或者截断只保留一次有效交互。3. 双塔向量召回工业界的标配3.1 双塔结构为什么能成为主流传统协同过滤能解决一部分召回问题但它本质上是“记忆”而不是“泛化”模型只能从历史共现中找规律遇到没有交互过的用户和物品就很难处理。真正让召回模型迈上一个台阶的是向量召回其中最经典的就是双塔模型。双塔的结构很好理解左边是用户塔输入用户特征输出一个用户向量右边是物品塔输入物品特征输出一个物品向量两个向量通过内积或者余弦相似度计算相关性得分。训练时让正样本对的得分高负样本对的得分低。上线后把物品向量提前算好存入向量索引用户请求时算出用户向量然后做一次近邻检索就能拿到相似度最高的那一批物品。它的核心优势在于“解耦”。用户塔和物品塔可以分别处理各自的特征互不干扰物品向量可以离线批量算好线上部分只需要对用户侧做实时推理。所以双塔模型天然适配大规模场景工程上非常友好。相比之下如果用一个复杂的交叉模型做召回用户和物品特征必须在线交叉计算算力上完全撑不住。3.2 训练样本构造batch内负采样与困难样本双塔模型的效果七成取决于样本怎么构造。最常用的负采样策略是batch内负采样一个batch里有N个正样本对拿第i个用户的物品作为正例把batch里其他用户的物品当作它的负例。这样做的优点是零成本Es不需要额外采样而且batch内负样本天然就是“热门样本”因为热门物品被采中的概率更高。这其实是一个隐式的热度惩罚能有效避免模型把高分给到所有人都在看的头部内容。但纯batch内负采样有个问题负样本太简单了。模型很快就能学会区分“用户交互过的物品”和“随机物品”但对那些真正难分的样本比如相似品类、相似标题的商品区分能力会很差。解决思路是引入困难负样本从上一版模型的召回结果里挑一部分排在前面但用户没有交互的物品混进负样本一起训练。我在实际项目里一般会让困难负样本占比在10%到30%之间太小没效果太大会把模型训练得过于激进导致在线结果偏向一些莫名其妙的物品。3.3 向量检索近似最近邻搜索双塔模型训练完面临的第二个问题是物品向量可能有几百万甚至上千万个用户向量来了得在毫秒级时间内在这么大的集合里找到TopK最相似的向量。这个问题的准确解是暴力扫描但几百万个向量做全量内积时间完全不可接受。所以要用近似最近邻搜索ANN。工业界常用方案大致有三类基于哈希的LSH、基于量化的PQ、基于图的HNSW。LSH的思路是把高维空间划分成桶相似的向量更可能落在同一个桶里PQ的思路是把高维向量拆分成若干段每一段分别做聚类用聚类中心的ID来表示向量从而大幅压缩存储和计算量HNSW的思路是用多层的跳表结构组织图搜索时从高层粗粒度往下层精细找工程上用的最多。这些算法各有取舍HNSW召回精度高、检索快但内存占用大PQ内存占用小但精度会有损失。实际落地时通常会用faiss、Milvus这类现成的向量检索库而不是自己从零实现。我自己的经验是几百万规模用HNSW足够了千万级以上才需要考虑PQ或者分片。3.4 用户塔与物品塔的更新不同步怎么办双塔落地时最容易被忽视的一个工程问题就是塔的更新频率不一致。物品塔可以离线跑全量比如每小时或者每天更新一次向量但用户塔必须做到在线更新因为用户的行为是实时产生的等一天再更新用户已经看了好几轮新内容了。这种不同步会带来一个很实际的问题用户刚才点击了某件商品但物品向量最快也要下一个更新周期才会包含这件新品的信息用户的实时兴趣没法立刻反映到召回结果里。我见过的折中方案有这么几种。一是做“增量用户向量缓存”用户每次在线推理出向量后结合最近的行为特征做一次轻量更新比如把行为序列里的最新item用物品向量做一次加权平均和用户塔输出的向量混合。二是在服务端维护一个“最近交互物品池”把用户刚交互过的物品直接追加到召回结果里绕过向量检索。三是在模型层面给用户塔输入增加实时行为特征让模型本身就对新鲜行为更敏感。这三种方法不冲突可以叠加使用。4. 多路召回与融合策略4.1 线上常见的召回路热销、类别、向量、图、序列到现在为止行业里没有任何单一召回模型能包打天下。所以工业界的普遍做法是多路召回每路召回用不同的逻辑从不同角度捞候选最后合并去重再交给排序层处理。常见的召回路包括热销召回直接返回全网或当前品类下的热门物品。虽然听起来很“土”但在冷启动和新用户场景下非常管用能保证不会推荐出太离谱的内容。它的核心目的是保障地基而不是体现个性化。协同过滤召回基于ItemCF或者矩阵分解横向拓宽用户兴趣能捞到和用户历史交互物品相似的东西。向量召回基于双塔等模型做语义级泛化能处理协同过滤覆盖不到的长尾和冷门兴趣。序列召回直接建模用户最近的行为序列捕获短期兴趣漂移适合新闻、短视频这种实时性强的场景。图召回把用户和物品建模成二部图利用GraphSAGE等模型学习节点表示能利用多跳关系解决数据稀疏问题。多路召回的设计原则我个人的看法是每路召回都应该有它独特的价值路与路之间的重复率要低。如果两路召回返回的候选重叠率超过30%其中一路大概率是在浪费线上资源应该思考它是不是可以被合并或者替换。4.2 分数怎么对齐归一化与权重调节多路召回最直接的麻烦是各路召回的分数不可比。向量召回的相似度可能分布在0到1之间热销召回可能只有一个销量值协同过滤打出来的分是任意实数。如果只是简单地把各路结果接在一起后面排序层会乱掉。常规做法是在召回层做分数归一化把每一路召回的分数转换到同一个尺度然后再按权重做加权融合。常见的归一化方案是min-max归一化或者z-score归一化。前者把分数映射到0到1简单直观但容易受异常值影响后者利用均值和方差做标准化相对稳定。权重则一般在离线召回评估时通过网格搜索来确定优化目标可以是Hit Rate和覆盖率的一个加权组合。但这里我要说一个更重要的观点召回层的分数融合最终目的不是让候选集内的物品有一个从高到低的顺序而是保证那些“值得被精排认真看”的物品不会被截断丢掉。所以如果后续有粗排召回层甚至不必过分纠结精确的分数对齐只要保证各路候选都能以合理比例进入下一层就行。比例的控制比分数本身更重要。4.3 召回与粗排/精排的衔接很多团队只做召回和精排两层中间不设粗排。但在候选集数量上千甚至上万时直接让精排去算依然扛不住。粗排层的意义就是再做一次轻量筛选把候选从千级压到几百然后才进精排。召回输出给粗排的除了物品ID最好还能附带一些结构化信息比如来源路、路内分数、召回原因。这些信息对粗排非常有用。比如粗排可以根据来源路做加权也可以把召回分数作为粗排模型的一个特征输入帮粗排模型建立先验。这里有一个很常见的坑如果粗排和召回共用了一套样本和特征粗排很容易变成召回的“复读机”无法提供新的筛选能力。理想的衔接方式是让召回的输出成为粗排模型的特征之一而不是完全决定粗排结果。粗排需要有自己的特征视角哪怕特征少一点、模型轻一点也要有独立筛选的能力。5. 进阶召回思路序列召回、图召回与生成式召回5.1 序列召回捕捉兴趣漂移的关键双塔模型的用户塔通常是对全部历史行为做聚合相当于在表达“这个用户长期来看喜欢什么”。但用户的兴趣是会漂移的。昨天看的还都是家居用品今天突然刷了一晚上露营装备这个短期兴趣如果不能在召回层体现推荐就会慢半拍。序列召回就是为了解决这个问题。它的输入是用户最近一段时间的交互序列输出是用户下一步可能交互的物品。MINDMulti-Interest Network是这里面的代表模型它用动态路由机制把用户的历史行为序列聚合成多个兴趣向量每个向量代表用户某一方面的兴趣。SDM则是把长期和短期序列分开建模用多头注意力机制捕获两个时间段的不同信息再融合成最终的用户表示。实操中要注意序列召回的在线成本比双塔高。双塔可以预计算用户向量序列模型需要实时跑一段序列所以一般会把序列长度做限制比如只取最近50个交互物品或者做增量计算把长期序列的状态缓存起来只对最新行为做一次局部更新。否则序列召回就会成为线上时延的重灾区。5.2 图召回把行为关系变成图结构传统召回模型把用户和物品当成独立的样本看待忽略了彼此之间的多跳关联。比如用户A和用户B没有直接交集但他们分别和物品X、Y交互过而X和Y又经常一起出现那么A和B的兴趣就有潜在相似性。这种间接关系普通双塔模型很难学出来因为它的训练信号只来自“用户-物品”的二阶交互。图召回的做法是把用户、物品当成图中的节点把交互行为当成边然后用图神经网络学习每个节点的向量表示。GraphSAGE是这里最为人熟知的框架它通过采样邻居节点、聚合邻居特征、更新节点表示的方式让每个节点的向量蕴含周围节点的信息。图召回的落地难点是工程复杂度高。图每秒都在变全量重新训练不一定来得及采样和聚合的分布式实现也不简单。小规模场景下可以做成离线全量小时级更新大规模场景就需要考虑动态图采样和增量训练。我的建议是如果协同过滤和双塔已经能覆盖主要召回需求先把图召回放一放把资源和精力留给排序模型收益更大。5.3 生成式召回跳出“匹配-检索”的框架生成式召回TDM是另一种思路它不采用“先算向量再做ANN检索”的范式而是直接对物品集合建模出一棵层次树然后用深度模型在树上做逐层筛选最后选出候选集。TDM的核心思想是与其在巨大的物品池里做相似度检索不如把物品组织成树结构用模型逐层判断应该走哪个分支把搜索空间指数级缩小。TDM在实测中通常能做到比双塔更高的精度因为它可以用更复杂的模型结构不局限于“用户和物品分别编码后内积”。但它的训练和部署成本也比双塔高得多树结构需要定期重建模型的在线推理也需要逐层进行。目前来看TDM更适合对精度要求非常高、算力也非常充裕的场景一般团队用双塔加多路召回完全够用。6. 召回模型上线前必须做的评估6.1 离线评估指标与注意点很多人评估召回只看Hit Rate把Hit Rate从20%提到25%就开心得不行结果线上A/B没有收益最后灰溜溜回滚。问题出在Hit Rate这个指标太单一它只考察“用户交互过的物品有没有出现在候选里”并不考察候选的整体质量。离线评估应该至少看三个维度。第一是Hit Rate和Recall衡量候选是否覆盖用户真实兴趣。第二是候选多样性。可以看候选集中不同类别、不同来源路的占比如果某一类占比过高说明召回结果趋向单一。第三是候选集的“可排序性”或者说“头部集中度”。简单做法是统计候选集内item被历史交互过的次数分布。如果候选集主要集中在少数几个头部物品召回基本没有提供有效增益。另一个注意点是评估切分方式。建议采用“用户行为序列按时间切分”的方式用时间靠前的行为训练时间靠后的行为做评估。千万不要随机切分否则会出现时间穿越评估指标虚高。6.2 线上A/B的观察指标与时长线上A/B实验是召回模型最终效果的试金石。观察指标至少包含业务核心指标CTR、点击量、完播率、GMV做推荐这件事最终要对业务负责。漏斗转化指标曝光到点击的转化率、点击到深入交互的转化率。召回层改动最直接影响的就是这一步。每用户交互深度比如人均点击次数、人均浏览深度能反映召回候选的长期吸引力。生态健康指标新内容曝光占比、长尾内容曝光占比。召回模型如果只推头部短期内CTR可能好看但长期会损害内容生态。关于实验时长至少跑一周。原因是用户行为有周期性工作日和周末的推荐效果差别明显只跑三天容易得出错误结论。另外需要注意刚上线时模型冷启动尚未完全稳定前一两天的数据尽量不要作为最终判断依据。6.3 召回层覆盖率与新鲜度的监控召回模型上线后不能只看效果指标还要持续关注覆盖率和新内容进入候选集的比例。我在维护推荐系统时会专门做一个监控大盘每分钟更新几个关键量当前召回候选覆盖了多少个不同的物品新增内容进入召回的占比以及各召回路输出的候选数量是否稳定。这个监控的价值在于很多召回模型的风险其实是滞后的。比如用户塔在线更新逻辑如果出了问题可能过了两小时才会发现所有用户都在召回同一批旧内容那时候线上CTR已经跌了不少。如果有一个覆盖率监控就能在这种风险发生的时候快速定位而不是等业务报警了才开始排查。7. 实际项目中的高频问题与排查技巧7.1 离线指标涨了线上为什么不涨这是最让人头疼的问题。我排查过很多次基本可以从三个方向入手。第一是离线评估是否有偏差。前面提到的样本切分问题、样本泄露问题都会让离线指标虚高我把这叫做“你的评估在帮你作弊”。检查一下训练集和评估集有没有重叠用户、有没有用到未来信息。第二是线上和离线的特征一致性。双塔模型在训练时用了某几个特征但线上服务端漏传了其中一个或者线上特征值分布和离线差异很大模型推理结果自然对不上。这种问题在工程上非常常见解决思路是建立特征一致性对比平台定期抽样请求做离在线特征校验。第三是阈值和截断问题。离线评估时你可能只看了Top100的Hit Rate但线上实际只取Top50进精排超出Top50的部分根本不会产生曝光。可以换个说法如果你的模型在Top50已经达到了效果上限那提升的离线指标可能发生在Top100到Top200这段“无效区域”线上当然没变化。7.2 召回结果单一多样性差向量召回特别容易出现多样性差的问题。原因是用户长期行为的物品向量平均之后高频兴趣会被强化低频兴趣被淹没最后所有用户都被拉到同一个大众兴趣方向上个性化几乎消失。我的处理经验是在双塔模型里做“多兴趣输出”不要求用户塔只输出一个向量而是通过Attention机制输出K个兴趣向量每个向量负责用户某一个方面的兴趣。召回时分别用K个向量去检索再把结果合并。这样做能在保持精度的前提下显著提升多样性和覆盖率。如果暂时改不动模型也可以在后处理阶段做候选集去重和品类打散保证同一品类在召回结果中占比不超过一定阈值。另一个容易忽略的点是负采样策略。如果负样本全部来自batch内采样热门物品会被反复惩罚模型会更倾向于给长尾内容高分。这会让召回结果变得“太偏”反而破坏了和精排之间的配合。可以在负样本中加入一部分随机均匀采样的物品保持模型对热度的判断力。7.3 用户冷启动和物品冷启动冷启动是召回永远迈不过去的坎。用户冷启动阶段历史行为几乎没有双塔模型算出来的用户向量基本是随机瞎猜。这个阶段不能依赖模型要用规则补位。我的做法是新用户强制掺入热销召回和热门品类召回占候选的比例可以逐步随用户行为增多而降低。这本质上是把“探索”的代价放在召回层让排序层有更多素材可以调度。物品冷启动则需要从信息上做文章。新物品没有交互数据向量是所有同批次物品的平均区分度很低。常见办法是给新物品打上更强的属性特征比如文本、标签、类目、封面图的预训练embedding让它在没有交互的情况下也能在向量空间里有一个合理的位置。同时物品上线后要有“探测机制”主动把新物品小流量推荐给合适的用户快速积累反馈数据让模型对它形成真实表征。还有一个细节新物品的向量更新要及时。如果一个新物品上线了但物品向量要等第二天才更新等于它在召回里白白浪费了24小时。建议新物品单独走一路“新品召回”不依赖向量索引直接用内容特征匹配用户群。做召回这几年我最大的体会是召回模型的迭代真正难的地方不在模型结构而在样本构造和工程链路。双塔模型的结构人人都会画但负样本怎么采、困难样本怎么加、用户向量怎么更新、冷启动怎么兜底这些细节才是决定线上效果的关键。希望这篇总结能帮你把召回这条线理清楚哪怕只是避开我踩过的一个坑也算值了。
返回列表