推荐系统双塔模型:从召回与精排的本质差异到样本构造的艺术
1. 从“召回”与“精排”的本质差异说起如果你在推荐系统领域待过一段时间肯定听过“召回”和“精排”这两个词。很多刚入行的朋友甚至一些有经验的工程师都容易把它们的目标搞混。最常见的误解是召回不就是把一堆可能相关的物品找出来吗精排不就是给这些物品打分排序吗这个理解对但不够本质也容易导致后续模型设计和样本构造上的偏差。今天我想从一个更根本的视角来聊聊这件事这也是我踩过不少坑才想明白的召回的目标是区分用户“感兴趣”和“不感兴趣”的东西而精排的目标是区分用户“感兴趣”和“非常感兴趣”的东西。这句话听起来简单但它直接决定了双塔模型乃至整个召回层样本构造的哲学。你想想看如果召回阶段就试图去分辨“有点兴趣”和“特别有兴趣”的细微差别那无异于让一个刚学会认字的人去品鉴诗歌的平仄韵律不仅效率低下而且很容易出错把真正的好诗用户可能感兴趣但没那么热门的物品给漏掉。为什么会有这种差异根源在于它们所处的“战场”规模不同。召回面对的是百万、千万甚至上亿的候选物品池它的核心任务是“大海捞针”用相对粗糙但高效的筛子快速过滤掉绝大部分明显无关的“海水”留下那么几千、几百个可能的“针”。这个筛子的网眼不能太细否则计算量爆炸也不能太粗否则漏掉的针太多。而精排则是在召回留下的这几百个“准针”里用更精细的放大镜和天平去判断哪一根针最亮、最尖、最适合用户此刻的需求。所以召回是解决“有和无”的问题精排是解决“好和更好”的问题。理解了这层差异我们再来看双塔模型。双塔之所以成为召回阶段的“标配”正是因为它完美契合了“快速区分感兴趣与不感兴趣”这个目标。它的结构——用户特征一个塔物品特征一个塔最后计算向量内积作为匹配分——本质上是在学习一个巨大的“兴趣过滤器”。这个过滤器不需要知道用户对《权力的游戏》和《指环王》哪个更喜欢5%它只需要知道用户对“奇幻剧”感兴趣而对“乡村爱情故事”不感兴趣。这就是双塔在召回阶段的使命也是我们构造样本时必须时刻牢记的黄金准则。2. 正样本构造锚定“感兴趣”的模糊边界双塔模型训练的第一步也是最重要的一步就是告诉模型什么叫“用户感兴趣”。正样本就是用来定义这个概念的。但“感兴趣”本身是个非常模糊的概念它不像图像分类里的“猫”和“狗”那样界限分明。在推荐场景下一个用户点击了一篇文章是感兴趣观看了视频超过一半时长是感兴趣甚至只是在一个商品详情页停留了较长时间也可能是一种潜在兴趣的信号。如何选择正样本直接决定了模型学习到的“兴趣”边界在哪里。最常见的正样本来源就是用户的显式反馈例如点击、播放、购买、收藏、分享。这些行为明确表达了用户的意图是高质量的正样本。但这里有一个关键细节是否所有点击都同等重要在实际业务中我们往往需要区分点击的“含金量”。例如在信息流场景中用户可能因为标题党而点击但瞬间就退出了这种点击作为正样本的权重就应该降低。反之用户点击后进行了深度阅读、互动评论甚至二次传播这样的正样本信号就强得多。因此一个常见的实践是为正样本设计权重比如weight f(停留时长 互动行为)将权重参与到损失函数如加权交叉熵的计算中让模型更关注那些真正表达了兴趣的样本。除了显式反馈隐式反馈和序列信息也是构造正样本的富矿。用户的搜索词、浏览路径、甚至在一个类目下的反复筛选行为都暗示了其兴趣。我们可以通过滑动窗口将用户历史行为序列中的前序物品作为正样本来预测下一个物品这其实就是引入了时序信息让模型学习兴趣的演进。例如用户连续看了三部科幻电影那么第四部科幻电影作为正样本的概率就很大即使用户没有点击它但在序列建模的视角下它依然是用户兴趣流中的一个合理延续。注意使用序列信息时要警惕“信息泄露”。不能把未来发生的行为数据当成当前预测的上下文特征必须严格按照时间戳划分训练和验证集确保模型的在线服务效果与离线评估一致。更高级的正样本构造还会考虑全局信息。比如利用图神经网络将用户-物品交互行为构建成异构图通过随机游走如Node2Vec或消息传递如LightGCN来生成物品和用户的向量。这些向量本身蕴含了协同过滤信息可以作为“增强正样本”。例如与用户历史点击物品在向量空间非常接近的其他物品即使该用户没有直接交互过也可以作为辅助正样本或称为困难正样本用于帮助模型学习更鲁棒、更泛化的兴趣表示。在实际操作中我通常会采用“混合正样本”策略。以视频推荐为例一个训练样本的构造可能是这样的核心正样本用户U点击并完整观看了视频V。辅助正样本1用户U在会话中点击V之前观看过的视频序列正样本。辅助正样本2通过Item-CF或向量检索得到的与V最相似的Top-K个视频相似正样本需控制比例如10%。这样构造的正样本集合既包含了强烈的即时兴趣信号也包含了兴趣的上下文和泛化信息能让双塔模型学习到的用户塔向量更具包容性为后续的粗筛做好铺垫。3. 负样本构造艺术与科学的结合定义“不感兴趣”如果说正样本定义了“感兴趣”那么负样本就是在定义“什么是不感兴趣”。负样本的选择是双塔模型训练中最具挑战性也是最见功力的部分。选得太简单例如随机从全库采样模型学不到东西轻易就能区分上线后对困难样本的区分度很差选得太难例如只选精排打分高但用户没点的样本模型可能无法收敛或者把一些用户潜在感兴趣的物品也判定为负导致召回遗漏。负样本构造的核心思想是模拟召回阶段模型需要面对的真实决策环境。在线上服务时双塔模型需要从海量物品中快速找出用户可能感兴趣的。因此训练时的负样本应该尽可能覆盖这些“容易被误召回”的物品。下面介绍几种主流的负采样策略及其应用场景。3.1 全局随机负采样基础的校准器这是最简单的方法从全量物品池中随机抽取一定数量例如1000个的物品作为负样本。它的作用是提供一个“背景噪声”分布让模型知道世界上绝大多数物品用户都是不感兴趣的。但它的问题也很明显对于热门用户或热门物品随机采到的负样本很可能与用户毫无关联过于简单导致模型对“困难负样本”的区分能力不足。因此全局随机负采样通常只作为负样本的一部分例如占30%用于保证模型训练的稳定性和基础性能。3.2 批次内负采样高效且自带困难样本这是当前最主流、最高效的负采样方法。在一个训练批次Batch内对于给定的一个正样本对(用户U, 物品I)批次内其他用户的正样本物品I-自然可以作为用户U的负样本。因为从概率上讲其他用户喜欢的物品当前用户U喜欢的可能性较低。这种方法有两大优势效率高无需额外的采样开销GPU在计算批次内所有用户-物品对相似度时天然就得到了大量的负样本。自带困难负样本批次内的物品通常是经过热度筛选或上一轮模型召回过的它们本身可能就是“有点相关但又不完全相关”的物品属于“困难负样本”能有效提升模型的判别能力。其损失函数通常采用多分类softmax的变体例如Sampled Softmax或更常用的Batch Softmax (即InfoNCE loss)。对于一个正样本对(u, i)损失函数鼓励u和i的相似度远高于u与批次内所有其他物品i-的相似度。提示使用批次内负采样时Batch Size的大小至关重要。更大的Batch Size意味着每个正样本能获得更多的负样本通常能带来更好的效果但受限于GPU显存。可以采用梯度累积等技术来模拟大Batch训练。3.3 基于曝光的负采样还原真实场景用户没有点击一个物品有两种可能1. 根本没看到未曝光2. 看到了但没兴趣曝光未点击。显然第二种情况才是模型真正需要学习的“不感兴趣”。因此从用户历史曝光但未点击的记录中采样负样本是最符合业务直觉的方法。这种方法能有效解决“曝光偏差”问题。例如一个质量很高的冷门视频因为初始流量少从未曝光给某个潜在感兴趣的用户那么它就不应该作为该用户的负样本。否则模型会错误地将“未被曝光”与“不感兴趣”划等号进一步加剧马太效应埋没优质长尾内容。实际操作中我们可以为每个用户维护一个曝光未点击的物品池从中进行负采样。为了增加难度还可以对这个池子里的物品按照热门程度或上一版模型的预测分进行排序优先采样那些“看起来应该被点击但实际上没有”的物品作为负样本。3.4 对抗性负采样与混合负采样当模型训练到一定程度后简单的负样本对它来说已经太容易了。这时需要主动制造“困难”即寻找那些当前模型最容易判错预测分高但实际是负样本的物品作为负样本。这被称为“对抗性负采样”或“Hard Negative Mining”。实现方式通常是在训练过程中动态进行定期如每N个step用当前模型对候选池进行快速推断为每个正样本用户生成一批得分较高的物品。过滤掉其中用户实际有过正反馈的物品剩下的就是“困难负样本”。将这些困难负样本加入训练数据中。这种方法能迫使模型不断优化其决策边界但对训练流程和资源有一定要求。更实用的策略是混合负采样对于一个训练样本我们同时使用多种负样本。70% 来自批次内负采样提供大量困难样本。20% 来自曝光未点击负采样保证真实性。10% 来自全局随机负采样维持全局视角防止模型过于激进。通过调整这个混合比例我们可以像调音师一样校准模型在“召回率”和“精确率”之间的平衡。4. 双塔模型训练中的样本陷阱与调优经验理解了正负样本的选择逻辑不代表就能训练出一个好的双塔模型。在实际操作中有几个陷阱需要特别注意这些也是我趟过不少雷区总结出的经验。4.1 样本偏差与冷启动问题样本永远是基于已发生的行为数据这天然存在偏差。热门物品曝光多产生的正负样本都多冷门物品曝光少样本稀疏。模型会倾向于给热门物品打高分因为它在训练中见得多。这就是“流行度偏差”。缓解方法除了上文提到的曝光负采样还有纠偏加权在损失函数中为每个样本(u, i)赋予一个权重权重与物品i的热度成反比。例如weight_i 1 / log(1 popularity_i)。这样可以让模型不过分偏向热门物品。因果推断方法引入IPSInverse Propensity Scoring等技术从理论上纠偏但实现较为复杂。专门处理冷启动对于新物品可以更多地依赖其内容特征文本、图像等在双塔的物品塔中确保内容特征通道有足够的表达能力。对于新用户则依赖其实时行为序列和人口属性特征。4.2 在线与离线的一致性鸿沟我们精心构造了训练样本但线上服务环境是完全不同的。最大的差异在于服务时的候选集。训练时负样本是我们采样得到的但线上召回时模型需要从全量候选集中计算Top-K。如果训练负样本的分布与线上全量物品的分布差异巨大就会导致模型在线效果大幅下降。一个经典的解决方案是使用“采样校正”。以流行的Sampled Softmax为例我们需要在计算损失时对每个负样本物品引入一个修正项log(q(j))其中q(j)是该物品被采样为负样本的概率。这样可以将“从采样分布中学习”校正为“从真实全局分布中学习”。在TensorFlow或PyTorch的相关实现中这个校正项通常需要自己显式地加入损失计算。4.3 负样本中的“潜在正样本”污染这是一个非常隐蔽但危害巨大的问题。当我们从曝光未点击中采样负样本或者做批次内负采样时很可能采到了用户未来会感兴趣或者实际上感兴趣但当时因各种原因如标题不好、封面图不吸引人而未点击的物品。把这些“潜在正样本”当作负样本训练会直接损害模型的召回能力。应对策略时间窗口过滤只使用足够久远的历史数据如30天前中的曝光未点击作为负样本。假设用户如果在30天内都没有对某个曝光物品产生后续行为那么它作为“潜在正样本”的可能性就极低了。置信度筛选利用更复杂的模型如一个轻量级精排模型对曝光未点击样本进行打分只将那些得分很低的样本作为高置信度负样本。多目标学习不简单地将样本标记为正或负而是引入多任务。例如主任务预测点击辅助任务预测停留时长或互动。对于一个曝光未点击但停留时长很长的样本它在点击任务中是负样本但在停留时长任务中可能是正样本。通过多任务共享底层特征模型能学到更精细的表示。4.4 特征穿越与数据泄漏在构造样本时必须严格遵守时间序原则。即用于预测用户未来兴趣的模型其训练样本中的特征绝不能包含“未来”的信息。常见的错误包括使用了用户在未来才发生的行为特征。使用了物品在未来才统计到的全局热度特征。在构造序列特征时包含了预测点之后的行为。这会导致离线评估指标虚高但线上效果一塌糊涂。解决方法是在特征工程和样本生成流水线中建立严格的时间点快照机制。对于每一个训练样本都模拟线上推理那一刻的特征状态所有特征值都只能截取到该时间点之前的数据。5. 超越基础双塔样本选择如何影响模型进阶当我们把基础的样本构造玩明白之后就可以尝试一些更高级的双塔变体而这些变体的成功很大程度上依赖于更精巧的样本设计。5.1 引入“困难负样本”的Margin Loss标准的双塔通常使用点积内积后接Softmax交叉熵损失。这种损失函数倾向于将正样本对的分数推高负样本对的分数推低。但对于召回任务我们有时并不需要无限拉大正负样本的分数差距而是希望正样本分数比负样本高出一个安全的边界Margin即可把更多的模型容量留给学习更精细的特征表示。这时可以尝试Triplet Loss或Circle Loss。以Triplet Loss为例我们需要构造三元组(用户锚点U, 正样本I, 负样本I-)。损失函数要求正样本对(U, I)的相似度要比负样本对(U, I-)的相似度至少高出一个边界值marginL max(0, sim(U, I-) - sim(U, I) margin)这里的核心技巧就在于如何选择这个负样本I-。我们需要选择那些与用户U相似但又不如正样本I相似的“困难负样本”。通常的做法是在训练过程中动态挖掘对于一个锚点用户U用当前模型为其寻找一批相似物品然后选择其中得分最高、但又不是真正正样本的物品作为I-。这种基于难样本挖掘的训练方式能显著提升模型在向量空间中的判别力。5.2 多兴趣召回与样本聚类一个用户可能有多个兴趣点例如既喜欢篮球也喜欢美妆。传统的单向量双塔模型用一个向量来概括用户所有兴趣可能导致兴趣混杂召回结果不聚焦。MIND (Multi-Interest Network with Dynamic routing)等模型尝试为用户生成多个兴趣向量。在训练这类模型时样本构造也需要相应调整。我们不再简单地为用户分配一个正样本物品而是需要考虑这个正样本物品应该由用户的哪个兴趣向量来负责召回这通常通过动态路由或聚类算法来实现。在训练时模型会学习将用户的历史行为物品聚类每个类簇对应一个兴趣向量。正样本物品会被分配到最相关的兴趣向量下去计算损失。此时负样本的选择也可以更有针对性对于一个由“篮球”兴趣向量主导的正样本负样本应该更多地来自“美妆”或其他不相关领域而不是随机采样。这种“兴趣感知”的负采样能帮助每个兴趣向量学得更纯粹、更专注。5.3 融合实时反馈的样本流在新闻、短视频等强时效性场景用户的兴趣变化极快。传统的双塔模型基于天级别或小时级别的样本更新存在滞后。解决方案是构建实时样本流与实时特征平台对接。具体来说用户的每次实时交互点击、滑动、停留都立刻生成一条训练样本注入到Kafka等消息队列中。流式训练框架如Flink消费这些样本对双塔模型进行在线学习或微调。这里的样本构造关键在于实时负样本的获取。由于无法在毫秒级进行全库采样通常采用两种策略曝光未点击即负样本在流式场景下这是最直接、最真实的负样本来源。从实时召回集中采样从本次请求中实时召回但未曝光的物品列表中随机采样作为负样本。这模拟了线上服务时模型需要从一个小规模候选集中做选择的过程。通过实时样本流模型能够快速捕捉热点事件和用户兴趣的瞬时转移让召回结果更具时效性。这要求整个样本生成、特征拼接、模型更新链路具备极高的稳定性和低延迟。说到底双塔模型是一个框架它的强大与否一半在于网络结构的设计另一半则完全依赖于我们喂给它的“粮食”——也就是训练样本。样本定义了模型要学习的世界观。召回阶段“区分感兴趣与不感兴趣”的世界观要求我们的样本必须覆盖广、有难度、贴近真实分布。每一次对采样策略的调整每一次对样本偏差的纠正都是在重塑模型对用户兴趣的理解。这个过程没有一劳永逸的银弹只有结合业务数据特点不断实验、分析和迭代才能让这个高效的“兴趣过滤器”发挥出最大的威力。