双塔召回模型实战:从架构原理到工业落地与调优指南

发布时间:2026/10/31 13:11:46
双塔召回模型实战:从架构原理到工业落地与调优指南

双塔召回模型这几年在推荐系统领域几乎成了标配。无论你是做电商、短视频、信息流还是社交推荐,只要涉及“从千万甚至亿级候选物品里快速挑出用户可能感兴趣的一批”这个场景,双塔模型基本是绕不开的基础方案。这篇文章我不会从头讲推荐系统发展史,直接切入正题:双塔召回是什么、为什么它能成为工业界主流、实际落地时会踩哪些坑、以及我在真实业务中沉淀下来的实操细节。不管你是刚接触召回的新手,还是已经在用双塔模型但想深挖优化空间的老手,本文都值得花几分钟读完,至少能让你的迭代路径更清晰一些。

1. 双塔召回模型的设计思路与方案选型

1.1 双塔架构的基本逻辑

双塔模型(Two-Tower Model)的核心结构非常简洁:它把用户和物品分别编码成两个独立的向量表示,通过向量之间的相似度计算来预测用户对物品的兴趣程度。形象点说,就像一个婚恋平台的“双向匹配”:用户侧有一个“偏好档案”,物品侧有一个“属性档案”,两者越匹配,牵手的概率就越高。

这两个“塔”分别处理不同的输入特征。用户塔接收用户ID、历史行为序列、人口属性、上下文特征等,经过多层神经网络后输出一个定长的用户向量;物品塔接收物品ID、品类、价格、标题文本等,经过自己的网络结构输出一个定长的物品向量。最终用内积或余弦相似度衡量这两个向量的距离,距离越近,代表用户对物品的兴趣越大。

我第一次接触这个架构时的第一反应是:这不就是矩阵分解的深度神经网络版本吗?确实,从思想上说二者一脉相承。矩阵分解学习用户隐向量和物品隐向量,双塔模型则用神经网络把更丰富的特征映射到隐空间。但双塔模型能成为工业界主流,绝不是简单换了个函数逼近器,后面我会展开讲它的关键优势。

1.2 为什么双塔模型能成为召回方向的主流

从2019年Google公布双塔召回在YouTube实际业务中的应用效果之后,这个架构就迅速在各家互联网公司铺开。与其听我罗列它有多少优点,不如还原一下工程师在技术选型时真实的决策过程。

召回阶段面对的核心约束是:需要在极短时间内从海量候选物品中筛出几百上千个,再交给排序模型精排。如果候选池是千万级、亿级的,意味着你不可能在这个阶段上复杂模型——排序模型再轻量,跑一遍千万级推理的时间也扛不住。

双塔模型能解决这个问题的关键在于“物品向量离线算好,线上只算用户向量”。物品向量事先算好放进向量检索索引,线上用户请求到达时,只需跑一遍用户塔拿到用户向量,再到近邻索引里查最相似的几百个物品。原来千万级打分变成了一次向量检索,耗时从几百毫秒压到几毫秒甚至更低。这套“离线算物品、在线算用户”的架构,是双塔模型能在工业界全面铺开的根本原因。

另一个让我觉得它“妙”的点是物品塔天然具备扩展性。新物品入库后,不用重新训练整个模型,只要跑一遍物品塔拿到向量就能立刻参与检索,配合近线增量更新,新物品分钟级就能进入召回候选池。在物品更新频繁的电商、内容场景,这个特性非常关键。

1.3 双塔模型与其他召回方案的横向对比

很多同学会问我:双塔模型和FM、Item2Vec、DeepWalk这些召回方法比,到底好在哪里?为了说清楚这个问题,我把主流召回方案放在一张表里对比。

方案表达能力在线推理成本离线维护成本适用场景
协同过滤(ItemCF/UserCF)低极高(需要实时算相似度)低小规模冷启,baseline效果验证
矩阵分解(MF/BiasSVD)较低中(内积打分)低简单行为数据,特征维度少
Item2Vec/DeepWalk中高(需要实时查物品序列)中物品间关系挖掘
FM及其变体中高(千万级无法全量打分)中排序场景居多,召回受限于算力
双塔模型高低(在线只算用户向量)中大规模召回、多路召回主路径

