ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

生成式推荐:从打分到序列生成的排序模型新范式

生成式推荐:从打分到序列生成的排序模型新范式

从两段式打分到直接生成有序列表,推荐排序这件事正在换一套底层逻辑。我做了几年排序模型,之前一直用GBDT、DNN、双塔这一路判别式思路,直到最近系统性地梳理生成式推荐,才意识到排序模型的天花板可能不在特征和模型结构上,而在“目标函数到底建模了什么”这件事上。

如果你也跟我一样,每天和CTR、CVR、NDCG这些指标打交道,建议花点时间看看生成式推荐这条线。它跟我们熟悉的排序模型(包括精排、重排)的关系不是替代,而是一次视角升级:从“对候选集打分”变成“直接生成推荐结果序列”。这篇文章我会把我整理的生成式推荐的核心原理、实操细节、落地中的坑,以及跟排序模型的结合方式,一次讲透。

1. 从判别式到生成式:排序模型的又一次范式转移

1.1 排序模型演进路线里,生成式处于什么位置

传统的排序模型走的是清晰的判别式路线:召回阶段捞出来几百个候选,精排阶段给每个候选打一个分,最后按分数排序截断。这个路线的本质是学习一个条件概率 P(y|x),其中 x 是用户和商品的组合特征,y 是“是否点击/是否转化”这类标签。无论是LR、GBDT还是DNN,核心都是这个条件概率的拟合。

到了重排阶段,我们开始考虑列表效应,引进PRM、Seq2Slate这类模型,试图在排序层面做优化。但本质上,重排还是在一个已经生成的候选序列下做“调整”或“选择”。生成式推荐往前走了一步:它把整个推荐过程建模成一个条件序列生成问题,输入是用户的行为序列和其他上下文特征,输出直接是一个有序的商品序列,而不是一组孤立的分数。

这是个本质区别。打分模型学的是每个商品的独立质量,生成模型学的是一整段序列的联合分布。换个角度理解:排序模型的演进,从单点打分 -> 考虑列表内互作用的打分 -> 直接生成有序结果,实际上是一个“从点到列再到序”的转变。生成式推荐就站在了“序”这一端。

1.2 为什么说生成式推荐适合当前的数据和算力环境

一个很现实的问题是:生成式推荐的数据基础从哪里来?答案是我们过去几年积累的海量用户行为序列数据。用户天然按照时间顺序浏览、点击、购买,这些历史行为本身就是一串序列。传统排序模型把历史行为聚合成特征再打分,本质上做了一步“有损压缩”,把序列的结构信息压成几个统计量(比如最近7天点击次数、类目偏好占比)。生成式模型直接用序列建模序列,绕过了这一步特征工程,尽可能保留原始的路径依赖。

算力层面,GPU集群和大模型训练技术的普及让“对序列直接建模”成为可能。我们不需要像几年前那样把序列强行pooling成稠密向量,而是直接用Transforme架构做next-item prediction或者序列生成。排序模型因此第一次可以“站在全局序列的视角”看待用户的每一次交互,而不是只看几个统计量。

从项目定位上看,我把这篇作为排序模型的第四期,因为它不是凭空冒出来的概念,而是排序模型在AI大模型时代自然延伸的方向。你要学它,前面的三期(传统CTR模型、GBDT类模型、序列模型与重排)一个都不该跳过。

1.3 生成式推荐带给排序模型的新能力

生成式推荐给排序模型带来的第一项新能力是“显式的全局一致性”。传统排序是独立打分后排序,模型本身不知道最终列表里前几位分别是什么类目、什么价格带、什么品牌。生成式模型在生成时天然要考虑整个序列的连贯性,哪怕模型没有显式设计约束模块,序列生成的过程本身就在强制建模“已经生成了什么,下一个该生成什么”的依赖。

