☰
OneTrans-V2:用Transformer统一推荐系统的召回、粗排与精排
2026/11/2 12:32:29 网站建设 项目流程

这两年做推荐系统的人,大概都有一种越来越强烈的感觉:召回、粗排、精排三套模型分兵把守的日子,真的快熬不下去了。模型越来越多、链路越拉越长,一个特征要从精排改到召回,光对齐三套训练管线就够喝一壶。更麻烦的是,精排觉得好的东西,召回在漏斗源头压根没看见;召回辛辛苦苦捞回来的候选,粗排一剪刀下去全剪没了。整个系统不是被模型精度卡住,而是被架构层面的不一致拖死。

所以当我看到字节跳动的 OneTrans-V2 时,第一反应是这条路终于有人走通了。标题已经很直白:保留工业推荐漏斗,用一个 Transformer 统一召回、粗排与精排。先说结论——这个方向的价值不在于"把三个模型变成一个"这么表面的省事,而在于它在不推翻漏斗工程结构的前提下,把三套模型背后的表示学习、目标函数和部署逻辑收拢到一套体系里。这篇文章我想从工业推荐系统为什么长成漏斗、OneTrans-V2 最可能的统一思路、联合训练的关键难点,以及线上落地时算力与效果的权衡这几个角度,把我能看到的细节和经验全部拆开讲。

1. 三模型分治的代价:工业漏斗曾经合理,如今必须收敛

1.1 工业漏斗存在的物理前提:延迟和算力是硬约束

先聊个基础问题:为什么工业推荐一定要有漏斗?道理很简单,候选集太大了。一个日活过亿的 App,物料池少则千万级,多则亿级甚至十亿级。你要在用户请求的几百毫秒内,从这么多item里找出他最可能感兴趣的十几个,纯靠一个复杂模型对全量物料打分,算力上根本不允许。

于是业界形成了固定套路:召回阶段用双塔、向量检索这类轻量模型,把全量物料的候选压缩到千级别;粗排阶段用一个中等复杂度的模型,把千级别进一步砍到百级别;精排阶段才舍得用真正的"重武器",对百来个候选做完整特征建模和精确排序。每一层都在用更贵的算力处理更少的数据,这就是漏斗存在的意义。它不是设计缺陷,而是在物理约束下长出来的最优工程结构。

最典型的量级可以参考这个配置:召回吃全量物料,用ANN(近似最近邻)检索,单请求耗时只要几毫秒;粗排在千级候选上跑一个轻量DNN,给截断提供分数依据;精排则在百级候选上跑完整模型,既有稠密特征又有交叉特征,耗时可能是粗排的十倍以上。三层的算力成本曲线非常陡峭,省哪一层都不行。

1.2 三套模型并行的隐性成本:特征、目标、迭代全方位割裂

漏斗本身没错,错的是传统做法里每一层都独立训练一套模型,这带来的隐性成本在规模化之后非常致命。

第一是特征不一致。精排模型可以用 user 侧、item 侧、context 侧再加上交叉特征去做精细预测,但召回双塔模型为了满足向量检索的高效性,只能用 user 侧和 item 侧的独立特征做内积,交叉特征根本带不进去。结果就是:精排模型明明知道"这个用户在这个时段对这个品类特别敏感",召回模型却感知不到这个信号,候选源头就漏了。

第二是目标不一致。召回阶段优化的往往是召回率、覆盖率,粗排阶段关心的是在精排结果上的 AUC,而精排阶段又在优化点击率、时长、转化率等多元目标。每个模型都觉得自己在正确做事,但串成漏斗之后,目标函数之间的传递误差没人负责。粗排为了让自己的 AUC 好看,可能会把一些精排其实会打高分的 item 提前剪掉,因为粗排的评估指标里根本没有"精排是否满意"这一项。

第三是迭代成本爆炸。一个新增特征,精排加完还要给粗排加,粗排加完又因为特征口径对不上,还得给召回单独设计一套可向量化的表示。三套训练管线、三套配置、三套上线流程,一次全链路特征升级,光是联调就要按周算。这也是很多团队到了中后期,模型涨点越来越难、系统却越来越脆弱的根本原因。

1.3 为什么偏偏是 Transformer 来统一

既然要统一,用什么模型当骨架?答案几乎只能落在 Transformer 上。原因有三点。