从表格能看出,双塔模型的核心竞争力不是单点能力最强,而是“表达能力强 + 在线成本可控”这个组合最均衡。FM虽然在线也能通过向量化做召回,但特征交叉能力受限于二阶组合,深层次非线性关系拟合不了;Item2Vec只能学物品关系,接不住用户特征。

另外还有一点容易被忽略:模型训练的效率。双塔模型的负样本采样天然支持大规模batch训练,GPU利用率高,千万级样本训练一轮在单卡上也就几个小时。我见过一些团队想直接上复杂的序列模型做召回,效果没比双塔好多少,训练成本却翻了十倍,最后不得不回退。

1.4 双塔模型的适用边界与局限

双塔模型不是银弹,它有明显的适用边界。最典型的问题在于双塔两侧交互太晚——特征只能在最后的内积层发生交互,用户特征和物品特征中间的交叉信息丢失严重。某些情况下排序场景里能轻松学到的“用户喜欢红色的球鞋”这类交叉关系,在双塔模型里几乎学不到。

所以把双塔模型用在召回阶段更合适,排序阶段需要的是强交叉能力,不适合用它。还有一种场景我建议慎重:如果召回目标极度依赖某个强特征组合(比如地理位置+品类),纯双塔结构很难学出这个关系,这时候可以考虑在双塔底部加入交叉层,或者干脆上多塔结构。

此外双塔模型对负样本的定义极其敏感。负样本选不好,模型学出来的向量空间会变形,召回结果偏向热门物品,冷门好品完全沉底。这一点在后面的实操章节我会详细展开。

2. 双塔召回模型的核心实现细节

2.1 特征工程:用户塔和物品塔各吃什么特征

双塔模型的特征设计有个根本原则:用户塔特征只来自用户侧,物品塔特征只来自物品侧,中间不交叉。这既是结构限制,也是线上推理成本控制的关键——如果用户塔的输入依赖物品信息,在线推理时就必须对每个候选物品跑一遍用户塔,计算量直接爆炸。

我一般会把特征分为三类来看。用户侧特征包括:用户ID Embedding、历史点击/购买物品ID序列的Embedding聚合、用户画像特征(性别、年龄段、城市等级)、上下文特征(当前时间、设备、网络环境)。物品侧特征包括:物品ID Embedding、物品属性特征(品类、品牌、价格区间)、物品统计特征(近7天点击率、曝光量、好评率)。统计特征要注意做时间衰减,老数据直接影响模型对物品当前热度的判断。

历史行为序列的处理是最容易出彩也是最容易出错的地方。常见做法是把最近N次交互的物品ID取Embedding后做sum pooling或attention pooling。我强烈建议用sum pooling而不是average pooling,原因很简单:用户历史行为数量本身携带兴趣强弱信号,交互次数多的用户兴趣浓度就是比交互少的用户高,average会把这个信息洗掉。用sum时注意对交互次数做截断和归一化,防止个别重度用户向量模长过大。

2.2 Embedding维度与网络结构的设计选择

不少同学一上来就问:Embedding维度设多少合适?我的经验是不要拍脑袋,先想清楚你要投放到哪个量级的向量检索系统。如果只用内积做打分,维度其实不敏感;但如果要用faiss这类ANN索引做近邻检索,维度直接关联索引性能。128维在大多数场景是性价比比较合理的起点,再高到256维检索性能下降明显,收益却不一定涨。

网络结构上,我习惯的做法是:Embedding层——>两个全连接层(256->128)——>输出128维向量。激活函数用ReLU,最后接一个L2归一化。中间层的维度不需要太大,召回模型的特征交叉能力本来就不是重点,与其堆宽度不如优化特征质量。过深的塔在这里不仅没有收益,还会增加过拟合风险,尤其是物品侧冷启动物品特征稀疏,深网络很容易把噪声学进去。

2.3 相似度计算方式:内积还是余弦

双塔模型的最后一层相似度计算,内积和余弦相似度都常见,但我在实践中更倾向用余弦相似度,原因在于它天然带有L2归一化的效果,能把嵌入向量的模长信息剥离,只保留方向信息,这让向量检索的索引更稳定。当一个物品的向量模长异常偏大或偏小时,内积计算会被模长主导,影响召回排序的合理性。

