1. 双塔召回模型到底在解决什么问题先聊一个场景。你在刷短视频App系统从几十亿的候选视频里只用几十毫秒就给你挑出了十几个你可能感兴趣的。这个“从海量候选里快速筛出少量靠谱候选”的过程就是召回。召回不是排序它不需要精准到把最好的排第一它只需要保证“好东西别被漏掉”把候选从亿级压到千级甚至百级交给后面的精排模型去细抠。说到召回早期业界主流是ItemCF、UserCF这类协同过滤或者FM、DeepFM直接拿全量特征打分类似的分数。但这些方案在超大规模候选集下有一个绕不开的痛点推理时要把用户和所有物品做交叉计算复杂度随物品量线性增长线上根本扛不住。于是“双塔召回模型Two-Tower Retrieval Model”成了工业界最主流的解决方案——把用户侧特征和物品侧特征分别编码成两个独立的向量线上只用算一次用户向量然后去物品向量库里做近邻检索复杂度和物品总量解耦只在检索阶段承担ANN的开销。我最早接触双塔是几年前在做推荐系统召回优化的时候当时团队被线上延迟和召回覆盖率两头折磨换了双塔之后效果立竿见影。但这玩意儿上手容易做好很难坑也很多。这篇就系统地把双塔召回的来龙去脉、架构细节、训练技巧、线上部署和踩坑经验一次讲清楚给正准备上手做召回的同学一份能直接抄作业的参考。2. 双塔架构的核心设计与选型思路2.1 从“交叉特征”到“向量内积”的思路转变传统排序模型的核心是特征交叉比如用户性别和物品类目的组合模型能学到“男性用户点击游戏类物品的概率更高”这种细粒度规律。但召回阶段面对的候选集是亿级的排序模型那套复杂交叉根本跑不动。双塔的思路是反过来的用户侧特征和物品侧特征各自独立过网络分别得到user embedding和item embedding最后用内积或者余弦相似度作为匹配分。为什么要用内积而不是其他交互方式核心原因是内积可以拆解成“先算用户向量再和物品向量做内积”而物品向量可以提前算好存进向量库。如果换成MLP拼接、bilinear交互这类方式用户和物品的特征就必须实时交叉没法离线预计算物品向量线上就退化成全量计算了。所以双塔的结构看似是“两个塔”本质上是“为了支持向量化检索而做出的结构妥协”。这里有个容易误解的点双塔不是“不用交叉特征”而是把交叉能力压缩到了向量内积这么一步。内积本身就是一种最简单的交叉。那用户侧和物品侧各自的塔内可以做特征内部的交叉比如FM式的特征嵌入加和也可以做特征之间的注意力交互只要不跨塔就行。塔内结构可以很复杂塔间结构必须简单这是双塔设计的第一性原则。2.2 两个塔各自该喂什么特征用户塔输入的是用户侧特征常见的有用户ID、年龄、性别、城市、近期点击的ItemID序列、行为统计特征近7天点击品类分布等。物品塔输入的是物品侧特征常见的有ItemID、类目、标签、标题分词、发布时间、作者ID、物品统计特征近3天曝光量、点击率等。特征处理上要注意几点用户ID和ItemID这类高基数类别特征需要做embeddingembedding维度一般取32到128之间基数特别大的可以适当调高但不能无脑高维度太高训练参数爆炸且容易过拟合。多值特征比如用户近7天点击的ItemID序列需要对每个ID取embedding再池化池化方式首选加权平均权重可以用行为时间衰减系数越近的点击权重越高也可以直接用注意力机制学权重。连续特征要做归一化或者分箱直接扔原始值进网络数值差异太大会让梯度不稳定。时间类特征比如物品发布时间距今天数建议做log变换效果比线性归一化好很多。我还见过一种做法是把“用户最近点击的物品的类目分布”做成一个变长稀疏特征喂给用户塔。这类“统计类交叉特征”虽然没有直接实现用户和物品的特征交叉但通过行为序列把用户侧和物品侧的语义信息在塔内打通了实际效果提升非常明显。如果你做双塔召回总觉得塔内表达能力不够可以优先考虑在特征层面加强这类跨侧信息的注入。2.3 为什么双塔是召回任务的工业标准答案相比其他召回方案双塔的工业优势非常清晰训练和推理解耦用户塔和物品塔可以分别训练、分别上线用户侧模型更新不影响已构建好的物品向量索引。支持大规模检索物品向量可以离线计算并建ANN索引如HNSW、IVF线上查询时用近似最近邻检索代替全量打分QPS能做到很高。扩展性好新增物品只影响物品塔和索引不需要重训用户侧新增用户同理。冷启动能力物品的特征embedding可以覆盖未登录ID对长尾物品的召回明显优于纯协同过滤。当然双塔也有众所周知的缺陷塔间交互太弱难以建模用户和物品的细粒度交叉。所以业界通常的做法是“双塔召回负责广度精排负责精度”双塔的目标不是做到完美排序而是做到“不漏掉好东西”。如果想在双塔基础上增强交叉能力可以用senet双塔对特征做动态权重、多兴趣双塔用户塔产出多个embedding、或者双塔图模型的混合方案。3. 数据构造与负样本工程3.1 正样本点击还是完播还是下单正样本的定义直接决定了模型学到的“匹配关系”是什么。如果做信息流推荐正样本可以是点击、有效播放、完播、点赞、收藏等。不同业务对正样本的偏好不同我见过的最常用组合是“点击完播/阅读时长加权”。比如某视频App的正样本定义用户点击且播放时长超过5秒或者播放时长超过视频总时长的60%。这种定义兼顾了点击率和内容质量避免模型只学到标题党。正样本采样的时候还有几个细节一个用户一天内对同一个物品多次点击只保留一次不然高频重复行为会主导梯度。正样本数量要做上限截断比如每个用户每天最多取200个正样本不然头部用户的正样本把模型带偏。负反馈点了不感兴趣、刷走要不要当成正样本我建议单独建模不要混入正样本。用户的“不喜欢”是另一种语义混在一起会让向量空间互相拉扯。3.2 负样本随机负样本和难负样本的平衡负样本是双塔召回里最值得花时间的部分。经典的负样本策略是随机负采样从全量物品里随机抽一批当作“用户不感兴趣”的样本。随机负样本的问题在于太简单模型很快就学会区分“用户点过的”和“随机物品”学到的向量空间区分度很高但泛化性差。后来业界普遍采用“随机负样本难负样本”的混合策略。难负样本指的是那些“看起来可能感兴趣、但用户实际没有交互”的物品。怎么构造难负样本常用方法全局热门榜采样把高热物品随机当作负样本模型被迫学会区分“用户是否真的对这个热门感兴趣”而不是套热门流量。同类目采样随机选一个与正样本同二级类目的物品作为负样本模型要学到类目内部的精细匹配信号。Batch内负采样当前batch内其他用户的正样本对于当前用户来说就是天然的负样本。这个策略成本极低而且能近似利用全量的物品分布是双塔训练最核心的负样本来源。负样本比例上随机负样本Batch内负样本类目负样本 111是我用下来比较稳的组合。如果总负样本量是正样本的3到5倍效果通常不错。注意别把负样本加太多否则训练耗时和显存压力都会变大收益却不明显。3.3 Batch内负采样与温度系数的纠缠Batch内负采样是双塔训练里最常用的技巧其实就是把batch内其他用户的正样本物品当成当前用户的负样本。这个做法的好处是负样本随模型训练动态更新越到后期负样本越“难”模型训练质量越高。但缺陷也很明显热门物品出现在其他用户正样本里的概率高所以热门物品被当成负样本的次数也更多会产生“热门打压”偏差。解决方案有两个方向。一个是在损失函数里对负样本做曝光量校正比如用物品的曝光频次对logits做减项修正这个在Google的《Sampling-Bias-Corrected Neural Modeling for Large Corpus Item Recommendations》里有详细的推导。另一个是调整温度系数温度系数τ越小softmax分布越尖锐模型越聚焦于“最难区分的负样本”τ越大分布越平滑负样本之间的差异被弱化。实践中温度系数取0.05到0.2之间效果最好我常用的起点是0.1然后根据负样本难度做微调。还需要注意Batch size对负采样的影响batch越大负样本池越大模型见到的物品分布越接近全量效果越好。但batch太大显存扛不住所以业界普遍用64到512的batch size配Async SGD并行训练在保证负采样充分的同时控制显存。如果算力允许尽量把batch size往大了调能明显改善长尾物品的召回效果。4. 模型结构设计与特征工程细节4.1 塔内结构Embedding层、池化层与多层MLP双塔的塔内结构一般包含这几个部分稀疏特征Embedding层、多值特征池化层、全连接层。Embedding层把高基数类别特征映射为稠密向量多值特征通过池化把变长序列转成定长向量然后拼接所有特征向量输入几层MLP得到最终的塔输出向量。MLP的层数和维度设置我的经验是用户塔可以做到3层每层256或512维因为用户行为复杂物品塔2层到3层每层128或256维因为物品特征相对简单。塔输出维度一般取32到128然后做L2归一化。输出维度太小表达力不够太大则ANN检索的精度和性能都会受影响。这里还要重点提一下“特征拼接顺序”对训练的影响。实践中发现把ID类特征放在拼接的前段、把统计类特征放在后段收敛速度会更快。原因可能是ID类特征本身就承载了主要的语义信息网络早期更容易学到ID之间的共现关系。虽然这个现象没有特别强的理论解释但我在多个数据集上验证过效果差异是存在的。4.2 归一化、Embedding初始化与数值稳定性双塔训练里数值稳定性是个容易被忽略的大坑。两个塔的输出做内积如果向量维度是128每个维度取值在0到1之间内积的结果会随维度累积变大。这时候如果不做任何处理softmax里的logits会非常大梯度消失或者梯度爆炸都有可能发生。所以两个塔的最终输出一般都要做L2归一化把向量长度固定为1这样内积就是余弦相似度取值范围被限制在[-1,1]数值上稳定很多。Embedding初始化同样关键。我的建议是采用均值为0、标准差为0.01到0.05的正态分布初始化太小会让前向信号微弱太大会让训练早期梯度不稳定。另外一个技巧是给ID类特征的Embedding加一个小的L2正则权重设置在1e-5到1e-4之间能有效缓解高基数ID的过拟合。4.3 多兴趣与多向量方案经典的匹配模型是“用户一个向量物品一个向量内积打分”。但用户的兴趣是多样的一个用户既喜欢科技新闻又喜欢美食视频用一个用户向量去和所有物品向量做内积会把这些兴趣全部揉在一起导致最终检索结果互相稀释。MINDMulti-Interest Network with Dynamic Routing就是解决这个问题的代表性工作——它让用户塔产出多个兴趣向量每个兴趣向量分别去物品库检索最后合并候选集。多向量方案在线上检索的时候用户侧需要产出K个向量每个向量都去做一次ANN检索然后再做结果合并去重。K的经验值是4到8太大则检索开销成倍增加。多兴趣模型对用户行为序列建模的要求更高需要类似动态路由或者注意力聚类的机制实现成本比单向量方案大不少但召回效果的提升通常值得这个成本。我个人的建议是如果当前单向量双塔的召回效果已经不错不要为了追新而强行上多向量。先把单向量的特征工程和负样本做到位如果长尾兴趣的召回仍有明显缺失再考虑升级多兴趣方案。工程上小步快跑比大步重构稳妥得多。5. 损失函数与训练细节5.1 Softmax损失、NCE与Sampled Softmax双塔召回最标准的损失函数是“batch内softmax”形式[ L -\log \frac{\exp(s(u, i^) / \tau)}{\exp(s(u, i^) / \tau) \sum_{j \in N} \exp(s(u, i^-_j) / \tau)} ]其中 (s(u,i)) 是用户向量和物品向量的内积或余弦相似度(\tau) 是温度系数。这个损失的本质是让正样本的物品在所有候选物品中打分最高。分母里的负样本来自batch内其他用户的正样本相当于用近似的方式估计了全量物品的softmax分母这叫sampled softmax。如果物品量特别大比如超过千万sampled softmax的负样本数量相对全量来说依然很少估计的分母会有偏差。这时候可以用NCENoise Contrastive Estimation或者其变体来校正。但NCE需要知道物品的噪声分布计算上更复杂工程上除非特别需要比如YouTube推荐系统这种超大物品量场景用sampled softmax加温度系数就够了。5.2 双塔专属温度系数与内积尺度温度系数这个超参数很多人忽略它的重要性但它在双塔训练里的作用比想象中大得多。温度系数控制着softmax分布的“锐度”温度低模型会非常激进地去压制最难负样本的得分温度高模型对负样本的压制会比较温和更看重整体区分度。我的调参经验当负样本质量参差不齐、难负样本比例低时温度系数可以稍微调低比如0.05帮助模型在难负样本上多花功夫当负样本整体质量较高时温度系数调高比如0.15到0.2防止模型过拟合到个别难样本上。温度系数不是一个“调一次就不管”的参数它和负样本策略是耦合的改负样本策略时必须回来重新调。5.3 双塔的常见训练trickEMA、梯度截断与异步训练双塔训练的在线学习场景很常见因为用户行为数据是持续产生的。训练方式上我有几个亲测有效的建议EMA指数滑动平均参数更新对模型参数做滑动平均能在不显著增加训练时间的情况下让模型更稳定AUC一般能涨0.2到0.5个点。梯度截断双塔的梯度方差偏大给梯度设置一个clip比如最大范数5.0可以避免偶发的异常样本把模型权重冲飞。异步并行训练双塔的两个塔结构相对独立可以用异步SGD并行训练加快处理大规模用户行为数据的速度。但异步训练需要控制staleness否则模型收敛不稳定。还有一个经验是训练轮次不宜过多。双塔学的是“粗粒度匹配”不是精排序训练到验证集指标不再上升后继续训练只会让模型对训练数据过拟合线上效果反而可能下降。建议在训练过程中保存多个checkpoint用线上召回率指标做选择而不是只看离线指标。6. 线上推理与向量检索架构6.1 从模型输出到向量索引的构建流程模型训练完线上并不是直接跑模型而是物品侧离线推理得到所有物品的embedding灌入向量检索库如Faiss、Milvus、Elasticsearch的kNN插件构建ANN索引。用户侧则实时推理出用户向量也可以用定时任务预计算来降低延迟然后到向量库里做TopK检索。建索引注意几个细节索引类型选择亿级物品量用IVFPQ或HNSW千万级用HNSW就够。HNSW的参数里M每个节点的最大连接数取值16到64efConstruction取值200到800检索时efSearch取值100到500。M越大索引质量和召回率越高但内存和构建时间也越大。向量维度与内存128维的float向量1000万条占内存约5GB如果要上亿务必用PQ压缩或者降维。索引更新频率物品向量更新不需要全量重建可以增量追加但删除和修改需要定期做全量合并。经验做法是每天一次全量重建加实时增量。6.2 召回结果的后处理与多样性控制向量检索返回的TopK物品不能直接当成最终召回结果。原因有二一是向量空间里相似度高的物品容易扎堆比如返回的全是同一个作者或者同一个细分品类的物品二是热门物品的向量在空间中往往处于中央位置容易被大量用户召回导致马太效应。我的后处理实践经验按作者/来源做去重或限流比如每个作者最多保留2个物品。按类目做多样性约束可以设定一个配额确保召回结果覆盖3到4个用户感兴趣的大类。加入轻量的规则兜底比如TopK中必须保留至少1条“今日热门”保证新用户或行为稀疏用户不会拿到太冷的候选。对召回结果进行一次轻量过滤用简单的逻辑规则排除明显不合适的物品比如用户已拉黑作者的内容。这些后处理逻辑虽然简单但对线上真实体验的影响往往比模型本身还大千万别省。6.3 全链路一致性训练和推理的特征对齐做过双塔的人都知道线上推理最大的坑不是模型质量而是特征不一致。训练时用的是“用户点击时”的历史特征线上推理时用的是“当前时刻”的特征两者如果分布差太多模型效果会明显衰减。典型问题包括训练时用了未来特征比如物品当天的总点击量上线后这些特征在推理时刻还不存在属于数据泄漏。用户行为的聚合窗口比如“近7天点击”线上取的时间点和训练时不完全一致导致特征分布偏移。物品侧特征更新滞后比如物品类目改了但向量索引没有及时更新召回结果就会和线上物品属性不一致。我的做法是在训练脚本里对特征做严格的“时间截断”测试模拟推理时刻的特征分布重新评估模型AUC和召回率如果与在线指标差异过大优先排查特征的可用性和时效性。这个习惯能帮你省去大量线上排查时间。7. 评估体系离线指标与线上AB实验7.1 离线评估召回率、HitRate与长尾覆盖率双塔召回模型的离线评估不能直接用精排那套AUC或者GAUC因为召回阶段关注的是“候选集是否包含用户真实感兴趣的物品”。常用指标HitRateK用户真实交互的物品是否出现在召回TopK里。反映了召回的基本准确度。RecallK检索出来的相关物品占用户所有相关物品的比例反映覆盖率。长尾覆盖率召回结果中长尾物品曝光量低、交互少的占比。这个指标往往被忽视但它是判断双塔是否“学废了”的关键——如果模型只召回热门物品说明向量空间已经被热度主导模型本身存在严重偏差。我自己还有一个习惯把召回结果按类目分布和用户行为序列的类目分布做对比看两者是否接近。如果模型召回的教育类内容占比明显低于用户历史行为中的教育类占比说明这一块的召回能力偏弱可以针对性地增强该类目的特征表达能力或者负样本构造。7.2 线上指标时长、点击率与留存双塔召回上线后的核心指标按我的优先级排序是人均时长/人均阅读量反映用户整体满足度 点击率反映单次匹配精度 互动率点赞评论 留存。召回层的改动最先体现在人均时长上其次才是点击率。如果你上线一个双塔模型点击率涨了但时长没涨要警惕是不是只是把用户吸引到了低质量内容上。线上AB实验的注意事项实验分组前先确认流量是否均匀避免辛普森悖论的坑。观测周期至少一周覆盖工作日和周末召回类的改动在周末的数据表现往往不同。用逐步放量的方式先5%流量观测稳定性再逐步扩到20%、50%。如果早期出现指标异常及时回滚比继续观察更重要。7.3 数据漂移监控与模型迭代双塔模型上线后不能放着不管。用户兴趣在变化物品库在更新时间长了模型效果必然衰减。我建议建立一套监控体系关键指标用户向量空间分布的变化度定期跑一批用户向量计算分布距离比如Wasserstein距离如果变化过大说明用户兴趣漂移了需要重训。物品向量的更新覆盖率确保新物品在合理时间内得到向量并被索引。召回HitRate的趋势监控这个指标掉了优先看数据质量和特征时效性。一个实用的迭代频率参考热门场景的推荐系统双塔模型建议每1到2周重训一次行为数据波动较大的内容社区可以缩短到每天增量更新每周全量重训。8. 常见问题与排查技巧实录8.1 离线指标高线上却没什么效果这个问题的头号原因是“离线评估和线上业务场景不一致”。离线指标用的是历史行为做label线上用户面对的是实时的内容池和上下文。排查思路是先看特征一致性再看出力分布最后看召回结果是否有收益空间——如果当前召回位置已经基本被规则策略填满模型提升自然不明显。8.2 模型只召回热门物品长尾物品几乎不出现典型的热度偏差问题。排查点负样本里是否热门物品占比过高导致打压过度还是占比过低导致模型无脑学热度。物品塔的统计特征如曝光量是否权重过大导致热门物品在向量空间居中。温度系数是否过小softmax过度聚焦难负样本而难负样本多是热门物品。解法上可以尝试对高曝光物品做降权负采样、对物品塔热度特征做无偏化处理比如用曝光量取log后再进网络或者干脆去掉热度特征只保留ID和内容特征。8.3 双塔训练Loss下降但召回率波动剧烈典型原因是负采样策略不稳定。Batch内负采样依赖当前batch的组成如果batch的分布抖动大Loss的波动也会大。解法是增大batch size、固定随机种子分片、或者使用更稳定的“全局负采样少量batch内负采样”组合。8.4 用户向量和物品向量的模长差异很大如果只对用户向量做L2归一化而物品向量没做内积的数值会受物品向量的模长影响热门物品因为出现频次高向量模长通常偏大模型容易被模长主导。正确做法是两个塔的最终输出都做L2归一化只保留方向信息。行业内的许多公开方案也都验证了这一点的必要性。8.5 向量检索的召回结果和模型打分不一致这通常不是模型的锅而是ANN索引的近似误差。HNSW这类索引牺牲了一部分精度换速度如果检索参数调得激进召回结果和暴力全量打分的重合率会下降。排查方式很简单用全量暴力检索和ANN检索在同一个测试集上做对比看TopK重合率一般要求做到95%以上。如果达不到把efSearch、M这类参数往大了调。9. 从双塔到下一代召回增强与演进的方向聊到这儿双塔已经不只是一个“模型结构”而是一整套召回系统设计范式。它的核心思想——把匹配建模拆成两个独立编码器再去检索——在后续很多工作中都有延续和增强。下一代召回的几个重要方向多兴趣召回用户向量从1个变成K个代表用户在不同兴趣维度的表征MIND、ComiRec是代表工作。图模型增强把用户和物品的行为交互建模成图用GCN产出embedding作为双塔的增强特征能大幅提升冷门物品的召回能力。大模型语义召回用预训练语言模型把文本语义编码进物品塔对冷启动和语义相似召回有明显帮助尤其适合短视频、资讯等多模态内容场景。双塔与精排的联动优化让双塔的召回结果直接面向精排目标优化而不是只面向“匹配”优化这类端到端联调的研究越来越多。但不管演进到哪一步双塔背后的工程思路——可离线化、可检索化、可扩展化——始终是工业推荐系统落地时必须遵守的原则。模型的精巧程度永远要让位于系统的稳定性和可运维性。10. 一些踩坑后的真心话做了几年双塔召回我最有感触的一点是双塔模型不是“模型结构”问题而是“系统问题”。用户塔、物品塔、embeddding、负样本、索引、特征一致性任何一个环节出了纰漏最后都会表现为线上指标变差而排查起来往往要跨多个系统。给准备上双塔的团队几个总结性建议先定好评估指标再动手离线指标和线上指标的口径最好一开始就统一。负样本策略的优先级高于模型结构双塔的性能上限很大程度由负样本质量决定。特征一致性是线上效果的最大变量训练代码里一定要留特征时点检查。模型先简单再复杂先用标准双塔跑通全链路再逐步加难负样本、多兴趣、注意力等增强模块。如果你正在做召回或者正准备从精排扩展到召回希望这篇能帮你少踩一些坑。双塔这个概念听起来简单真正跑起来里面值得琢磨的细节远比想象中多。
阅读完成 · 觉得有帮助?