2026/11/4 20:54:23

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

生成式推荐:从打分到序列生成的排序模型新范式 从两段式打分到直接生成有序列表推荐排序这件事正在换一套底层逻辑。我做了几年排序模型之前一直用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之间没有语义关联模型很难对没见过的商品做泛化冷启动长尾商品基本无从建模。业界逐渐转向语义IDSemantic 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_items100000, item_dim128, model_namet5-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_outputsencoder_input, labelstarget_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(descendingTrue) 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可以表达排序效果但它对序列内部的关联性不敏感。最好再补充序列级的一致性指标比如类目覆盖率、品牌多样性、加权新颖度这些直接反映生成序列是否符合业务预期。我的做法是离线跑四个维度排序能力NDCGK、覆盖能力多样性、可解释性类目分布一致性和业务约束满足度运营位命中率。四个维度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化”和“规则对账”这两关最容易在后半程炸雷。先花两周把这两关的框架搭好后面项目推进会顺畅很多。生成式推荐是排序模型这个系列里另人振奋的方向但它没有脱离排序模型的基本盘用户行为序列、候选商品、打分、排序、评估。变化的是模型结构和目标函数不变的是对用户需求和业务目标的理解。只要这个核心不丢无论模型怎么演进你都能把它用在实处。