SetMIR: Multi-Interest Retrieval as Set Prediction

Xiaodong Liu, Congfei Zhang, Hsiang-wei Chao, Siman Wang, Xiao Bai, Tong Zhao, Jingxiao Ma, Wen Zhang, Zhe Liu 等 16 人 · Snap · arXiv 2608.30251 · 发表 Tue, 01 Sep 2026 00:00:00 -0400
14 分钟阅读时长 5298字 5.00综合分 第 1 名09-02 Personalization 日报
目录 · 8 节1它到底在解决什么问题?2先看全景3拆开看:训练时怎么分配目标4拆开看:线上怎么决定发几路5这套方案真的更好吗?6我认为它的局限7一个推荐/搜索团队可以怎么试8一句话结论
相关 5新颖 5实用 5证据 5时效 5
把多兴趣召回改写成“带存在概率的集合预测”,同时解决表征冗余和服务预算浪费,适合长行为序列与兴趣数量变化大的用户。实践上可先复用现有向量索引,只替换用户塔和 dispatch 策略;同索引同配额下相对 I2I 源 CTR 提升 44%、CVR 提升 51%,说明动态兴趣路由值得优先验证。

做召回的团队值得认真读,尤其是已经在用多兴趣召回、又苦于几个兴趣向量越学越像的团队。它的改动很小,一个训练时的匹配方式和一个输出头,却同时处理了训练和线上两个老问题。来自 Snap 的动态商品广告(DPA,Dynamic Product Ads)团队,已在线上作为新召回源上线。核心思想一句话:用 K 个可学习的兴趣 query 去“预测一个兴趣集合”,没分到真实目标的 query 学会说“这次不存在”,线上据此决定实际发几路 ANN 检索。做法借自目标检测里的 DETR(用一组可学习 query 直接预测目标框集合的检测模型):训练时用匈牙利匹配把真实目标一对一分给 query。几个数字:把匹配方式从常见的 argmax 换成匈牙利匹配,R@10 差距达 36.5%(argmax 版本只有完整版的 63.5%);线上平均每次请求只发 6.70 路 ANN,比固定发 10 路少 33%;作为新召回源上线后整体 CVR +3.11%。

它到底在解决什么问题?

先看一个示意场景。一个用户这周看了衬衫、裤子、连衣裙、凉鞋和耳饰。单向量双塔会把这些平均成一个向量,结果往往偏向点得最多的那个品类。论文图 3 的真实案例就是如此:单向量召回的前 10 个全是裤子。

多兴趣召回(MIND、ComiRec 一类)给每个用户输出 K 个向量,每个向量各发一路 ANN 检索再合并。但它们有两个毛病。

第一个叫兴趣坍缩(interest collapse)。常见训练方式是让每个目标商品去找和它最近的那个兴趣向量(argmax),只更新那一个。看一个示意的相似度表:

兴趣 query目标 1:衬衫目标 2:连衣裙
q10.80.6
q20.30.5
q30.10.2

按 argmax,两个目标都选 q1(0.8 和 0.6 都是各自那一列的最大值),q2、q3 收不到任何梯度。训下去 q1 什么都管,其他 query 要么不动,要么漂移,最后有效兴趣只剩一两个。论文的消融里,argmax 训练的模型平均只有 2.16 个 query 被判为“存在”。

第二个叫静态派发(static dispatch)。线上不管用户今天有几个兴趣,都固定发 K 路 ANN,每路分 N/K 个候选名额。如果 10 个向量里有 4 个几乎重复,就有 4 路检索在重复取回同一批商品,白白占了候选预算和调用成本。

SetMIR 的出发点是:用户的兴趣是一个大小不定的集合,模型应该预测这个集合,而不是硬造 K 个互相独立的向量。

先看全景