Transformer 的特征交互能力够强。推荐系统的核心问题是特征组合,一个用户过去三天频繁点击某个类目,现在又来一个新 item,它和用户历史上哪些行为序列有长距离依赖——这类模式用 self-attention 建模非常自然,比传统 DNN 的有限阶特征交叉有更强的表达能力。

Transformer 的输入形式足够灵活。它可以把用户行为序列、item 属性、context 特征全部序列化成 token,这让"一个模型统一三个阶段"成为可能:召回时把特征序列截断一部分,精排时把全部特征喂进去,模型结构不用变,变的只是输入长度和特征拼版。

Transformer 的表示是共享的。不管是召回、粗排还是精排,本质都是在学"用户和 item 的匹配关系"这个底层表示。用同一套 Transformer 参数作为公共 backbone,每个阶段只加一个轻量头部,就等于把三个模型各自都要学一遍的表示学习任务合并成了一个。这是 OneTrans 这类方案最底层的逻辑支点。

2. OneTrans-V2 的核心设计思路:共享 Transformer 加阶段差异化

2.1 一个模型如何装下三个任务:共享表示加多任务头

既然公开资料没有把 OneTrans-V2 的全部结构细节摊开,这部分我会结合工业界做统一模型的常见做法和我自己的工程经验来做合理拆解,后面所有设计推断都基于"合格从业者最可能采用的可靠方案"这个前提。

最合理的结构是:底层一个完整的 Transformer encoder 作为共享表示层,往上再分出三个轻量任务头(head),分别对应召回、粗排、精排。Transformer 部分承担最重的表示学习——它把用户行为序列、item 属性序列、上下文信息编码成统一的向量表示。三个任务头各自负责自己阶段的预测目标:召回头输出一个可用于向量检索的 user/item embedding,粗排头输出一个快速截断分数,精排头输出精细的点击率或目标价值分。

这样做最直接的好处是:无论哪一层,模型看待用户和 item 的方式完全一致。召回阶段学到的"用户喜欢什么"和精排阶段学到的"用户喜欢什么"不再各说各话,因为它们的底层是同一套参数。三段漏斗共享同一个"世界观",item 从召回到精排的路就不再是三道各不相关的关卡,而是一条连续过筛的流水线。

2.2 阶段感知是怎么实现的:让同一个模型在不同漏斗层级切换角色

这里有一个关键问题:共享参数是好事,但召回和精排的任务性质毕竟不同。召回关心的是"从亿级里捞出最可能感兴趣的千级",它偏好高召回、广覆盖,喜欢抓大放小;精排关心的是"从百级里排出最合适的十几个",它偏好高精度、强区分,必须在很窄的候选空间里精雕细琢。如果三个头共用一个完全相同的 Transformer 输出,任务差异会被抹平。

我推测 OneTrans-V2 的做法是引入阶段感知机制——在输入序列里加入一个明确的"阶段标记"(stage embedding),告诉 Transformer 当前喂进来的样本是用于召回、粗排还是精排。模型学到的就不是一套静态表示,而是三套有交叠又有差异的动态表示:当 stage 是 recall 时,attention 的权重分配会更偏向行为广度;当 stage 是 rank 时,注意力会更聚焦在精细特征和上下文信号上。

这个设计和多任务学习里的 task-specific layer 思路一脉相承,但比硬分三套网络更优雅。阶段标记让模型自己学会在不同角色之间切换权重分配,既保留了共享参数的省心和泛化,又给了每个任务足够的自由度。打个比方:同一个演员演三个角色,服化道(任务头)变了,但他的基本功(Transformer backbone)是同一个,而 stage embedding 就像给他提词的导演,告诉他现在该进入哪个角色的状态。

2.3 漏斗序关系建模:用漏斗本身的顺序当监督信号

OneTrans-V2 标题里最值得琢磨的是"保留工业推荐漏斗"这七个字。为什么统一了还要保留漏斗?因为漏斗不只是一种工程妥协,它本身携带着有价值的序信息:被召回捞回来的 item,理应比没捞回来的 item 在后续阶段里更容易被精排认可;被粗排保留下来的 item,理应比被剪掉的 item 有更高的精排分。

传统三模型分治时,这种漏斗序关系完全没有被利用,每个模型只对自己的输入负责。而 OneTrans-V2 因为三个阶段共享同一个网络结构,就天然可以把序关系当成监督信号注入训练。具体做法可以是这样的:构造训练样本时,对同一个用户,取一批"召回通过但粗排没过"的 item 和"粗排通过且精排高分"的 item,让模型学习拉大它们之间的分数差。