第二项能力是“覆盖混合目标”。单个分数很难同时表达点击、时长、多样性、新颖度这些混合目标,但在生成式框架里,可以把这些目标统一到同一个序列生成目标中,用条件控制或重新排序的方式动态调节。这让排序模型从“最大化CTR”进化成“生成一个让用户愿意持续消费的序列”。

第三项是“和自然语言能力融合”。商品可能有文本描述,用户可能有搜索词、评论、社交媒体内容。生成式推荐天然支持把这些非结构化信息塞进序列上下文里,不需要额外设计复杂的结构化特征。这点在精排里特别有价值——很多长尾商品的稀疏特征可以被文本信息补强。

2. 生成式推荐的核心技术拆解:把“推荐”变成“序列生成”

2.1 商品如何变成“词”:Token化方案是地基

要让一个推荐系统具备生成能力,第一步是把商品映射成可生成的离散符号,也就是item token。这一步比想象中重要得多,它直接决定了模型的上限。

最朴素的做法是给每个商品分配一个独立ID,像词表里的一个token一样。对于商品数量在百万级的平台,这个做法还勉强可行。一旦商品数到达千万、亿级,全量ID词表会膨胀到无法训练和推理。更重要的是,独立ID之间没有语义关联,模型很难对没见过的商品做泛化,冷启动长尾商品基本无从建模。

业界逐渐转向语义ID(Semantic ID)方案。基本思路是:把商品的文本、图片、类目等特征送进一个编码器,得到稠密向量,然后用量化方法把向量转成离散ID。比较有代表性的做法是TIGER模型中的MoE-QT方法,它把每个商品表示成类似子词token的序列。用这套方案,商品之间只要语义相近,它们的ID序列就有重合或局部相似,模型就有机会通过对已见商品的泛化来推断未见商品的合理位置。

在实际项目中,如果你没有资源训练一套完整的量化器,也可以退而求其次用类目路径分层的ID方案。比如把“3C/手机/旗舰机/某个具体型号”拼成一串离散符号。这样至少序列中前面的部分是共享的,模型能学到类目层级之间的约束。要记住,Token化方案不是一步到位的工程细节,它是生成式推荐的“词表设计”,直接决定你后面能走到哪一步。

2.2 用户侧输入怎么构造:从交互序列到条件上下文

生成式推荐的输入不是一个特征向量,而是序列。最典型的是用户的历史交互序列,即用户最近浏览、点击、购买的按时间排列的商品列表。这个序列里的每个商品都会被换成对应的item token,然后拼接上用户的属性token,比如性别、年龄段、城市等级、会员等级。

另一种重要的输入是上下文token,包括当前时间、星期、是否大促、用户所处地域等。这些上下文会影响用户当前的需求偏好。比如工作日中午用户可能更倾向快餐,周末晚上更倾向聚餐类商品。把上下文作为前缀token拼到序列开头或用专门的控制token注入,可以让生成过程在不同场景下自适应。

需要注意的是,用户序列不是越长越好。传统DIN这类模型还能在超长序列里做attention pooling,生成式模型因为要输出整段token序列,输入太长会显著增加训练和推理成本。实际项目中我建议先对行为序列做采样和下采样,比如只看最近30天、保留最近50到100个行为,并按行为类型加权。这样既保留用户的短期意愿,又不至于让输入长度不可控。

2.3 生成打分还是打分后重排:两种路径怎么选

生成式推荐在排序阶段有两条实现路径。第一条是“全生成式”,模型直接输出一个有序的商品token序列,对应推荐结果。这条路最符合“生成”的原始定义,但挑战也最大:输出空间是组合级的,训练时为了让模型生成一个合理的完整排列,需要精心设计输出顺序和目标函数,而且最后生成的列表未必严格满足业务约束,比如必须包含某个运营位商品。

第二条是“生成式打分”(Generative Scoring),也就是把候选集喂给模型,让模型在已有上下文下计算每个候选token的概率(或者打分),然后依然用传统排序框架来完成最终排序。这条路不算“彻底生成”,但它把生成模型的序列建模能力完美接入了现有排序体系。对线上系统来说,第二条路平滑得多,因为候选集和排序截断逻辑都还在。