SetMIR 的三段结构。左边是输入 embedding:历史商品先过一个冻结的预训练商品内容模型,再加上可学习的位置 embedding 和行为类型 embedding(浏览、加购、购买)。中间是 J 层 transformer 编码器读历史得到 H。右边是 M 层兴趣解码器:K 个可学习的兴趣 query 先互相做 self-attention,再对 H 做 cross-attention,最后每个 query 输出一个召回向量 e_k 和一个存在分数 p_k。
SetMIR 的三段结构。左边是输入 embedding:历史商品先过一个冻结的预训练商品内容模型,再加上可学习的位置 embedding 和行为类型 embedding(浏览、加购、购买)。中间是 J 层 transformer 编码器读历史得到 H。右边是 M 层兴趣解码器:K 个可学习的兴趣 query 先互相做 self-attention,再对 H 做 cross-attention,最后每个 query 输出一个召回向量 e_k 和一个存在分数 p_k。
阶段做什么为什么需要
输入最近 40 个行为,每个商品用冻结的内容向量,加位置和行为类型商品塔冻结,线上直接复用现成的单向量 ANN 索引
编码2 层 transformer 读历史得到上下文化的历史表征
解码10 个 query,6 层解码器,query 之间先互相看、再读历史让 query 在读历史前先“分工”,不去追同一个主信号
输出每个 query 一个归一化召回向量和一个存在分数存在分数决定线上这路检索发不发
训练匈牙利匹配 + InfoNCE + 存在损失 + 多样性损失一对一分配目标,未分到的 query 学会说“不存在”
线上存在分数过阈值 → query 去重 → 每路 ANN → 取最大分合并按请求动态决定发几路

模型很小,隐藏维 128,可学习参数约 3M。监督信号来自未来 3 天窗口里的高意向行为:购买、加购和上划(swipe-up,Snap 广告里点进商品落地页的动作)。目标按商品去重,同一商品保留最强的行为,最多保留 15 个;窗口之前的最近 40 个行为作为输入。

拆开看:训练时怎么分配目标

匈牙利匹配是什么,为什么能防坍缩? 匈牙利匹配是一种经典算法:给定一张“query × 目标”的代价表,找出一种一对一的配对,使总代价最小,任何 query、任何目标都不能被用两次。SetMIR 的代价是“负相似度”加一项“负的存在置信度”:

两项权重分别取 1 和 0.5,第二项在前 10% 的训练步里线性升温,让早期的匹配主要由向量几何决定。

回到上面的示意表,先只看相似度。可选的一对一配对里,q1→衬衫、q2→连衣裙 的总相似度是 0.8+0.5=1.3;q1→连衣裙、q2→衬衫 是 0.6+0.3=0.9;q1→衬衫、q3→连衣裙 是 0.8+0.2=1.0。最优是第一种。于是 q2 被迫负责连衣裙,拿到梯度往连衣裙方向走;q3 没分到目标,得到一个“存在 = 0”的标签。这说明匈牙利匹配从训练目标上就保证了“一个兴趣只由一个 query 负责”,不需要额外的正则去硬拉开 query。

分到之后怎么学? 每对(query,目标)用 InfoNCE 训练:正样本是分到的目标,负样本是同一个 batch 里跨 GPU 收集到的所有目标向量,温度 0.05。没分到的 query 不参与 InfoNCE,不会被拉向任何方向。

存在分数怎么学,为什么需要? 每个 query 的存在头输出 p_k,标签就是“这次有没有被匹配上”,用二分类交叉熵训练。这是 argmax 训练缺的那块:argmax 下没被选中的 query 什么信号都没有,在 SetMIR 里它会被明确告知“这次不存在”。要注意论文自己的说明:没匹配上只代表未来 3 天窗口里没有更多高意向目标,不代表用户永远没有这个兴趣,所以存在分数是“这次请求要不要发”的调度信号,不是用户画像标签。

多样性损失做什么? 只对存在分数大于 0.5 的 query 两两计算余弦相似度,超过 0.3 的部分才罚。示意:两个活跃 query 余弦 0.5,罚 0.5−0.3=0.2;余弦 0.2,不罚。已经分开的 query 不受影响。三项损失权重分别是 1.0、0.5、0.1。

拆开看:线上怎么决定发几路