我把这个叫漏斗序一致性约束。它的价值在于把三个阶段的决策过程串成了闭环——不再只是"粗排单选题",而是"粗排要为精排负责"。最终线上效果就是:漏斗的每一层淘汰,都在为下一层提供更高质量的候选,而不是各扫门前雪。

3. 训练方案里的三座大山:样本组织、损失设计、排序一致性

3.1 训练数据怎么组织:联合抽样,但绝不能简单混合

统一模型的训练数据组织和传统单模型完全不同。如果直接把召回、粗排、精排三份样本倒在一起混合训练,你会立刻踩到分布失衡的坑。三份样本的分布差异巨大:召回样本来自亿级候选,绝大多数是负样本,正样本稀疏得可怜;精排样本来自深度筛选后的候选,正样本密度高得多,还带着大量人工规则和多样性约束的痕迹。

我实操时的建议是分阶段采样、联合训练。具体做法是:每个 batch 里按比例从三个阶段的样本池里分别采样,然后喂给同一个模型。这个比例不能拍脑袋定,要按线上流量换算——比如线上精排只处理 100 个候选,召回却处理一亿个,那召回样本的权重如果和精排一样,模型会被召回侧的分布彻底带偏。

还有一个容易忽略的点:不同阶段的特征完备度不一样。训练精排时你手里有完整的交叉特征,但线上召回时这些特征根本来不及算。所以统一模型在训练时一定要做特征掩码(feature masking),随机屏蔽一部分特征,让模型学会在特征缺失的情况下也能输出合理表示。否则线上召回一上线,模型会发现"这输入怎么跟我训练时看到的不一样",效果直接崩。

3.2 损失函数怎么选:pointwise、pairwise 一个都不能少

统一模型的多任务损失设计,本质上是在回答一个问题:召回、粗排、精排三个目标,到底怎么加在一起才算对。

精排阶段本质是一个分类问题——用户会不会点击/转化,用 pointwise 的交叉熵损失最直接,它关心的是单个 item 预估的绝对准确度。但精排最后是排序,绝对分数准不代表顺序准,所以业内通常还会叠加 pairwise 排序损失来优化序关系。

召回和粗排则完全不同。召回面对亿级候选,只看单个样本的 loss 没有意义,必须用 pairwise 甚至 listwise 的方式,让模型学会"这个 item 比那个 item 更值得被召回"。粗排也一样,它的使命是从千级砍到百级,关心的不是绝对分数,而是谁该留在前一百。可以把粗排损失写成对精排分数的近似——粗排头输出的分数要去逼近精排头的输出,做回归或者排序蒸馏。

总损失的大致形态可以写成这样:

L_total = λ_click * L_click(精排头) + λ_pair * L_pair(粗排头,精排结果对齐) + λ_recall * L_recall(召回头,batch内负采样排序) + λ_order * L_order(漏斗序一致性约束)

这里面的 λ 权重值得单独调试,我的经验是:先固定精排损失的权重为 1,再依次调节其他三项。因为精排是最终效果口径,其他任务的权重如果压过精排,模型会为了照顾召回侧的海量负样本而损失精排的区分度,这是统一模型最容易翻车的点。

3.3 排序一致性蒸馏:让粗排头主动对齐精排头

传统漏斗里最大的断层,是粗排的排序逻辑和精排的排序逻辑完全无关。粗排用自己的损失函数训练出一个分数,精排又用完全不同的特征和模型算出另一个分数,两者在漏斗交界处经常打架——粗排留下的 item,精排并不买账;粗排剪掉的 item,反而是精排的高潜力对象。

OneTrans-V2 这类共享骨架方案处理这个问题就顺理成章了。因为粗排头和精排头共享同一个 Transformer,训练时可以直接让粗排头去拟合精排头的排序结果。我之前在类似项目上落地过这个思路:把精排头的输出当作 soft label,让粗排头的输出做回归逼近,损失可以用 KL 散度或者简单的 MSE。这样一来,粗排就不再是"自己认为什么好就留什么",而是"模拟精排认为什么好就留什么"。

这里有个很实用的技巧:蒸馏的时候不要让粗排头完全逼近精排头的绝对分数,而是逼近序关系。因为粗排在线上算力有限,特征也没那么全,完全逼近会造成过拟合,一旦输入特征分布稍有变化,粗排分数就飘了。只对齐序关系,粗排的稳定性和泛化能力会好很多。这也解释了为什么 pairwise 类损失在漏斗一致性建模里比 pointwise 回归更常见。