我个人的建议是:如果你的项目是全新推荐系统,可以探索全生成式路径;如果已经有成熟的精排和重排体系,先把生成式打分作为一个新的特征或排序信号接入,风险更小、见效更快。全生成式更像是终局状态,需要业务和技术同时成熟后才能稳定运营。

3. 实操记录:从零搭一个轻量生成式排序模块

3.1 环境准备与数据形态

下面是我在项目里实际跑通过的一套轻量方案,基于Hugging Face的T5模型做生成式排序,不依赖超大算力。先说明,这套方案不是为了刷SOTA,而是为了让你完整走一遍生成式推荐的流程,理解每个环节。

环境方面,我使用的是PyTorch 2.x + Transformers 4.3x,显卡用A100或4090都可以,模型参数量控制在1亿以内,训练数据在千万量级。需要安装的核心依赖如下。

pip install torch transformers tensorboard datasets

数据形态上,关键是把用户行为序列转化为模型输入输出对。我定义了一个简单的格式:输入是用户特征 + 历史行为商品ID,输出是目标商品ID或商品ID序列。

# 数据样本示例 { "user_features": [101, 32, 1, 2024], # 性别/年龄/城市等级/会员等级 "history_items": [23012, 23015, 23321, 23988], # 历史点击商品ID(已转token) "target_items": [24551, 23990], # 目标生成商品序列(点击序列或人工排序) "candidate_items": [24551, 23990, 23011, 27660] # 候选集商品ID }

这里的关键设计思想是:把推荐的目标从“预测一个点”改成“生成一个序列”。target_items是用户在之后一段时间的交互序列,或者是经过重排序后的人工优质列表。模型的任务是看输入序列,生成输出序列。这比单纯做Next Item Prediction信息量更大。

3.2 模型结构与Token化配置

我的做法是直接用T5的encoder-decoder结构,encoder吃用户侧信息,decoder负责生成目标商品序列。因为T5本身是文本模型,必须做一层映射:把商品ID走一个独立的Embedding层,序列拼起来之后通过一个投影层适配到T5的隐层空间。

这一段是初始化模型的核心代码:

import torch.nn as nn from transformers import T5ForConditionalGeneration class GenerativeRanker(nn.Module): def __init__(self, num_items=100000, item_dim=128, model_name="t5-small"): super().__init__() # item embedding: 把商品ID映射成连续向量 self.item_emb = nn.Embedding(num_items, item_dim) # 线性投影: 把item向量投影到T5的语言模型空间 self.proj_in = nn.Linear(item_dim, 512) # 解码输出端再把T5隐层映射回item空间 self.proj_out = nn.Linear(512, item_dim) self.t5 = T5ForConditionalGeneration.from_pretrained(model_name) def forward(self, item_input_ids, user_emb, target_ids): # item_input_ids: [batch, seq_len] 用户历史商品ID # user_emb: [batch, user_dim] 用户特征拼接embedding items_vec = self.item_emb(item_input_ids) # [batch, seq_len, item_dim] items_vec = self.proj_in(items_vec) # 给encoder添加用户特征向量作为前缀 user_prefix = user_emb.unsqueeze(1).repeat(1, items_vec.size(1), 1) encoder_input = items_vec + user_prefix # encoder/decoder共用t5主模型, 用投影层做维度对齐 # 这里简化了位置编码处理 outputs = self.t5(encoder_outputs=encoder_input, labels=target_ids) return outputs.loss

这只是示意代码,实际工程里要注意三个细节。第一,位置编码要让模型能区分历史序列、用户特征、上下文token,不能全用同一个位置索引。第二,输入输出长度都有限制,T5这类模型有max_length上限,需要对用户序列做截断或采样。第三,item_emb要单独做初始化,避免初始embedding与T5文本embedding空间差异过大导致收敛困难。