不过这里有个前提:如果你希望模型能建模“喜好强度”,比如重度用户和轻度用户对同一物品的偏好差异,那么纯粹的余弦相似度会丢失模长中包含的强度信息。这种情况下先用内积训练,最后再加L2归一化做检索,效果往往比直接上余弦好。我在实际项目里的做法是:训练时用内积加温度系数软化,推理时用余弦距离检索,二者之间做个转化即可。

2.4 温度系数tau的选择与调参

温度系数(Temperature)在双塔模型里是个容易被忽略但影响巨大的超参数。它本质上控制了softmax分布的尖锐程度——温度越低,loss对困难负样本的惩罚越大;温度越高,分布越平滑,模型对负样本的区分越宽容。

我见过很多团队用默认值或者干脆不设置,导致模型训练半天效果很平淡。调试温度系数时,我的经验是从1.0往下调,观察验证集上的召回率变化。温度在0.05~0.2区间往往效果最好,低于0.01会让训练不稳定,loss震荡严重,高于0.5会让正负样本拉不开差距。值得一提的是,温度系数和负样本质量有强耦合关系:负样本越“难”,温度可以相应调低,强化模型对难样本的区分能力。

3. 训练工程与实操要点

3.1 样本构建:正样本、简单负样本与难负样本

双塔模型是典型的判别式学习——目标是让正样本对的相似度高于负样本对。样本构建的质量直接决定模型的天花板。

正样本一般取用户真实发生交互(点击、购买、观看)的物品。要注意的是,不同类型的交互权重应该不同。购买行为的权重远高于曝光点击行为,我在实践中会把训练样本按交互类型加权采样,避免点击行为完全淹没购买信号。

负样本构建分三个层级。第一层是随机负样本,从全量物品库里随机抽样,负责让模型具备基本的分辨能力;第二层是热门负样本,优先选热门但用户没有交互的物品,防止模型被热门物品带偏;第三层是难负样本,选择同品类或相似物品中没有交互的样本,强制模型学习精细的区分边界。三层按比例混合构建,我的常用比例是随机:热门:难负样本等于5:3:2。

3.2 Batch内负样本与GPU显存优化

Batch内负样本是双塔模型训练速度的关键。简单来说,每个batch内采样B个正样本对,除了第i个样本自身的正样本外,其余B-1个物品天然可以作为负样本。这避免了显式采样负样本的计算开销,又能一次性利用整个batch的信息。

但直接使用全Batch内负样本有个问题:热门物品出现在不同样本中的概率高,容易被频繁当作负样本,导致梯度方向被热门物品带偏。所以我建议在Batch内负样本基础上,引入基于物品出现频率的采样修正(类似Sampled Softmax中的logQ修正)。实现上也不复杂——统计物品在训练样本中的出现频次,在logits计算时减去logQ,能有效消除热门偏差。

此外Batch Size本身也影响负样本质量:Batch越大,Batch内负样本越多样,模型效果通常越好。我实测下来,从256提升到1024,召回率在线下评估中能涨2~3个点,但再往上提升就是边际递减了,而显存压力成倍增长。在单卡16G的情况下,Batch Size 1024加128维Embedding是一个比较合理的配置。

3.3 特征归一化:让模型更好地学习相似度

特征归一化在双塔模型里是保证训练稳定性的关键操作。输入特征范围不统一,数值大的特征会主导梯度,导致模型学不好其他特征。归一化分两类:数值型特征的缩放,以及Embedding向量的L2归一化。

数值型特征比如价格、点击率、热度分,我习惯做log变换后再标准化。原值直接喂给网络会出现极端值问题——价格从10到10000的跨度,线性网络很难适应。log变换把指数级别的差异压成线性关系,网络学习明显更平稳。

Embedding向量的L2归一化放在网络输出层。刚接触双塔模型的同学常忽略这步,直接拿未归一化的向量去做内积。结果就是训练过程震荡严重,因为内积值范围受向量模长影响,不同batch的梯度尺度差异巨大。L2归一化后内积值域被限制在[-1,1],配合温度系数,训练稳定性会有质的提升。

3.4 训练过程中的常见技巧:难负样本增强与退火策略

难负样本增强是双塔模型训练里的“彩蛋”技巧。纯粹依靠Batch内负样本,模型学到的是粗粒度的区分能力——用户感兴趣的类目VS不感兴趣的类目。但真实场景中,用户对一个类目下的具体物品有更强的偏好,比如都是球鞋,用户就是喜欢Nike不喜欢Adidas。这种精细区分需要难负样本才能学出来。