线上只做一次前向,得到 10 组(e_k, p_k),然后三步:

  1. 存在门控:只保留 p_k > 0.3 的 query;一个都不过就保留分数最高的那个。
  2. query 去重:按 p_k 从高到低排,如果某个 query 和排在它前面、已保留的 query 余弦超过 0.9,就去掉。这一步论文叫 query 级 NMS(非极大值抑制,目标检测里去掉重复框的做法)。
  3. 每个剩下的 query 各检索 ⌈N/K̃⌉ 个商品,K̃ 是剩下的路数;同一商品被多路召回时取最高分,再取前 N 个。

手算一个示意例子:5 个 query 的存在分数是 0.95、0.90、0.70、0.25、0.10。门控后剩前 3 个。第 2 个和第 1 个余弦 0.93,超过 0.9,被去掉。最后 K̃=2,假设候选预算 N=1000,每路取 500 个。这说明总召回深度基本不变,省下的是 ANN 调用次数,以及重复检索浪费掉的名额。

这套方案真的更好吗?

离线数据是 Snap DPA 的用户-商品行为日志,商品库数亿,测试集约 100 万用户、530 万个目标商品。按 Snap 的规定只报相对值,下表每列都以 SetMIR 为 100%。所有方法共用同一套冻结商品向量、K=10、同样的优化器和召回预算:

方法平均 ANN 路数R@1R@10R@100MRR
MIND1045.7%52.9%61.7%44.4%
ComiRec-SA1067.4%89.8%99.9%80.0%
DCM1026.0%37.4%49.3%29.1%
KuaiFormer1043.0%52.0%62.8%42.2%
SetMIR(argmax)2.1662.1%63.5%69.1%56.4%
SetMIR6.70100%100%100%100%

最强的基线 ComiRec-SA 在 R@100 上几乎追平(99.9%),但 R@1 只有 67.4%、MRR 80.0%。这说明它能捞回差不多的候选池,排序却差得多,而下游精排最看重的正是靠前的那部分。最有说服力的一行是 SetMIR(argmax):结构完全一样,只把匈牙利匹配换成 argmax,R@10 就掉到 63.5%,活跃 query 从 6.70 个降到 2.16 个。KuaiFormer 结构和 SetMIR 最接近,也是 transformer 加可学习 query,R@10 只有 52.0%。这说明差距主要来自 query 怎么被监督,而不是网络结构。

单项消融里,去掉存在损失后 R@10 降到 91.4%,门控还放进了 8.23 路而不是 6.70 路。这说明没有监督的存在分数不携带“该不该发”的信息,结果是发得更多、召回反而更差。去掉多样性损失后 R@10 降到 93.2%,query 之间更相似了。三者中匹配方式的影响最大。

K 的扫描也很有信息量:以 K=1 为 100%,K=3 时 R@10 已到 137.5%,K=10 到 155.0%,K=15 是 155.1%,基本饱和。更有意思的是活跃路数:K=15 时平均也只有 7.59 路被判为存在。这说明有效兴趣的个数是由数据决定的,给更多 query 模型也不会全用。

线上服务的消融用同一个模型、三种派发策略:

派发策略平均 ANN 调用R@1R@10MRR
固定发 10 路10.00100%100%100%
+ 存在门控(阈值 0.3)7.73101.9%99.4%100.7%
+ query 去重(阈值 0.9)6.70101.8%99.2%100.7%

门控砍掉 23% 的调用,R@10 只损失 0.6%,R@1 反而略升,因为低置信 query 召回的候选本来就会稀释合并列表。

单个用户的召回对比。最上面是用户历史,覆盖衬衫上衣、裤子、连衣裙、鞋、半裙、手袋和配饰。黄色一栏是单向量召回,前 10 个全是裤子。绿色部分是 SetMIR 各活跃 query 的召回:head 3 负责衬衫,head 9 负责连衣裙和半裙,head 5 负责裤子,head 0 负责配饰,head 7 负责鞋;head 1 也是衬衫,因为和 head 3 太像被去重掉(灰色)。
单个用户的召回对比。最上面是用户历史,覆盖衬衫上衣、裤子、连衣裙、鞋、半裙、手袋和配饰。黄色一栏是单向量召回,前 10 个全是裤子。绿色部分是 SetMIR 各活跃 query 的召回:head 3 负责衬衫,head 9 负责连衣裙和半裙,head 5 负责裤子,head 0 负责配饰,head 7 负责鞋;head 1 也是衬衫,因为和 head 3 太像被去重掉(灰色)。