现实中很多人直接尝试把商品ID塞进T5的文本词表里,效果通常很差。原因很清楚:商品ID是纯离散符号,没有语言含义,T5预训练好的位置假设和参数初始都无法直接迁移。通过独立id的projection层做空间变换,再跟T5微调,实践中收敛快得多。

3.3 训练目标:不只是Next Item Prediction

很多人以为生成式推荐就是Next Item Prediction,把用户下一个点击的商品当作target训练。这是最直觉的做法,但有明显缺陷:它只建模了“下一个商品是什么”的单步分布,没有建模“整个列表应该长什么样”。

更接近排序目标的训练方式,是直接用带顺序的序列作为target,比如用户在一个session里连续点击的多个商品按时间排列作为目标序列。训练目标变为给定历史,最大化整个序列的生成概率。这样相当于把下一阶段的多个行为一起预测,模型学到的不只是下一个商品,而是未来几步的联合分布,在线重排时更能输出连贯的整体列表。

实践中还可以加一个排列损失。比如模型生成目标序列后,再计算序列两两之间的相对顺序损失。这个思路在华为(或学术界某团队)的一些工作里被验证过,能明显改善NDCG这种跟顺序强相关的指标。代码层面实现起来也不复杂,在生成序列上做一个成对的margin loss即可。

loss = generation_loss + alpha * ranking_loss

其中ranking_loss基于模型预测的序列内两两商品的score差值构造。alpha我试下来在0.1到0.5之间比较稳,太大会让生成任务学习不稳,太小则对排序指标提升有限。

3.4 推理与重排序:生成结果如何落地到线上

生成式模型直接输出的序列可能跟线上系统的业务规则冲突,比如必须包含某些运营位商品、不能连续出现同品牌、类目要有分布约束。因此纯生成结果直接上线风险很大。我见过比较成熟的做法是生成式作为“第一轮初排”,后面再接一个可配置的重排序层。

推理阶段,模型对候选集里的每个商品计算生成概率并排序,这样候选顺序可以动态调整,同时保留传统排序兜底。或者直接beam search生成一个序列,再用规则或浅层模型去修整。两种方式的取舍在于:前者更稳但生成式建模的优势发挥有限,后者更能体现序列生成能力但更容易产生业务不可控结果。

我的实操建议是上线初期限定为生成概率打分,后续有了充分的在线实验数据再逐步加大beam search生成的比重。稳字当头,推荐系统不是秀技术的场景,用户对推荐质量的波动非常敏感。

下面是一个完整的推理封装:

import torch def rank_with_generative_model(model, user_features, history_items, candidates): model.eval() with torch.no_grad(): # 处理用户序列: 输入拼接 input_ids = [user_features + history_items] # 简化为单样本 input_tensor = torch.tensor([input_ids]) # 候选集都作为需要打分的对象 candidate_tensor = torch.tensor([candidates]) # 生成每个候选的概率(简化为模型encoder输出后计算logits) # 实际会用decoder逐步解码计算概率 logits = model(input_tensor, candidate_tensor) scores = torch.nn.functional.log_softmax(logits, dim=-1) # 对每个候选取对应token位置的概率 candidate_scores = scores[0, candidates] # 排序 ranked_idx = candidate_scores.argsort(descending=True) ranked_items = [candidates[i] for i in ranked_idx] return ranked_items

真实线上部署需要考虑batch化和缓存优化,否则这部分会变成整个链路里的性能瓶颈。我会在第4节仔细讲性能问题。

4. 落地中最容易踩的坑:从训练到上线的四个大坑

4.1 坑一:商品ID稀疏冷启动怎么破

生成式推荐比传统排序模型的商品ID信息用得更重,因为模型学的是ID序列的生成规律。一个商品如果行为数据极少,它在模型里基本就是个几乎没训练过的token,生成概率自然会偏低。传统排序模型至少还能靠类别特征、文本特征打底,生成式模型对冷启动商品简直是灾难。