我用过的一种比较有效的难负样本方法是:先从当前模型召回结果里取用户没交互过但相似度排在前列的物品,作为额外负样本补充进训练集。这个做法有点类似hard negative mining,但要注意样本更新频率——每隔一定训练步数重新生成一次难负样本,否则模型会过拟合到固定样本上。

退火策略则是解决训练从“易”到“难”过渡的问题。训练初期模型能力弱,全部用难负样本会让收敛困难;后期模型能力增强,全部用简单负样本又提升有限。我按训练步数动态调整负样本构成比:前30%训练步数以随机负样本为主,之后逐渐增大难负样本比例,最后稳定在难负样本占30%~45%左右。

3.5 从PaddlePaddle到TensorFlow:一个可复现的训练流程参考

工具选型方面,我接触过TensorFlow、PyTorch、PaddlePaddle等多套框架。如果只是训练原型双塔模型,PyTorch的灵活性最好,自定义损失函数和难负样本采样非常方便;如果要高效部署到线上环境,TensorFlow Serving对模型导出支持更成熟。

下面给出一段PyTorch风格的简化训练代码骨架,帮助理解整体流程。

class TwoTower(nn.Module): def __init__(self, user_feature_dim, item_feature_dim, embed_dim=128): super().__init__() self.user_tower = nn.Sequential( nn.Linear(user_feature_dim, 256), nn.ReLU(), nn.Linear(256, embed_dim) ) self.item_tower = nn.Sequential( nn.Linear(item_feature_dim, 256), nn.ReLU(), nn.Linear(256, embed_dim) ) def forward(self, user_features, item_features): user_vec = F.normalize(self.user_tower(user_features), p=2, dim=-1) item_vec = F.normalize(self.item_tower(item_features), p=2, dim=-1) return user_vec, item_vec def contrastive_loss(user_vec, item_vec, tau=0.1): # 内积相似度矩阵 logits = torch.matmul(user_vec, item_vec.T) / tau batch_size = user_vec.size(0) labels = torch.arange(batch_size, device=user_vec.device) loss = F.cross_entropy(logits, labels) return loss

这段代码的关键在于:模型输出先做L2归一化,相似度矩阵除以温度系数tau,标签直接取对角线索引。这样整条训练逻辑就是标准的Batch内负样本对比学习。

4. 服务化部署与近线更新

4.1 向量索引:从全量打分到ANN检索

训练好的双塔模型,物品侧全量Embedding需要事先算好并写入向量索引。这里要从“精确检索”和“近似检索”两种方案里选。

精确检索就是全量计算用户向量和所有物品向量的内积。千万级物品池的精确检索在CPU上大概需要几十毫秒到上百毫秒,在GPU上能到个位数毫秒,但单机显存放不下全量Embedding时就得做分片。近似检索基于ANN(近似最近邻)算法,用空间换时间,典型代表是faiss的IVF、HNSW。线下评测时精确检索和ANN召回率差异在可接受范围内(通常差1~2个点),但线上延迟差异巨大。

我给的选型建议是:物品池在百万级以内,先用精确检索,简单可控;上了千万级再考虑faiss的IVF-PQ方案。索引参数需要根据线上流量压测调优,nprobe数量和PQ的子空间个数与召回率、延迟直接相关。

4.2 双塔异步更新:低延迟与一致性的权衡

双塔模型线上推理只跑用户塔,物品向量全部离线算好丢进索引,这本身就是一个异步架构。问题在于:用户侧特征变化频繁(一个点击行为立刻要反映到用户向量),物品侧特征相对稳定(物品属性变化少,统计特征有延迟),两侧更新频率天然不同。

我见过最朴素的方案是定期全量重建物品索引,比如每天凌晨一次。但这样做的问题很明显:新物品无法当天进入召回,用户当天新产生的行为也要第二天才能影响召回结果。这在信息流场景几乎是不可接受的,因为用户连续刷新几次后兴趣变化已经很大了。

更好的做法是“全量+增量”结合:每天离线全量刷一次物品塔Embedding,写入全量索引;同时新物品或发生过交互的物品实时/准实时算好向量,写入增量索引。线上召回时合并全量索引和增量索引的结果。增量部分的向量可以用一个轻量模型服务(只是单次前向推理)来承载,成本可控。