线上 A/B 跑了约一周,下游排序模型不变,SetMIR 作为新召回源加入、总召回名额不变。整体 CTR +0.21%、CVR +3.11%。另外在实验组内部按召回源归因,SetMIR 比并行跑的 I2I 召回(直接拿用户最近的高意向商品当种子去 ANN)CTR 高 44%、CVR 高 51%;两者共用同一套商品向量、索引和单路名额,区别只在查询向量怎么来。

我认为它的局限

第一,离线对比对基线不太公平。SetMIR 用冻结商品塔,而 MIND、DCM(用可微聚类构造兴趣向量的多兴趣模型)等本来是和商品塔联合训练的;DCM 和 KuaiFormer 没有公开代码,是作者按论文复现的;训练步数也不同,基线在 6 万到 10 万步收敛,SetMIR 训到 16 万步。作者自己也说 MIND、DCM 的数字只能看作“在冻结商品空间下这些机制的表现”。所以表里 37.4%、52.9% 这类大差距不宜直接当作方法优劣。

第二,线上证据不完整。论文不报流量比例、样本量和显著性;整体 CTR 只 +0.21%,CVR 却 +3.11%,两者差了 15 倍,论文没有解释。44% 和 51% 这组数是实验组内部按召回源归因得来的,不是随机对照,会受精排偏好和召回源之间相互挤占的影响。

第三,存在标签依赖监督窗口。“没匹配上”只表示未来 3 天没有高意向行为,所以窗口一改,存在分数的含义就跟着变。另外目标最多保留 15 个,而 query 只有 10 个,意味着目标多于 10 个的用户一定有目标分不到 query。论文没有做窗口长度或目标上限的敏感性实验。

一个推荐/搜索团队可以怎么试

  1. 如果已有多兴趣召回,先离线统计 K 个兴趣向量两两的平均余弦,以及每个 query 平均分到多少目标。分布很偏(一两个 query 包揽大部分目标),就说明有坍缩,值得试。
  2. 最小改动是只换训练时的分配方式:argmax 换成匈牙利匹配,K=10 时每个用户只是一个 10×W 的小问题,算法开销可以忽略。看 R@10 和 MRR 的变化。
  3. 再加存在头,线上先不改派发,离线按论文的阈值 0.3 和去重阈值 0.9 回放,算 ANN 调用能省多少、召回损失多少。
  4. 线上 A/B 时,优先看实验组和对照组的整体指标;按召回源归因的指标只作参考,避免把挤占效应当成增益。
  5. 换监督窗口长度(例如 1 天、3 天、7 天)各训一版,看存在分数和活跃路数是否稳定,确认它适合自己的业务节奏。

一句话结论

多兴趣召回的问题不全在模型结构,更在训练时 query 怎么分目标;SetMIR 用一对一匹配加一个“这次存不存在”的分数,把防坍缩和按请求动态派发合成了一件事,改动小,离线消融和线上效果都支持它。

维度评价
问题重要性高。兴趣坍缩和固定派发是多兴趣召回的普遍问题
方法创新性中高。DETR 式集合预测在推荐召回里的直接迁移,存在头用于派发是新用法
实验证据中高。消融干净,有线上 A/B;但基线对比不完全公平,线上缺显著性
工程成本低。约 3M 参数,商品侧和 ANN 索引不用改
是否值得尝试值得,尤其是已有多兴趣召回的团队,先换匹配方式
适用场景用户兴趣分散、品类多的电商和广告召回

读完这篇,你觉得

点一下即可,无需登录;再点一次撤回。只统计匿名次数,不记录你是谁。

讨论

欢迎补充实验细节、复现经验或不同看法。需 GitHub 登录。

正在加载讨论…