我的解法是两路并进。第一路是语义ID方案,用商品的文本描述和图片特征生成商品表征再量化,这样冷启动商品也有可泛化的语义位置。第二路是在训练时对低频商品的embedding做约束,比如用正则项让低频商品的embedding初始化靠近同类高频商品的平均向量。实测下来长尾覆盖率能提升20%以上。

我在一个业务数据上对比过:纯ID方案的新品召回率只有3.5%,换成语义ID方案后上升到11.2%,效果差距非常大。虽然语义ID训练量化器也要花资源,但这笔投入在生成式推荐里是值得的。

4.2 坑二:推理延迟扛不住

生成式模型天生推理重。传统精排也就是几层DNN跑几百个候选打分,生成式模型做个beam search可能就翻了几十上百倍计算量。线上系统根本承受不住秒级的推理延迟。

两招组合拳我亲测有效。第一招是限制decoder生成步数,把所有候选一次性输入,encoder端计算共享,只对需要排序的位置做生成打分,而不是完整自回归生成一个长序列。第二招是上模型量化,用INT8或INT4量化后显存和速度都有明显收益。

更进一步的方案是知识蒸馏:用完整生成模型在离线批量产出排序结果,蒸馏给一个小的双塔或单塔打分模型。在线用轻量模型,离线用大模型来不断刷新目标。这套方案保证线上延迟可控,同时离线大模型的效果能持续传导到线上。

4.3 坑三:评估指标怎么定才算合理

生成式推荐上线前,离线评估指标会让人很纠结。传统离线指标比如AUC、GAUC都聚焦于单点预测,生成式模型更关心序列整体的质量。使用NDCG可以表达排序效果,但它对序列内部的关联性不敏感。最好再补充序列级的一致性指标,比如类目覆盖率、品牌多样性、加权新颖度,这些直接反映生成序列是否符合业务预期。

我的做法是离线跑四个维度:排序能力(NDCG@K)、覆盖能力(多样性)、可解释性(类目分布一致性)和业务约束满足度(运营位命中率)。四个维度leaderboard同时看,不只看一个指标。很多生成式模型在排序指标上未必超过传统模型,但在多样性、长尾覆盖上优势明显,要结合业务目标判断。

在线评估更复杂,因为生成式模型修改了整个列表,不能只看CTR。用户接受率(用户对推荐列表的整体接纳程度)是一个综合指标。我倾向于把在线指标拆解成短期消费指标(点击率、时长)和长期体验指标(一周留存、抱怨反馈率)。短期的提升如果以牺牲长期用户信任为代价,这个生成式模型就不能上线。

4.4 坑四:生成结果的可控性差

生成式模型在训练数据分布内表现良好,一旦遇到分布外的情况容易出现“失控推荐”。比如用户历史行为里大量点击高价商品,模型可能认为用户喜欢高价,把所有后续推荐都生成高价值商品,忽略价格敏感用户可能的促销需求。这实际上是生成模型对训练分布把握过拟合的表现。

对策一是在推理时引入可控因子,比如对生成概率做加权,把价格带、类目分布的先验信息注入到每一步生成中。对策二是在训练数据里刻意保留多样性的样本,不要只保留点击数据,曝光未点击、负反馈这类样本也要参与训练。对策三是设置一个“安全网”,凡是生成结果里出现明显违背基本规则的商品组合,直接回退到传统排序结果。

可控性问题如果处理不好,轻则指标波动,重则用户投诉。我见过一个生成式模型把同品牌雷同商品连续推了五个,用户直接去客服投诉推荐系统“疯了”。这种事件一次就能让管理层把整个项目砍掉,宁可慢也不要冒进。

5. 学习建议:生成式推荐的知识地图与入手路径

5.1 打基础:机器学习底子不能虚