4.3 模型异构:训练与推理的一致性保障

训练和推理的一致性问题是双塔模型部署中最容易翻车的环节。训练时用户塔输入是训练日志里落盘的特征,推理时线上请求的特征从特征服务实时拼接——两边的特征定义、预处理逻辑、Embedding字典完全一致,才能保证线上效果和离线评估一致。

我踩过的典型坑是特征的“回看窗口”定义不一致。离线训练时“用户近7天点击的物品ID序列”取自行为日志;线上推理时这个序列来自实时特征服务,如果两边时间窗口的起止时间不一致,用户向量就跑偏。解决办法是建立一套统一的特征配置管理,把特征口径收敛到一个配置中心,训练和推理共用同一份配置解析逻辑。

数值特征在训练和推理时的分布偏差同样值得关注。离线训练数据的特征分布来自历史日志,线上实时特征可能因为推荐策略改变而分布漂移。我习惯在特征上线前做一次分布对比,KL散度过大时先修正特征而不是盲目上模型。

5. 双塔模型的效果评估与落地见效

5.1 离线评估指标:不只是Recall@K

很多人评估双塔模型就看Recall@K,一个指标定生死。实际上离线评估至少要看三个维度。Recall@K衡量召回覆盖率,但单独看会受热门物品主导——一个只召回热门物品的模型,Recall@K可能很高,对业务的实际增量有限。所以我同时会看Recall@K在长尾物品上的分桶表现,用物品的曝光区间把结果拆开看长尾覆盖率的变化。

第二个维度是排序质量指标,比如MRR(平均倒数排名)或NDCG。双塔模型的输出会直接作为召回队列的排序依据之一。MRR能体现用户真正感兴趣的物品在召回列表中排得多靠前,比Recall@K对排序质量更敏感。

第三个维度是向量空间的合理性。如果训练收敛后用户向量和物品向量在嵌入空间里的分布结构不合理(比如全部挤在一起、缺乏聚类性),后续调参会很被动。我习惯定期对Embedding做PCA可视化,看类目是否形成清晰的簇结构,结构混乱时说明特征或负样本构造出了问题。

5.2 在线评估:AB实验与业务指标联动

离线指标涨了不代表线上效果就好,AB实验才是验证双塔模型价值的最终标准。做双塔模型AB实验时,我建议不只盯着CTR(点击率)看。召回侧优化的直接收益体现在曝光结构的变化上——用户是否看到了更多之前没有见过、但确实感兴趣的物品。

推荐关注三个核心业务指标组合:人均曝光物品数(看多样性变化)、人均点击物品数(看整体匹配效率)、长尾物品曝光占比(看对冷门物品的挖掘能力)。只改动召回层的时候,CTR往往不会有特别剧烈的变化,但人均点击物品数上涨通常意味着匹配效率的实质提升。

我踩过的坑是在小流量实验没跑足够久就下结论。双塔模型影响的是用户长期兴趣探索,短期看CTR可能波动不大甚至略降,因为召回结果里多了用户未曾见过的新物品、点击不确定性更高。建议至少跑满一周完整周期(覆盖工作日和周末的行为差异)再做决断。

5.3 双塔模型对新物品冷启的改善

双塔模型相比协同过滤的一个显著优势在于物品侧特征可以包含文本和属性信息,新物品只要特征完整就能算出向量、立即进入召回池。这在商品上新频繁的电商场景非常有用。

实践中有个关键点:新物品的统计特征(点击率、转化率)为空,需要先做特征填充,否则物品塔输入中大量零值会让向量偏离正常分布。我习惯用同类目成熟物品的均值统计特征做填充,等新物品积累到一定曝光量后再逐步切换成自身统计特征。这个过程叫“特征渐进填充”,相当于给新物品一个平稳的冷启动阶段。

5.4 日志驱动闭环:模型持续迭代的飞轮

双塔模型上线不是终点,而是持续迭代的起点。我维护的推荐系统有个核心闭环:线上曝光日志回灌到离线样本池,新产生的用户行为自动进入下一轮训练数据,模型每周重新训练一轮。这背后的逻辑是:用户兴趣持续漂移、物品热度不断变化,静态模型上线三个月后效果必然衰减。