4. 线上部署的算力权衡:一套参数如何扛住三种流量场景

4.1 召回侧的向量化:把 Transformer 输出变成可检索的 embedding

统一模型落到线上,第一个挑战就是召回阶段它要怎么跑起来。传统双塔召回之所以快,是因为可以把 item 侧 embedding 全部离线算好,灌进 Faiss 这类向量检索引擎,线上只算 user 侧的 query embedding,然后做内积检索。

OneTrans-V2 要想保留这种效率,也必须走同一条路。item 侧走 Transformer 的编码器,离线把所有 item 的表示算好、建好索引;user 侧在线上用同一个 Transformer 算 query 表示,然后去向量库里检索。这里面的关键优化点是:召回阶段的输入序列要尽量短,只保留对检索最有用的行为特征,把精排才需要的精细特征全部挡在召回输入之外。线上延迟每多一毫秒都要命,召回侧绝不能傻乎乎地把精排的全量特征也一起算。

一个值得注意的细节是:统一模型在召回侧输出的 embedding 要兼顾"可分性"和"一致性"。可分性指的是不同 item 的表示要散得开,聚类太紧会导致召回结果单一;一致性指的是这个 embedding 的语义要和精排头的语义在同一个空间里,否则下游粗排头接不住。实际操作中,我一般会对召回 embedding 做一层额外的归一化或线性变换,再送进向量检索引擎,保证两边的尺度一致。

4.2 粗排侧的轻量化运行:牺牲精度换延迟,但不牺牲一致性

粗排卡在召回和精排中间,位置非常尴尬——它要比召回跑得深,又必须比精排跑得快。线上粗排面对的是千级候选,如果每个候选都完整过一遍 Transformer 的每一层 attention,延迟会直接爆炸。

业界的普遍做法是给统一模型准备一个"粗排版本":同样的 Transformer 权重,但只计算前面几层,把后面的层直接截断,或者在粗排输入里只送最核心的特征子集。这本质上是一种训练时不做、部署时做的模型裁剪。因为 Transformer 前几层学到的多是基础特征交互,后面层学到的才是精细语义,粗排用前几层输出做截断,精度损失可控。

我实操中对比过几种裁剪方案,最有性价比的是"层数截断+头部简化"组合:固定 backbone 前 4 层参与粗排计算,粗排头也简化成一层 MLP。这样粗排的算力消耗可以压到精排的十分之一甚至更低,但因为它共享了底层表示和粗排精排一致性蒸馏,截断带来的精度损失远比独立训练一个轻量粗排模型小得多。这就是统一模型在工程上的红利——你不需要为粗排单独训练一套模型,只需要给同一套模型安排一个轻量运行模式。

4.3 精排侧的全量计算:最贵的地方必须花在刀刃上

精排是唯一一个可以"奢侈"使用算力的阶段,因为候选已经缩小到百级。OneTrans-V2 在精排侧可以打开全部能力:完整输入特征、全部 Transformer 层、完整的精排多头。这里才是模型展示真正实力的地方。

精排阶段还承载着一个重要职责:多目标融合。点击率、时长、转化率、完播率等业务目标不可能只用单一损失函数搞定,精排头往往要输出多个目标分数,然后由线上的融合公式决定最终排序。OneTrans-V2 的精排头如果按多任务来设计,就可以让共享 Transformer 学到各目标之间的共性表示,再通过不同目标分支输出各自分数,比传统"每个目标各训一套模型再融合"的方式省掉大量重复计算。

这里要提醒一个容易踩的坑:精排的全量计算模式,在训练时要专门单独 finetune 过。你不能指望召回和粗排的样本训练出来的 backbone,直接拿到精排场景就好用。我建议训练时做阶段隔离——backbone 参数共享更新,但精排头的梯度更新只由精排样本驱动,避免海量召回负样本把精排需要的精细分辨率给磨平了。

4.4 三档算力的工程实现:模式切换而非模型复制

线上工程落地时,最忌讳的做法是把统一模型复制成三份权重文件分别部署,那就又退回到三模型分治了。正确的思路是"一套权重、三种运行模式":部署的时候只发布一份模型文件,在模型服务框架里配置三个打分入口,分别对应召回、粗排、精排模式。每个入口按照该阶段的输入特征拼版、Transformer 层数、输出 head 进行裁剪。