看到生成式推荐这个方向,很多人第一反应是去追最新论文,结果被复杂的数学推导劝退。我周围做推荐算法的同事,能在这个方向上持续推进的,机器学习基础都相当扎实。周志华老师的《机器学习》这类经典教材,其中的概率模型、判别式与生成式模型对比、序列建模思想等章节,读透彻比刷十篇论文更管用。

《图解机器学习算法》这类偏实践的书可以用来快速建立直观理解,比如序列模型、注意力机制、采样生成的直观图示,即使不看公式也能明白模型在做什么。尤其是Transformer系列,看懂它的生成过程是理解下一步的关键。

给在校生一个直接建议:先手动推导一遍softmax和cross-entropy在训练和生成时的差异,再看tensor2tensor或transformers库里的生成函数源码。把这两步做完,再去看生成式推荐论文就不慌了。

5.2 动手路径:从序列推荐复现开始

我建议的动手路线,第一步不是直接挑战大规模生成式推荐系统,而是从序列推荐(Sequential Recommendation)入手。推荐领域知名的SASRec、BERT4Rec都可以在单个GPU上几天内跑完,而且是生成式推荐的前身。把这两个模型的pytorch实现调通,对输入构造、注意力掩码、序列预测目标都会有直接体感。

第二步,把候选生成改成简单的生成式打分。在SASRec的基础上,把输出层从打分改成带beam search的生成,尝试控制生成长度和多样性。这个过程能逼你把数据改造、模型改造、评估改造三件事完整走一遍。走完这一遍,再去看生成式排序、TIGER这些系统,基本上能看明白大半。

第三步才是彻底的生成式推荐实现。备好海量用户行为序列,选好一个语义ID方案,用T5或LLaMA做序列生成。这个阶段才能真正接触性能优化的坑。

5.3 面试与考核中怎么聊生成式推荐

现在算法岗面试几乎必考生成式推荐的基本认知。面试官常追问的几个点包括:生成式推荐与判别式排序的模型目标差异在哪里;商品ID与语义ID方案怎么选;线上延迟怎么优化;评估指标与传统排序有何不同。这几个问题如果你能结合上面的实操理解去讲,比空谈几个论文名强得多。

期末或考核场景下,生成式推荐常作为大作业或项目主题出现。多数人只会调库,讲不清楚设计取舍。能说清楚“为什么不用Seq2Seq文本翻译那套直接套用,而需要做商品侧词表设计”,“为什么生成序列不能直接上线当排序结果用”,这两点说明你真的理解了这个方向的关键矛盾。

我的体感是,生成式推荐还没有成为完全统一的工业标准,目前很多团队都在探索和验证阶段。如果你能在这个阶段建立了系统性的理解和实操经验,等到这套技术逐步成熟时,你已经有积累优势了。这是少有的“越早投入越有回报”的方向。

5.4 小经验:用一张流程表做项目自查

最后分享一个小技巧,我在设计生成式推荐项目时会画一张自查流程表,用来判断方案是否走偏。表格列一下最关键的五个环节,每个环节都写明验收标准。每次项目进展前先过一遍,能省去很多后面返工的麻烦。

环节关键问题验收标准
数据构建是否包含负样本和多样序列离线序列覆盖率达标
Token化商品映射是否有语义关联冷启动商品有合理ID分布
模型训练生成损失和排序损失是否平衡离线NDCG和多样性双双不降
在线推理延迟是否在预算内单请求P99达标
规则对账业务约束是否满足违规序列比例降到阈值以下

这五关每一关我都踩过,尤其是“Token化”和“规则对账”这两关,最容易在后半程炸雷。先花两周把这两关的框架搭好,后面项目推进会顺畅很多。

生成式推荐是排序模型这个系列里另人振奋的方向,但它没有脱离排序模型的基本盘:用户行为序列、候选商品、打分、排序、评估。变化的是模型结构和目标函数,不变的是对用户需求和业务目标的理解。只要这个核心不丢,无论模型怎么演进,你都能把它用在实处。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进