闭环迭代中最重要的事情是数据质量控制。线上日志里不可避免会混入异常流量(爬虫、刷量、低质量点击),如果这些噪声不清洗就进训练集,模型会被带偏。我每周花在数据清洗和特征校验上的时间,和花在模型迭代上的时间几乎一样多。别小看这个环节,很多团队模型上线后效果快速衰减,排查到最后都是数据质量问题。

6. 双塔模型常见问题与排查技巧

6.1 训练不收敛或收敛后效果差

双塔模型训练不收敛,我第一个怀疑的就是温度系数设置。温度太高导致softmax梯度消失,模型学不到东西;温度太低导致梯度爆炸,loss直接飞掉。可以先固定tau=0.1从头训练,确认Loss曲线能平稳下降后再去调其他参数。

第二个常见原因是Embedding未做L2归一化。如果模型输出层的向量模长没有约束,内积值域过大,训练初期遇到较大的梯度很容易直接把网络参数冲到数值不稳定的区域。把输出层改成“Linear + L2Norm”之后,这类问题多半能缓解。

第三是负样本质量问题。负样本太简单(比如都是完全不相关的物品),模型很容易就收敛到“区分相关与不相关”的状态,对精细兴趣的拟合能力很差。出现这种情况,你看到的Loss会下降得飞快,但Recall@K和排序质量指标表现平庸。加入难负样本后曲线会更陡峭,但最终离线指标会明显更优。

6.2 线上的召回结果偏向热门或偏向冷门

线上召回结果偏向热门是双塔模型最典型的问题之一。原因在样本构建阶段就埋下了:热门物品在训练样本中出现的频次远高于冷门物品,模型天然偏向把热门物品召回出来。即使热门物品并不完全匹配用户兴趣,它的Embedding也更容易落在用户向量的近邻区域。

排查思路分两步。先检查训练样本的负样本采样分布,确认热门物品被降采样了没有;再看Batch内负样本是否需要做logQ修正。如果这两步都做了、热门偏向还是严重,可以把L2归一化改成带温度的余弦相似度——温度系数调低一点,模型需要更多证据才能判定高相似度,热门物品被滥召回的情况会有缓解。

另一个极端情况是召回结果过于长尾、主要业务指标反而下降。这通常是负样本里难负样本比例过高,模型对热门物品产生了过度抑制。这时候把难负样本比例调低,重新平衡一下即可。

6.3 用户向量分布坍缩与多样性消失

有一段时间我的双塔模型召回结果多样性一直不行,排查发现用户向量分布集中在一个很小的区域内。原因在于用户历史行为序列里交互集中的头部物品太多,不同用户的向量高度相似,导致召回结果同质化。类比来说,大家看的都是同样一批爆款视频,各自的“兴趣档案”自然长得差不多。

解决思路是给用户行为序列里的物品Embedding引入“行为时间衰减权重”。近期的行为权重高、远期行为权重低,让用户向量更贴近当前兴趣而不是历史累积兴趣的整体平均。同时考虑对不同行为类型差异化加权:购买权重显著高于点击,这也能拉开不同用户向量的距离,避免被泛化的浏览行为主导了向量方向。

6.4 新物品向量质量差与冷启动失效

双塔模型的新物品冷启动上,最常出现的问题是文本特征稀疏导致的向量漂移。电商场景里新商品往往只有标题和类目信息,图片特征还没处理完,此时如果文本Embedding没有做好,算出来的物品向量就可能偏离所在类目的中心,召回时很难被相关用户命中。

我的排查方案是:新物品上线时先用同类目成熟物品的向量均值做初始化。具体做法是,物品塔输入里加一个“类目先验”特征,取该新物品所在类目下成熟物品Embedding的平均向量。这样即使新物品自身特征不完整,反正先让它在隐空间里落到类目中心附近,等积累了行为数据后再回归到真实特征驱动的向量。

6.5 问题排查速查表

我把常见问题整理成一张速查表,方便排查时快速定位。

现象可能原因处理建议
Loss不下降温度系数过大/过小从0.1起调,监控梯度范围
Loss震荡严重缺L2归一化输出层加归一化,合适调小学习率
召回结果偏热门负样本热门偏差热门物品降采样、加logQ修正
召回结果偏长尾难负样本占比过高降低难负样本比例
同类物品都召回行为序列无时间衰减加重近期行为权重
新物品召不回物品特征缺失/稀疏类目向量均值初始化
离线指标涨、线上没涨训练推理特征不一致统一特征口径与配置
多样性差用户向量分布坍缩行为加权多样化、引入探索噪声

