ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

搜索重排进阶指南:从LambdaMART到深度交叉模型

搜索重排进阶指南:从LambdaMART到深度交叉模型 搜索重排Re-ranking这几年在搜索、推荐和广告系统里是性价比极高但最容易被低估的一个环节。很多团队把精力全押在召回和粗排上觉得重排无非是“再把顺序调一调”但真正落地做过一轮就会发现一次合理的重排改造往往能带来好几个百分点的业务指标提升而且不需要重训底层模型。我从传统的LTRLearning to Rank学习排序讲起一路聊到如今主流的深度交叉模型结合真实工程里的踩坑经验把重排这条链路从头到尾拆开来讲。不管你是在做搜索相关度优化还是做推荐流体验治理这篇文章都适合你既讲清楚“为什么需要重排”也给出“到底怎么落地”的具体方案。1. 先看清重排在检索链路里的位置1.1 从召回、粗排、精排到重排的漏斗搜索和推荐系统通常是一个多级漏斗。以搜索为例第一层是召回从几千万或上亿的文档集合里快速筛出几百上千条候选第二层是粗排用轻量模型把候选剪枝到几百条第三层是精排用复杂模型对每条候选做精细打分产出50到200条左右的结果最后一层才是重排。重排器的输入是精排的输出列表输出是最终展示给用户的顺序。它和前几层最大的区别在于召回、粗排、精排本质上是“对单个Item打分”Pointwise思路而重排的核心诉求是“对整条列表做优化”。举个最直观的例子精排模型认为某品牌A的10个商品相关度都极高各自分数也很高如果按精排分直接展示用户看到的是清一色同一品牌不仅视觉疲劳还容易让用户觉得搜索结果被某个商家“包场”了。重排要解决的就是这类单点最优不等于序列最优的问题。我做过的模拟项目X里最初重排规则只有一条把运营配置的置顶内容插到最前面其他按精排分输出。后来加入多样性打散和商业约束之后线上点击率提升了5%左右但真正让我意识到重排价值的是搜索无结果率下降了不少——这说明重排不是简单“调顺序”它直接影响用户对搜索结果整体的满意度。1.2 重排要同时扛起相关性、多样性和业务目标如果你只把重排当成“精排分的微调”那你很快会在线上吃大亏。重排要承接的目标至少有三类而且它们之间经常互相打架。第一类是相关性。重排不能把精排认为“毫不相关”的结果提到前面否则用户点一下发现不对立马流失。第二类是列表多样性。无论是搜索、推荐还是广告同质化内容都会让用户体验快速下滑多样性是为了保底体验。第三类是业务目标。搜索里有商业流量占比要求推荐流里有完播率、停留时长、关注转化等约束广告系统里还有竞价排名和预算分配逻辑。这三类目标的冲突点非常典型比如一个电商搜索品牌词用户意图非常明确如果把不同品牌混进来“提升多样性”相关度反而崩了反过来在泛词搜索下如果一味追求相关度结果可能全是同一个店铺的商品。重排模型或规则必须具备感知上下文的能力而不能只看单条Item的分数。这也是为什么重排从机器学习时代开始就和精排有非常不一样的特征设计思维。1.3 重排和前级排序的本质差异我给不少团队做技术支持时经常被问到精排模型已经很复杂了Transformer、DeepFM都上了为什么还要单独做重排答案在于“输入输出的口径不同”。精排模型面对的是“Query-Item”二元组它看到的特征全部来自这条Item和查询之间的关系以及这条Item自身的统计量。它看不到这条Item的“邻居”是谁。哪怕你在精排模型里加入一些session维度的统计特征它依然只能从统计意义上间接感知上下文无法精确地对“A放第一、B放第二、C放第三”这样的排列组合做整体评估。重排模型站在精排肩膀上输入是已经“精排过”的候选列表它的任务是做最终决策哪些Item该保留哪些Item该降权哪些Item该互换位置。说白了精排回答的是“这条结果值不值得展示”重排回答的是“这组结果以什么顺序展示效果最好”。这两个问题口径完全不同所以不是同一个模型能一并解决的。在工程实现上重排最大的优势是数据量小、候选量少可以做更复杂的计算。精排在毫秒级要对几百上千条Item打分重排只需要对50到200条候选进行排列优化所以哪怕是深度交叉模型在重排阶段也能扛得住性能压力。这也是重排敢于上“交叉”架构的底气。2. 特征怎么喂重排模型和前级模型在原料上的分水岭2.1 三类核心特征Query侧、Item侧、上下文侧任何排序模型都离不开特征但重排的特征体系有自己的特点。我习惯把重排特征分成三组。第一组是Query侧特征包括查询词本身的长度、类目、意图类型、改写结果、联想词等。它描述的是“用户这次到底想找什么”。第二组是Item侧特征包括精排模型输出的相关性分数、点击率预估值、历史CTR/CVR、价格、时效性、品牌、店铺等级、内容类型等。这里最关键的是“精排分”因为重排是基于精排候选集做文章精排分相当于前级模型给每个Item的“简历”。第三组是上下文特征这是重排和精排最不一样的地方。上下文特征包括当前Item在候选列表中的原始位置、当前Item和列表中其他Item的相似度、当前列表本身已经展示过的类目分布、用户在当前session里已经点击过哪些类型的内容、用户所处的页面位置首屏/二屏等。上下文特征解决的核心问题是“位置偏差”和“列表内互相影响”。我举个例子你就懂了。同一件商品在搜索结果第3位和第30位被点击的概率差异极大这不是商品本身质量变了而是位置带来的注意力偏差。如果重排模型不感知位置信息它学出来的分数会严重偏向那些“原本排在前面的Item”导致排序固化。你必须在特征里显式告诉模型这个Item当前在第几位。2.2 位置偏差要不要进特征这是个好问题关于位置特征业内其实有两种做法。第一种是直接把当前位置作为特征喂给模型让模型自己去学位置影响但上线时要小心线上推理时刻的位置特征必须用当前候选的位置并且要配合训练样本的采样或加权策略否则模型会把位置当成品类信号。第二种做法是用“无偏学习”的思路比如在训练时对位置特征做随机化处理或者使用Click模型消除偏差。我在实际项目里更推荐混合方案离线训练时把位置特征保留但结合“Shuffle抽样”让模型不要过度依赖位置线上推断时先按概率采样一组候选顺序再用模型打分迭代多次选最优排列。这样既保留了位置信息又没有把位置当成绝对真理。这里有一个新手很容易踩的大坑特征穿越。训练数据里如果用了“用户点击了该Item之后的行为特征”来预测这次点击那离线指标会好看到爆表线上直接崩盘。重排特征设计时必须严格区分“决策前可得信息”和“决策后才发生的信息”凡是用到用户当前行为序列特征的一定要问一句模型做排序决策时这个信息真的已经存在了吗2.3 特征归一化与工程化重排模型的常用特征动辄几十上百个归一化和缺失值处理比精排更讲究。原因在于重排要做的是“对比排序”模型感知的是特征之间的相对差异。比如价格这个特征有些类目均价几十元有些类目几千元如果你不做类目内归一化模型会被绝对数值带偏。我常用的方案是对于连续特征先做分位归一化或log变换再按类目做标准化对于类目特征做Target Encoding或Embedding对于精排分这类“模型输出特征”不要直接裸用因为不同版本精排模型的分数分布可能变化很大最好对精排分做ranking归一化比如在候选集内转为百分位。特征存储上重排在工程上通常会有专门的“特征快照”——精排模型输出的候选列表以及每个Item的分数一起传给重排服务重排特征计算服务必须在毫秒级内完成几十个特征的拼接。这块如果做得不好重排环节会成为整个链路的性能瓶颈。3. 模型能力演进从LTR到深度交叉模型的路线图3.1 LTR的三大范式Pointwise、Pairwise、Listwise重排模型最早期的形态就是LTR。LTRLearning to Rank是一类“用机器学习方式学习排序”的方法统称它有三个经典范式。Pointwise把排序问题看成回归或分类问题对每个Item单独预测绝对分数比如“该Item是否会被点击”。它简单、训练快但缺陷很明显它不关心Item之间的相对顺序训练目标和排序评价指标NDCG、MRR不一致。Pairwise把问题转化为“二元组谁更靠前”的分类问题。典型代表是RankNet和RankSVM。训练时构造左右配对样本比如“用户点击过的Item应该排在未点击的Item前面”。Pairwise比Pointwise更接近排序本身但它只关心二元组的相对顺序不考虑列表整体位置的影响。Listwise直接对整条列表建模目标是让模型输出的排序和真实排序在列表级别尽量一致。ListMLE、SoftRank、LambdaRank都属于这个方向。Listwise的训练复杂度更高但评价口径和线上排序最贴合因此在实际重排场景里用得最多。这里要特别提一下LambdaRank/LambdaMART。LambdaRank的思路非常巧妙它不直接定义一个可导的损失函数而是给每个样本计算一个“Lambda梯度”这个梯度表示“如果把当前Item往前移动一位NDCG指标会提升多少”。梯度方向和大小直接对应排序指标的变化所以LambdaRank在优化NDCG这类指标时效果远超普通Pointwise回归。LambdaMART则是把LambdaRank的梯度用于GBDT训练用多棵树的加法模型学出排序函数。在深度学习没普及的年代LambdaMART几乎是搜索排序的标配即便到今天它依然是业内评价一个重排基线模型的强有力对照。3.2 深度模型如何逐步“交叉”起来LTR时代的特征交叉基本靠手工和树模型自动分裂来隐式完成。GBDT能自动找特征之间的非线性关系但它无法主动做稠密的高阶特征组合。深度学习来了之后重排模型的能力边界被大幅推开。第一波深度排序模型是“双塔结构”代表作有DSSM和后续的各种双塔变体。双塔把Query和Item分别编码成向量最后用内积算相似度。双塔对并行化友好、在线推理快但它本质上还是Pointwise打分没有做真正的列表级交叉。第二波是精排侧先火起来的“特征交叉模型”比如DeepFM、DCN、xDeepFM等。这些模型把稀疏ID特征和连续特征统一做Embedding再通过FM层、Cross层和Deep层完成二阶甚至高阶特征交互。它们解决的是“单个Item维度上的特征交叉”依然不是列表内的Item间交互。重排真正进入“交叉模型”时代是从把候选列表看作一个序列/集合开始的。早期有团队用Bi-LSTM或Self-Attention对候选序列建模后来Transformer架构全面接管。这类模型的基本思路是把精排TopN候选的每条Item特征序列作为输入经过多层Self-Attention让Item之间互相“看到”对方的信息最后输出每条Item的新分数或直接输出排列顺序。我把这条演进路线抽象一下从“逐点打分”到“两两比较”再到“整体序列建模”。每次跃迁都是在回答一个问题模型到底能看到多大的上下文窗口Pointwise只能看到Item自己Pairwise能看到一对ItemListwise能看到整条列表。深度学习交叉模型的价值就在于此——它让Item之间的关系从“手工特征描述”变成“网络自动交互”。3.3 重排模型的现实选型我知道看到这里很多人会问是不是一定要上Transformer重排才算合格答案取决于业务量级和算力。如果你的候选集在几十条级别、线上耗时预算在20毫秒以内那么一个LambdaMART模型加上简单的MMR多样性打散已经能覆盖大部分需求。它的优点是训练快、易解释、稳定非常适合中小团队快速迭代。如果你的候选集在百条以上、流量规模又足够支撑深度模型训练和在线推理那么可以考虑“双阶段重排”第一阶段用轻量级深度模型比如一个小型DNN对候选做粗筛把200条压到50条第二阶段用Transformer交叉模型对50条做最终排列。这种方案兼顾了效果和性能也是目前业内主流做法。还有一个折中方案是“全局特征 Pointwise深度模型”。具体做法是不做真正的序列建模而是把整个候选列表的统计信息比如类目熵、价格方差、前N条的平均精排分、同品牌占比作为全局特征拼接到每条Item的特征向量里再用DNN打分。这个方案好处是工程简单、推理快缺点是无法精确建模Item之间的两两关系。我自己的经验是如果从来没做过重排先从这条路径起步等团队积累了数据管道和评估体系再逐步升级到真正意义上的交叉模型。4. 实操如何从零搭一套重排训练与推理链路4.1 训练数据怎么构造日志、标签和偏差处理重排模型的训练数据可以复用搜索日志但构造方式比精排更讲究。精排训练数据通常只需要Query, Item, Label三元组重排还需要“列表结构”信息也就是一次搜索请求里哪些Item在同一屏展示过、顺序是什么。我建议的训练样本格式是一个Session一个样本同一个会话里曝光的所有Item按展示位置顺序排列每条Item带用户行为标签和特征向量。标签不要只做“点击/未点击”二分类更好的做法是分级标签未点击、点击、深度交互如停留时长超阈值、下单/加购/关注等不同级别对应不同权重的目标函数。这样重排模型学出来的不只是“点击率最大化”还能兼顾更下游的业务动作。样本质量上要特别小心三个坑。第一个坑是只拿“有曝光”的样本训练会引入选择性偏差。精排觉得差的Item根本没机会曝光重排模型也就永远学不到“这类Item在重排阶段输掉了”。解决办法是做随机化探索在线上保留一定比例流量让重排在精排结果上做随机扰动让低分Item也有机会进入候选。第二个坑是位置偏差前面提过这里再说一次直接用曝光日志训练会让模型过度偏向位置靠前的Item应当用Shuffle日志展示前随机调换顺序或位置特征修正。第三个坑是时间穿越训练数据的时间窗口和特征时间窗口必须严格对齐绝不能用“未来信息”预测当前行为。4.2 训练流程和代码骨架数据切分完成后训练阶段通常分四步特征工程、模型训练、离线评估、模型导出。这里给一个用LambdaMART作为基线的伪代码骨架实战里很多人就是从这个起点起步的。import lightgbm as lgb # train_data: 每个session是一个groupfolds按时间切分 train_lgb lgb.Dataset( X_train, labely_train, groupgroup_train, # 同一个session的所有候选为同一组 feature_namefeature_names ) valid_lgb lgb.Dataset( X_valid, labely_valid, groupgroup_valid, referencetrain_lgb ) params { objective: lambdarank, metric: ndcg, ndcg_eval_at: [1, 5, 10], boosting_type: gbdt, learning_rate: 0.05, num_leaves: 127, min_data_in_leaf: 50, feature_fraction: 0.8, bagging_fraction: 0.8, bagging_freq: 1, verbose: -1 } model lgb.train( params, train_lgb, num_boost_round500, valid_sets[valid_lgb], callbacks[lgb.early_stopping(50), lgb.log_evaluation(10)] ) # 导出结构线上用原生模型加载 model.save_model(rerank_lambdarank.txt)如果你直接拿这份代码去跑大概率能出一个不错的基线。但有两个细节一定要调对第一group参数必须按Session切分否则LightGBM会认为所有样本属于同一个列表NCDG计算完全错乱第二训练集和验证集要按时间顺序切分不要随机切分否则会出现验证集里包含未来样本离线指标虚高。这两个问题我在不少新人代码里都见过报错不一定有但效果就是上不去。再往前一步如果你打算做深度交叉模型训练框架推荐用TensorFlow或PyTorch自定义一个“列表级训练循环”。数据输入是(Batch, MaxSeqLen, FeatureDim)的矩阵BatchSize通常在32到64之间MaxSeqLen是候选数例如50或100Padding部分需要Mask掉。损失函数可以用ListMLE或Softmax交叉熵对模型输出的每个位置logit计算候选列表的排列概率和真实序做交叉熵。4.3 评估指标不能只看点击率重排模型评估是个系统工程单看CTR会把你带偏。我的做法是分层级看。第一层是相关性指标NDCGK、MRR、RecallK。这些指标和精排共用用来确认重排没有“破坏”基础相关度。第二层是体验类指标列表多样性类目熵、品牌熵、内容类型熵、同质化比例比如前10条中同品牌占比、最大连续重复类目数。第三层是业务指标点击率、转化率、GMV、人均浏览深度、无结果率等。离线评估时能直接算的是前三类中的相关性指标和大部分多样性指标业务指标只能通过模拟或小流量AB来验证。我强烈建议任何重排模型上线前都先跑2到4周的“影子模式”就是让重排模型并行打分但不真正影响线上展示把它的排序结果和线上真实排序结果存日志之后离线复盘看模型是不是真的做出了“更好的排列”。影子模式是几乎所有重排项目都必须经历的环节。它不需要你改动线上链路风险极低却能在AB之前暴露大量问题。4.4 在线推理链路怎么接重排模块在在线推理时的上下游关系一般是上游精排模块返回TopN候选和对应精排分重排服务拿到后先做特征拼接再调用模型打分最后经后处理规则输出最终列表。整个过程预算通常在10到30毫秒内。由于候选列表长度有限在线推理不适合逐条请求正确做法是“批量打分”一个请求的所有候选组成一个Batch一次前向计算全部输出。对LambdaMART来说单条打分本身很快对Transformer交叉模型来说一次性把50条候选编码成一个序列做一次前向会比分别打分快一个数量级因为Item之间可以并行计算Attention。后处理规则是重排链路里不可省的一环。即使模型再强你仍然要保留运营硬规则置顶内容、强插广告位、品牌限流、类目最低占比、时间衰减加减权等。模型负责“软性排序”规则负责“硬性约束”。在工程架构上规则引擎要放在模型打分之后、结果输出之前并且要支持运营实时调整不然一个活动改规则都要提工单等发版运营体验会非常差。5. 常见问题与排查技巧实录5.1 线上CTR涨了但GMV却跌了这是重排项目里出现频率最高的问题。现象很典型AB实验中点击率提升业务收入反而下降。第一步排查点是“点击质量”。重排模型如果把更多的曝光给了低价、低毛利、促销型商品用户确实更容易点击但成交金额被拉低了。第二步排查点是“相关性漂移”。有些模型为了提升多样性会把高相关但同质的结果压下去换上来的是低相关但不同类型的结果。用户点了更感兴趣的内容CTR涨但没有产生购买或完播业务转化降。解决思路是重新校准训练目标。CTR这类“点击意图”要和“成交/完播意图”分开建模或多目标加权不要把所有标签揉成一个Label。在实际项目里我会把“下单”这类强业务行为的样本权重提高到“点击”样本的3到5倍并直接以GMV或转化率为业务护栏指标做上线评估。5.2 离线NDCG高线上表现平平离线评估结果和线上不一致是排序类模型最常见的老大难。这里头最容易被忽略的因素是“位置偏差的离线估计偏差”。线上真实曝光位置由当前线上策略决定而离线评估用的是历史日志的曝光位置两者很可能不对齐。你用历史日志评估一个“会改变展示顺序”的新模型天然就存在分布偏移。还有一个元凶是“特征一致性断裂”。离线训练用的精排分来自某一版精排模型线上推理时精排模型已经继续迭代了两边的精排分口径对不上。我的解决方案是在特征存储里同时记录“训练时的模型版本”如果精排模型升级重排模型必须同步用新精排分重新生成训练数据并重新训练。5.3 模型输出结果和规则打起来重排模型经常和运营规则冲突。最常见的是运营要求某条内容必须置顶但模型认为这条Item分数很低两者拼接后出现“规则置顶了、模型特征里却是低分”的矛盾进一步导致模型在后续会话里越来越不信置顶位。这类问题的本质是规则和模型目标不一致。我的处理思路是运营硬规则尽量前置到“候选筛选”而不是全部堆到重排后处理。如果某条内容必须置顶那它就不应该出现在重排模型的候选序列里被“打分”而是在重排输出后强制插入。这样模型打分和规则互不污染。5.4 多样性参数怎么调如果你用的排序方案是“模型打分 多样性后处理”典型是MMR或DPP那绕不开一个话题多样性强度和相关性怎么权衡。MMR公式里的λ表示多样性权重Score_final λ * Sim(Query, Item) - (1 - λ) * max(Item 与已选列表的相似度)λ越大越偏相关λ越小越偏多样性。我的调参经验是先从λ0.7起步观察前10条列表的类目熵和同质比例如果类目熵低于预期每次调0.05往下走同时要按Query类型分组设置不同的λ比如品牌词用高λ更偏相关泛词用低λ更偏多样性而不是全站一个值。DPP行列式点过程更精细它通过核函数定义“相关性惩罚重复项”的联合分布。DPP的优势在于能全局最优地平衡相关性和多样性但核参数的标定需要做离线网格搜索。在候选量不超过100时DPP的推理耗时通常可控是比较推荐的重排后处理方案。5.5 常见问题速查表现象可能原因排查方法CTR涨但GMV跌点击质量下降、低价商品占比过高分流查看点击商品的价格带和毛利结构离线NDCG高但线上无差异位置偏差、特征一致性断裂影子模式对比线上排序日志重排输出和置顶规则冲突规则与模型目标隔离不足硬规则前置到候选筛选阶段多样性低了压不过去λ过低或全局统一λ按Query意图分组设定λ重排接口耗时超标特征拼接慢、模型前向未批处理候选批量打分特征服务预计算模型学到的全是“跟随精排”精排分特征过强、上下文特征不足降低精排分权重增加列表级特征5.6 一个容易被忽略的坑候选集长度变化重排模型在训练时往往固定候选集长度比如K50但线上由于召回波动或上游降级候选条数可能偶尔变成30或80。如果模型对序列长度敏感效果就会抖动。对LambdaMART这类模型候选条数变化影响不大因为它是逐条打分。对Transformer交叉模型就很致命训练时序列长度统一为50线上来了80条Positional Encoding和Attention计算都会出问题。通用做法是线上做一个“候选截断分桶”超过K条时按精排分截断到K条少于K条时Padding到K条保证序列长度与训练一致。这里我吃过一次亏上线后单位小时出现了几万个“长度异常”的请求排查到凌晨才定位到是粗排降级导致候选变长从那以后我在重排服务里加了候选长度的监控报警。6. 最后说点实在的我做了几个重排相关项目后最大的体会是不要试图一步到位上最复杂的交叉模型。重排这个环节的特殊性在于它的上游有召回、粗排、精排下游有业务规则和用户体验任何一个环节的特征分布变化都会传导到重排这里。先跑通LambdaMART基线把特征管道、影子评估、AB实验平台这三件事做扎实比模型本身值钱得多。当你把基线上线并稳定运行几个版本后再引入深度交叉模型你会发现很多问题比如特征穿越、时间一致、位置偏差已经在管道层面解决掉了模型的迭代效率会高非常多。最后一个实用技巧上线重排模型后第一时间监控“重排干预率”——也就是模型实际改动的相对精排顺序的比例。干预率过低说明模型在“抄精排作业”补丁式迭代干预率过高则说明精排和重排的目标可能出现了系统性分歧。找到一个健康的干预率区间整个搜索链路才会处于良性分工的状态。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表