@聊聊生成式召回的本质
链接: 聊聊生成式召回的本质 - 知乎
预测下一个 item embedding 的生成式召回,本质还是双塔
这种生成式召回往往是这样建模:输入用户的历史行为序列,保持在item ID这个粒度,可以附带author ID,timestamp这样的side info,预测下一个要产生正行为(比如点击,完播,点赞这种)的item。但是,工业场景item是个开集,#card
其一,ID在不断新增,我连下一个item是谁都不知道,更不可能知道它的ID是什么
其二,item ID的空间理论上是无限大的,不可能按照分类去建模。
所以建模的时候,往往是预测这个item的embedding,而不是它的ID。但还是那个问题,你根本不知道下一个要出现的item是谁,怎么会知道它的embedding长啥样呢?#card
所以即使我给模型这么一个任务,它也完不成这个目标。
模型实际上做到的是另外一件事:在过去发生正向行为的item embedding之间,找到某种滑动平均的中点。
注意,上面的这整个过程,都是user侧自己的计算,直到最后,才用inbatch的softmax和item侧产生交互(由于item是开集,用inbatch softmax而不是softmax)。那我们发现,#card
这套方案里面其实没有设计如何让user和item产生一个很复杂的交叉的过程。
尽管用户序列可以搞得很长,可以用transformer或者其他复杂结构,
到最后user的最终结果和item之间仍然是一个很简单的内积或者距离关系。
而且,在serving的时候,用户侧信息得到是一个中间结果的embedding,怎么拿它来找item呢?#card
那还得借助索引,就还上HNSW,那它本质不就还是双塔吗?
只不过是一个user加了长序列和复杂建模的双塔。
预测下一个cluster的生成式召回,本质是索引
另一种生成式召回的实现方式,最典型的是[[google TIGER]](Recommender Systems with Generative Retrieval),它的逻辑是,#card
- 把用户的历史行为序列都映射到某种token(或者说cluster)上,以cluster ID信息预测下一个要产生正向行为的item对应的cluster ID。
这个建模范式,和上面相比,最显著的变化就是,cluster是个有限集合,所以在预测的时候可以使用softmax做分类。但是,#card
同个cluster内部一般是没有序的,这就和deep retrieval中的一条path,streaming VQ中的一个cluster(这个是特殊情况,有办法使得它内部可以排序,详情见 现在,我们最好的召回模型来了)是一样的情况。
单路召回的出口条数其实还比较少,如果这样的生成式召回直接接召回出口,由于cluster内部无法取舍,很可能出现,选择n个cluster填不满,选择n+1个又超了的情况,而且这个n往往比较小,对聚类质量要求非常高。
所以一般来说,也会在上面流程的出口处,再接一个rerank模型,把很多个cluster的所有item放在一起,再排一遍序,最后拿一部分出口。#card
如果我们把这种两阶段的结构和streaming VQ做一个对比,“生成式”的这一部分实际上承担了索引的部分,有两个显著的特点:
1.可以粗略地对候选缩量;
2.某些item之间没有定义排序关系,需要加一个排序模型才能完整完成召回。
但和上一节略有不同的是,上一节中的生成式范式,在loss设计上和目前主流的召回模型没有区别。而这里的生成式范式,由于按照softmax分类,本质是随机负例,而不是inbatch负例。#card
因为这里softmax让其中一项最大化,其他的就要最小,但这个最小的约束和分布无关,看起来就像是均匀采集的负样本。
相反,inbatch负例和热度是相关的,能达到例如打压热度的效果,其实是更优的。
Semantic ID和协同cluster,谁优谁劣?
不过和TIGER利用多模态token不同,我更建议使用协同的cluster来实现生成式召回,例如嵌入一套VQ的训练框架。#card
这就来到了一个多模态更好,还是协同ID更好的问题。
我理解我们现在很希望多模态特征能替换掉协同ID,那样冷启动就不会是个问题,模型的泛化性会非常好,甚至我们也有可能直接用上LLM那边的模型参数。
但实际上根据我的实践经验,用多模态取代协同ID,费九牛二虎之力,最后也很难成,而且大家已经搞了很多年。反观协同ID,大不了随便加点embedding slice,这边就融化了。原因是什么呢?#card
设想两个模型,一个使用纯粹的多模态作为输入,一个使用现在的协同ID,也就是item ID这种。
用同样的训练数据,同样的损失函数,哪个会把拟合任务完成的更好?
如果在训练样本不充分的情况下,前者可能会更好,但在训练数据充足的情况下,就是后者更好。
为什么?因为协同ID的embedding是可变的,可以随着训练而调整,它的空间不是就比不能变化的多模态特征要更大吗?
(即使多模态特征接了MLP,本质还是没变,到输入层这里还是不可改变)
这就是为什么实践中往往多模态特征可以提升冷启动的效果,对于整体大盘的效果就很有限,能新增做出增量价值,已经是很难能可贵了。
目前的生成式召回都是streaming VQ的子集
以上几个点我们讨论完毕,那就到下面这个最终的结论—— #card
生成式召回其实都是目前streaming VQ框架可覆盖的。
而streaming VQ有很多独特的点,生成式召回反而提供不了。
首先看第一种,预测下一个item embedding的范式,他现在本质还是个双塔,而且实际上是相当于streaming VQ的ranking部分。#card
streaming VQ现在的ranking部分是已经可以支持复杂模型+长序列建模的,
因此这一部分肯定是streaming VQ 1:0领先。
对于第二种,我们就要对比索引阶段。这里又有分支,如果你不同意我的观点,认为多模态特征有增量价值,没关系,#card
- 我在streaming VQ聚类的过程中,把多模态特征都加上,不会付出太多代价,这样就不会输这一分,此处至少打平。
如果是基于协同的生成式范式,那么streaming VQ在索引上和对方有同样的VQ聚类体系,同样的双塔结构。#card
- 但他有对方没有的user ID、profile、地理信息上下文、item侧各种特征等等等等,再加一分。
现在生成式这里唯一占优的是长序列,和长序列建模。但还是那句话,#card
在双塔的前提下,streaming VQ想在单user侧加长序列建模是十分容易的,工程上并不比生成式负担更大,
那就是你加序列我也加,你加user复杂化我也加,你不打了我也不打了,你能赢得了我吗?
其实还不止,生成式召回这里,要保证输入和预测对象的一致,才用cluster ID。#card
- 对于streaming VQ框架来说,没有这个需求,输入的长序列完全可以是原始item序列,那还少承担一个量化误差呢!这就2.5:0领先了。
我们上面讲的第二种生成式使用softmax,而streaming VQ使用inbatch softmax,这个又是谁更好呢?#card
- 根据上面的分析,softmax对应的其实是随机负例,它往往是不如inbatch负例效果好的,所以综合以上,streaming VQ 3:0。
streaming VQ沿用了很多成熟的热度消偏方案,在索引平衡性、即时性、等等问题都有解决方案,#card
- 同样承担索引功能的生成式其实现在还没有,所以比赛的结局最终定格在4:0,VQ完胜。
综上所述,我们得到最终的结论,即,目前存在的生成式召回范式,其实都能被streaming VQ覆盖,后者是前者的超集。