这张表基本覆盖了我自己做双塔模型以来遇到过的绝大多数问题。遇到新问题时不建议一条路走到黑,先通过数据排查定位是样本问题、特征问题还是模型结构问题,再决定改哪里。

7. 双塔模型的进阶优化方向

7.1 多向量双塔与MIPS检索的取舍

标准双塔模型用一个向量表达用户的完整兴趣,这个结构天然有容量瓶颈——用户的兴趣往往是多峰的,一个向量很难同时表达“喜欢运动鞋”和“喜欢咖啡器具”。于是有了多向量双塔的变体:用户侧输出多个向量,每个向量表达一个兴趣子空间;物品侧仍然一个向量。线上推理时用每个用户向量分别检索,合并结果。

多向量方案在线下评测中确实能看到召回率提升,但代价也很直接:检索耗时随向量数量线性增长,N个向量就是N倍延迟。你需要在召回率和成本之间做平衡。我的建议是先把单向量模型的潜力挖到底,确认瓶颈确实在表征容量上的时候再上多向量方案。我见过不少团队单向量还没调好就急着上多向量,最后得不偿失。

7.2 引入序列模型增强行为表征

用户历史行为序列在标准双塔模型里通常就是Embedding求和,序列顺序信息完全丢掉了。行为序列中时间顺序其实携带很强的意图信号——用户刚看过A类商品又看B类商品,中途的跳转顺序往往透露了消费决策路径。可以在用户塔里引入Transformer或GRU这类序列编码器来增强行为表征。

引入序列模型后要注意训练成本的增加与过拟合风险。序列模型的参数量远大于sum pooling,训练数据量不够时非常容易在验证集上掉点。我的经验是先用轻量的GRU结构加注意力机制做对比实验,效果确认有提升再考虑上更重的Transformer结构。否则只是徒增工程复杂度,线上收益未必能覆盖投入。

7.3 召回-排序联动优化

双塔召回模型和排序模型不应该完全割裂。一个常见的联动痛点:召回模型优化的目标和排序模型期望的输入分布不一致。排序模型看到的召回队列全部来自“当前线上双塔”,而训练双塔时的正样本来自用户真实行为,两者存在分布偏差。

处理思路是让双塔训练时尽可能贴近排序模型的真实输入分布:构建训练样本时,把排序模型精排出来的Top结果也当作“软正样本”引入,让双塔看到更接近线上真实分布的样本空间。这类闭环反馈的思路在很多成熟团队都有实践,落地后往往能带来稳定的指标提升。当然这也意味着召回和排序两个模型需要放到同一套迭代框架里管理,协作成本会高一些,但对整体系统的提升是实打实的。

7.4 多场景迁移与跨域推荐扩展

双塔模型天然适合做多场景迁移。不同业务场景(首页推荐、搜索页推荐、相关推荐)可以共享底层物品塔,只针对场景做独立用户塔,离线训练时用一个底座加上多场景分支,线上部署时按场景路由到各自的用户塔。这样物品侧特征可以跨场景共享、积累,冷启动新场景时也能直接复用全量物品向量。

跨域推荐扩展类似:主站行为数据丰富,新业务或新频道行为数据稀疏,把主站预训练的双塔作为底座,在新的目标域上用少量数据微调用户塔,能显著加速新域模型的冷启速度。我落地过的一个内容App新频道模型,用迁移方式比直接在新域训练,冷启收敛时间缩短了一半以上,新域上线首周的点击指标就能接近老域水平的80%。

写在最后

分享一点我个人的体会。双塔召回模型在不缺数据和算力的工业场景里,核心瓶颈早就不是模型结构本身,而是样本工程质量、特征口径一致性、以及离线在线闭环的迭代效率。我在实操中见过太多“模型效果平庸”的案例,最后排查下来发现负样本随便采、特征口径两边不一致、日志里噪声根本没清洗。算法结构恰恰是最不需要反复折腾的部分,把工程基础夯实,双塔模型的效果就能超过绝大多数花哨方案。如果你刚准备在业务里落地双塔模型,我建议先花两周时间把样本和特征体系理清楚,再动手写模型代码,这个顺序能帮你少走很多弯路。