这个方案的运维收益太直观了:模型文件只有一个,特征配置文件只有一个,上线流程从三套变成一套。以往"精排模型升级但召回模型忘记同步特征"这种事故,在统一模型架构下从根上消失了。如果你的线上架构用的是 TensorFlow Serving 或者 Triton,这种模式切换完全可以在请求层通过 model spec 区分,不需要为每个阶段起独立服务。

唯一需要额外投入的是推理引擎的优化。Transformer 的 attention 计算比传统 DNN 重,即便裁剪后也需要注意 batch 内的 padding 策略,以及 GPU 显存占用。我的建议是粗排模式走 CPU 加轻量裁剪,精排模式走 GPU 加全量计算,让异构硬件各司其职。

5. 统一之后的价值边界:收益在哪里,坑又在哪里

5.1 可量化的收益:不只是省事,而是彻底改变迭代方式

盘一下 OneTrans-V2 这类方案真正带来的收益,我会分三层来看。

最表层收益是降低系统复杂度。一份模型、一套特征、一条上线链路,替代原来三份模型、三套特征口径、三条上线流水线。团队从"改一个特征要开三个评审会"变成"改一处全链路生效",研发效能是数量级的提升。

中层收益是漏斗一致性带来的线上效果红利。粗排在向精排对齐之后,剪错 item 的概率大幅下降;召回在共享表示之后,捞回高潜力 item 的概率大幅上升。相当于漏斗每一层都在为下一层输送更高质量的候选,整体排序质量会有一个稳定可预期的提升。

深层收益是模型能力的上限被抬高。传统三模型分治下,召回模型因为算力限制只能用简单结构,它的表示能力就是提不上去。统一模型则可以用精排的算力标准去训练共享 backbone,召回吃的表示红利是原来完全够不着的。也就是说,统一模型不只是把旧能力"合并同类项",而是让每一层都享受到了全模型能力的加成。

5.2 现实中的坑:统一不是银弹,这三处最容易翻车

这个方案踩过的坑我可太有数了,挑三个最典型的讲。

第一,阶段分布差异。召回侧海量负样本和精排侧优质候选的分布差距非常大,如果训练时不做阶段隔离或加权,模型会被大分布样本主导。我见过不止一次:统一模型训练完,精排 AUC 掉了,因为 backbone 被召回负样本"带偏"了,对精细语义的分辨率变差了。解决办法就是前面说的:样本分阶段采样、损失分阶段加权、精排阶段独立更新精排头。

第二,特征完备度不一致。训练时如果总是全特征输入,线上召回阶段特征缺失时模型表现就会很奇怪。这个问题的隐蔽性在于它不一定在离线评估里暴露——离线时你可以把特征补全,在线却真的没有。必须在训练阶段做随机特征掩码,让模型学会在"部分特征可见"的情况下仍然输出合理结果。

第三,模型容量与算力的再平衡。Transformer 统一之后,模型表达能力确实变强了,但线上推理成本也水涨船高。召回侧的向量化检索没问题,粗排侧的层数截断也能顶住,但如果你还想在召回阶段也塞进更多 Transformer 层,算力账单会非常难看。我建议先定线上算力预算,再倒推每层能用多少层数,而不是先训一个大模型再四处裁剪。

5.3 什么样的团队适合迁移:别只看技术,先看组织

最后说点大实话。OneTrans-V2 这类统一漏斗方案,技术上是明显方向,但并不是所有团队都适合立刻迁。做决策之前,建议先对照三个条件。

第一,你的漏斗链路是否已经稳定。如果召回、粗排、精排本身还在频繁调整阶段逻辑,迁移统一模型的成本会被不断放大,不如先把链路形态定住再动手。

第二,你的团队是否有能力维护一套重模型体系。统一模型的核心是个大 Transformer,训练、调优、推理优化都需要有相应经验的人。如果团队还停留在调参侠阶段,贸然上大模型会很痛苦。

第三,你的推荐系统规模是否值得。小流量场景三模型分治的成本还能忍受,统一模型的收益主要体现在规模化和迭代效率上。只有当模型迭代频率高、链路复杂度已经让团队吃不消时,OneTrans-V2 的思路才真正值得投入。

我在实际项目里迁移过类似架构,最深的体会是:统一模型真正难的不是训出来,而是让组织里的每个人都相信"一段链路,一套参数"是桩划算的买卖。等到迁移完成,你回过头看那段为了一个特征在三套模型间反复搬家、为了一个线上事故排查三份特征日志的日子,会很难理解自己当初是怎么熬过来的。这个方向未必每行代码都完美,但往这条路上走,方向一定是对的。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询