ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

搜索广告深度排序实战:LTR、双塔模型与工程落地

搜索广告深度排序实战:LTR、双塔模型与工程落地 简介一份面向搜索广告算法工程师和数据分析师的PDF文档收录了钟小勇撰写的《基于深度学习的搜索广告排序应用》全文。内容聚焦广告点击率CTR预测完整介绍了特征提取、模型训练到排序应用的基本流程并重点提出了卷积神经网络CNN与长短时记忆网络LSTM相结合的混合模型先用CNN自动抽取广告中的高影响力特征再借助LSTM捕捉用户行为的长期时序依赖从而提升CTR预估算法的准确性。文档同时对比了逻辑回归、决策树等传统机器学习方法并介绍了FNN、WideDeep、PNN等业界模型还提供了LSTM层数、隐藏层节点数、学习率等参数对AUC指标影响的实验对比结果为实际广告排序策略调优给出了具体的实验参考。资源为1个PDF文件大小2.73MB结构紧凑、要点明确目前已有122人学习适合快速了解深度学习在搜索广告排序领域的落地思路。1. 搜索广告排序把点击率、转化率和出价揉进同一个排序分搜索广告排序和自然搜索排序有个根本差别排序对象不是“对用户最有用的一条结果”而是“让用户和广告主都愿意接受的一条商业结果”。做这个方向的技术落地核心要回答的问题是——当候选广告摆了一排凭什么这一条排在另一条前面。传统做法用逻辑回归或 GBDT 做点击率预测再乘出价得到排序分但特征交叉、用户序列建模、多目标联合优化这些环节很快会碰到天花板。深度学习进入这块之后解决的是在同一个模型里同时建模点击率、转化率和相关性信号。这篇笔记按工程链路来写样本、特征、模型、评估、上线以及那些让离线指标很好看、线上收入却纹丝不动的坑。适合搜索广告方向刚入门的算法工程师也适合准备从传统机器学习切到深度排序的从业者。2. 从 LTR 到深度排序广告排序的排序分不是纯 CTR2.1 广告排序的三方博弈出价、相关性和用户体验搜索广告的排序分在业界通常是一个融合公式常见做法是排序分 f(预估点击率, 预估转化率, 广告主出价, 相关性)注意这里不是简单乘起来。如果只按“点击率 × 出价”排广告主可以靠提高出价把低相关广告顶到前面用户点一次就离开短期平台赚到钱长期用户开始无视广告位整体收入反而下降。所以排序分里必须有一个相关性信号或质量分做约束把“用户是否愿意接受”和“广告主是否愿意付钱”平衡起来。深度学习在这个问题里的位置不是替代出价策略而是把“预估点击率”和“预估转化率”这两件事做得更准。传统 LR 需要人工去做特征交叉GBDT 能做有限的高阶交叉但泛化到新特征组合时偏保守。深度学习用 embedding 把类别特征映射到稠密向量再通过网络结构自动做特征组合这个能力在广告这种“查询短、候选多、特征维度高”的场景里非常合适。我在实际项目中常见的做法是保留一个 GBDT 模型做基线深度学习模型作为主排序模型上线后GBDT 只用于特征重要性分析和异常排查。不是因为 GBDT 不够好而是深度模型在用户历史行为序列和广告侧的精细化表达上确实有结构性的优势。2.2 Pointwise、Pairwise、Listwise三种 LTR 范式在深度模型里的取舍LTR 本身是个老领域进入深度学习时代后三个范式的选择直接影响损失函数和模型结构范式学习目标损失函数示例深度模型适配度典型问题Pointwise预测单个样本的点击/转化概率交叉熵、MSE直接套 CTR 模型只关心绝对分数不关心排序位置Pairwise比较两个候选的相对顺序RankNet、LambdaRank容易在深度模型上实现对头部排序敏感忽略整体分布Listwise优化整个候选列表的排序指标SoftRank、ApproxNDCG实现复杂训练不稳定梯度和 NDCG 不可导的近似问题我在实际项目中第一版通常用 Pointwise 的交叉熵因为它和已有的 CTR 模型无缝衔接线上服务也不用改造。等模型稳定之后再在 Pointwise 基础上叠加 Pairwise 损失做微调这样能拿到“绝对概率准确 相对顺序正确”两个好处。Listwise 在搜索广告场景里我一般不建议第一版就上它对采样策略和候选集构造非常敏感线上效果波动大。Pairwise 在深度模型里最常用的实现是 RankNet 的思路两个候选共享同一个网络取 logit 差值再用 sigmoid 映射成“排序正确的概率”损失函数就是二元交叉熵。这个实现简单但要注意样本对怎么构造不能所有正负样本都配对会产生海量组合。2.3 深度模型选型双塔、DIN、DCN 在广告排序里的分工深度学习在搜索广告排序里不是只有一种网络结构。我从实际工程角度把常用模型分成三类第一类是双塔模型用户塔、广告塔各自编码最后做内积或点积。它的最大优点是线上推断快用户向量和广告向量可以提前算好缓存排序时只做向量检索。缺点是用户和广告的特征交互被限制在最后一层内积表达能力有限适合做召回或粗排。第二类是 DIN 这类带注意力机制的模型核心是对用户历史行为序列做加权求和权重由当前候选广告和每条历史行为的相似度决定。它解决的是“用户历史点击过很多类商品但今天搜的是另一类历史行为不能一概而论”的问题。在搜索广告场景里query 本身就是很强的意图信号DIN 的这个注意力计算通常放在 query 和广告侧特征的交叉上。第三类是 DCN、DeepFM 这类显式特征交叉模型适合特征体系里大量存在“广告类目 × 用户年龄段”这种强业务交叉的场景。它们在 Wide 侧做低阶交叉Deep 侧做高阶隐式交叉训练稳定调参空间清晰。实际落地时不需要一上来就上最复杂的结构。我通常先上双塔加 DIN 的注意力模块线上效果稳定后再考虑加 DCN 的交叉层。模型结构每复杂一层训练耗时、线上延迟、特征上线成本都会跟着涨搜索广告排序毕竟是要压 RTB 延迟的不能为了模型好看牺牲链路稳定性。3. 样本与特征深度排序模型能吃下什么样的数据3.1 样本构造曝光日志、点击回传和延迟转化深度排序模型的训练样本来自搜索日志。最简单的一条样本是“某次搜索请求下某条广告曝光给了用户用户点没点”。正样本是曝光且点击的广告负样本是曝光但未点击的广告。这里有个关键点负样本不是全量曝光都拿。一条 query 下可能有几十个广告曝光用户只点了一个剩下全部是负样本。如果全部拿进去正负比可能到 1:50 甚至更低。常见做法是控制负样本采样比例比如只保留 10% 的未点击曝光并给这些样本在损失函数里加权重。我一般会把采样率设成超参去调20%、10%、5% 都试一遍观察离线 AUC 和线上点击率分布的变化。另一个坑是转化回传延迟。用户点了广告之后可能隔了几天才下单如果按当天日志标准去标记样本转化会被漏掉。常见做法是引入“回看窗口”比如 30 天转化归因训练时只取发生在回看窗口内的转化。窗口越长样本新鲜度越差窗口越短转化归因越不全。我一般用 7 天做初版跑通后再看转化延迟分布曲线去调。下面是一段常见的样本构造逻辑用 PySpark 处理曝光和点击两张表from pyspark.sql import functions as F # 曝光日志一次曝光一条记录 expo spark.table(search_ad_expo) \ .filter(F.col(dt) 2025-01-01) \ .select( request_id, ad_id, user_id, query, expo_time, ad_rank ) # 点击日志一次点击一条记录 click spark.table(search_ad_click) \ .filter(F.col(dt) 2025-01-01) \ .select(request_id, ad_id, click_time) \ .dropDuplicates([request_id, ad_id]) # 左连接得到是否点击 train_df expo.join( click, on[request_id, ad_id], howleft ).withColumn( is_click, F.when(F.col(click_time).isNotNull(), 1).otherwise(0) ) # 负样本采样点击样本全保留未点击样本保留 rate 比例 train_df train_df.withColumn( sample_flag, F.when(F.col(is_click) 1, 1) .otherwise(F.rand() 0.1) ) train_df train_df.filter(F.col(sample_flag) 1)这段逻辑有四个参数值得说明。第一是回看窗口如果要做 7 天转化归因点击日志要回刷过去 7 天的数据不能只取当天。第二是负样本采样率这里rand() 0.1表示保留 10% 的未点击样本这个值在训练时对应损失函数的样本权重调整。第三是去重逻辑一次曝光只保留一次点击防止重复计数。第四是 join 键request_id ad_id在搜索广告场景里能唯一定位一次曝光不能只用 ad_id。3.2 特征体系类别特征用 embedding、统计特征要平滑搜索广告排序的特征体系大体分四类用户特征、查询特征、广告特征、上下文特征。用户特征包括性别年龄、历史点击类目分布、近 7 天点击序列查询特征是 query 本身以及经过分词之后的 term 序列广告特征是广告主类目、创意文案、出价、历史 CTR上下文特征是设备、时段、页面位置。类别特征在深度学习里直接映射到 embedding 表。数值特征要注意量纲差异广告出价可能是几元钱历史点击率是 0.01 到 0.1 的小数如果不做归一化梯度会被大数值特征带走。常见做法是分箱后映射成 embedding或者做 log1p 变换再标准化。统计特征里最容易出问题的是冷启动和稀疏桶的 CTR 统计。一个广告只有 20 次曝光、3 次点击CTR 算出来是 0.15但置信度极低直接用这个值进模型会让模型学出离谱的权重。常用做法是加平滑我用的是 Beta 分布的先验平滑import numpy as np def smooth_ctr(clicks, shows, alpha1.0, beta10.0): 对广告历史点击率做贝叶斯平滑 clicks: 历史点击数 shows: 历史曝光数 alpha/beta: Beta 先验参数相当于虚拟点击和虚拟曝光 prior_ctr alpha / (alpha beta) # 平滑后的 CTR 相当于先验和观测的加权平均 smoothed (clicks alpha * prior_ctr) / (shows alpha) return smoothed # 示例曝光 20 次点击 3 次 ctr smooth_ctr(clicks3, shows20, alpha1.0, beta10.0) print(ctr)这里的alpha和beta有两个调参方向。如果按 Beta(1,10) 取先验相当于我们预设一个广告初始 CTR 是 0.09这个值可以用全量广告的平均 CTR 代替而不是拍脑袋。其次alpha beta的值决定平滑强度这个值越大观测数据要越多才能把平滑后的 CTR 拉离先验。我一般先统计全量数据的 CTR 分布然后用矩估计去拟合 Beta 参数而不是手动调。3.3 特征在线一致性训练和线上拿到的特征必须同源深度排序模型在离线训练时用的特征和线上实时请求时拿到的特征经常对不上这是搜索广告排序里最隐蔽的坑之一。典型的例子是“实时点击率特征”。离线训练时我们用的是广告在历史一段时间的 CTR 统计线上排序时特征服务可能取的是过去 10 分钟的快照。两边口径不一致模型线上表现会明显低于离线预期。更严重的是一些基于窗口的特征比如“用户最近点击的广告序列”如果线上实时算但离线是 T1 回刷用户行为序列的分布都会漂移。我在项目里要求所有特征必须有“特征版本”。具体做法是离线训练时固定一个特征快照版本线上特征服务输出同一个版本号模型上线前用线上特征服务对历史日志做回放比较训练特征和线上特征的分布差异。这个回放检查哪怕只是算一下均值和覆盖率也能挡掉大部分上线事故。4. 训练与评估深度排序模型的网络结构、损失函数和离线验证4.1 损失函数设计从点级交叉熵到 Pairwise 辅助损失第一版模型通常直接预测点击概率用交叉熵。先说为什么不用 MSE。点击率是 0 和 1 的伯努利事件交叉熵对应的是伯努利分布的极大似然估计梯度在预测概率接近 0 或 1 时会自动变小不容易被极端样本带偏。MSE 在这个场景下梯度不够稳定预测值接近 1 的负样本会产生很大的梯度。直接用双塔模型输出点击概率时损失写法如下import torch import torch.nn.functional as F def ce_loss(logit, label): 双塔模型输出 logit 后计算交叉熵 logit: (batch_size, 1) label: (batch_size, 1) 取值 0 或 1 prob torch.sigmoid(logit) # clamp 防止 log(0) eps 1e-7 prob torch.clamp(prob, eps, 1 - eps) loss - (label * torch.log(prob) (1 - label) * torch.log(1 - prob)) return loss.mean() # 模拟一个 batch batch_logit torch.randn(8, 1) batch_label torch.randint(0, 2, (8, 1)).float() loss ce_loss(batch_logit, batch_label)如果要在点击率基础上加转化率目标有两种做法。一种是多塔输出一个塔出 pCTR一个塔出 pCVR两个损失加权求和。另一种是直接建模“点击且转化”的概率把转化目标的样本权重降低。我一般优先用多塔输出因为可以单独看两个目标的 AUC排查时更容易定位是哪一边出了问题。Pairwise 辅助损失的做法是构造正负样本对让正样本的 logit 大于负样本的 logit。实现上不需要改模型结构只需要在输出层多算一次差值损失。常用的是 RankNet 的二元交叉熵形式def ranknet_loss(pos_logit, neg_logit): pos_logit: 正样本点击模型的 logit neg_logit: 负样本未点击模型的 logit diff pos_logit - neg_logit loss F.softplus(-diff) # 等价于 log(1 exp(-diff)) return loss.mean()RankNet 的损失只关心差异的正负不关心差异的大小。这意味着它不会强迫模型把正样本的分数拉到无穷大比较温和。LambdaRank 在此基础上会对差异大的样本对加重梯度但实施起来要额外计算交换位置的 NDCG 差第一版不建议上。4.2 双塔模型结构写一个可跑的排序模型搜索广告场景下我默认用双塔结构。用户侧塔和广告侧塔都不用太重每塔两到三层的 MLP 就够。用户塔输入用户特征和 query 特征广告塔输入广告特征。两个塔输出的向量维度保持一致内积得到 logit。下面是一个简洁可跑的双塔实现import torch import torch.nn as nn class DsenetAdRanker(nn.Module): def __init__(self, num_user_feats, num_ad_feats, emb_dim16, hidden_dim64): super().__init__() # 类别特征 embedding 层每个特征一张表这里简化为统一维度 self.user_emb nn.Embedding(num_user_feats, emb_dim) self.ad_emb nn.Embedding(num_ad_feats, emb_dim) # 用户塔 MLP self.user_tower nn.Sequential( nn.Linear(emb_dim, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, emb_dim), nn.ReLU() ) # 广告塔 MLP self.ad_tower nn.Sequential( nn.Linear(emb_dim, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, emb_dim), nn.ReLU() ) def forward(self, user_ids, ad_ids): u_vec self.user_emb(user_ids).mean(dim1) # 用户特征 id 序列取平均 a_vec self.ad_emb(ad_ids).mean(dim1) u_vec self.user_tower(u_vec) a_vec self.ad_tower(a_vec) logit (u_vec * a_vec).sum(dim-1, keepdimTrue) return logit这个代码有几个参数值得说明。emb_dim决定 embedding 的表达粒度我一般在 8 到 32 之间调太小学不出特征差异太大容易过拟合且线上向量存储压力大。hidden_dim控制塔的宽度双塔模型中间层的隐层维度不需要比 embedding 维度大太多否则最后一层内积之前向量长度太大内积结果方差会偏大需要额外加 scale 层。mean(dim1)是把同一侧多个特征 id 的 embedding 取平均这是最简单的融合方式实际项目中可以换成 DIN 的注意力加权但第一版用均值就够了。双塔模型一个容易忽视的点是向量方向。内积计算没有限制向量长度如果某些特征主导了向量模长排序结果会被单个特征带跑。常见做法是在内积前做 L2 归一化或者除以一个温度参数。我习惯在训练时对两个塔的输出向量做 L2 归一化再内积这样排序分只看向量的 cosine 相似度模型更稳定。4.3 离线评估NDCG、HitRateK 和 AUC 分开看搜索广告排序的离线评估不能只看 AUC。AUC 衡量的是“正样本得分大于负样本得分”的概率但它对排序头部不敏感。广告排序真正关注的是排在第一位、前三位的结果准不准就像用户搜索只看前几条一样。业界常用的排序指标是 NDCGK。计算方式是把模型预测分数降序排列取前 K 个结果算 DCG折损累计收益再除以理想排序下的 IDCG。相关性标签可以是多级的但对广告场景最简单的是用“点击”作为相关性标签点击就是 1未点击就是 0。import numpy as np def ndcg_at_k(scores, labels, k10): scores: 模型预测的排序分一维数组 labels: 对应的相关性标签点击为 1 否则 0 order np.argsort(-scores)[:k] dcg sum(labels[i] / np.log2(idx 2) for idx, i in enumerate(order)) best_order np.argsort(-labels)[:k] idcg sum(labels[i] / np.log2(idx 2) for idx, i in enumerate(best_order)) return dcg / idcg if idcg 0 else 0.0这个实现里核心是np.log2(idx 2)位置从 0 开始第一位对应 log2(2)1权重不折损第二位权重是 log2(3)≈1.58。这样做是模拟用户浏览时的衰减注意力。还有一个常用指标是 HitRateK衡量真实点击的广告是否出现在模型预测的前 K 个位置里。它比 NDCG 更直接适合在模型调参时快速对比。我一般在每轮训练后同时算 NDCG5、NDCG10 和 HitRate10三个指标一起看。如果 NDCG 没涨但 HitRate 涨了说明模型在头部排序有改善反过来则说明可能在长尾质量上提升了但商业上价值有限。4.4 训练参数学习率、Batch Size、负采样比和正则深度排序模型的训练参数有两个容易被低估的坑。第一个是负采样比例和损失权重需要联动调整不能只调一个。负采样调到 10% 之后模型看到正样本的相对比例变高如果不加权重模型会倾向把所有候选预测成高点击率。我常用的做法是按采样比例给负样本的损失乘上倒数权重保持整体期望和未采样前一致。第二个是学习率深度排序模型用 Adam 时初始学习率我在 1e-3 到 3e-3 之间选预热几百步之后衰减。embedding 层的参数占比很大通常需要一个比 MLP 层更小的学习率或者对 embedding 层单独做 L2 正则。一个推荐参数表格参数推荐范围说明embedding_dim8 ~ 32太小欠拟合太大存储压力大隐层维度64 ~ 256双塔每塔两层足够学习率1e-3 ~ 3e-3Adam带预热batch_size512 ~ 4096越大越稳定显存换稳定性负样本采样率5% ~ 20%和损失权重联动训练轮数1 ~ 3广告数据量大通常一轮半收敛batch size 的选择和线上参数服务的能力有关联。batch 太大模型收敛后 AUC 会更平稳但每轮训练时间变长batch 太小梯度噪声大需要更低的学习率配合。我一般会先用小 batch 跑通链路上线前最后一批用大 batch 重训一版这样线上指标波动最小。5. 搜索广告排序落地的五个踩坑记录5.1 样本穿越我把“未来信息”塞进了训练特征现象离线评估 NDCG 提升了 5 个点模型上线后点击率不升反降转化基本没变化。排查时发现训练特征里有一个“广告近 24 小时点击率”统计特征离线计算用的是全量历史数据包括当天训练样本之后产生的点击。原因这个统计特征的窗口包含了未来信息。模型在训练时能看到“答案”预估自然偏乐观但线上只能拿到过去的数据特征分布完全不同。这是排序模型里最常见的离线线上不一致。解决所有统计类特征必须严格按时间截断训练样本 T 时刻的特征只能使用 T 之前的数据计算。用 PySpark 做统计特征时要按事件时间开窗不能按处理时间。建议在特征上线前加一道校验对每个特征计算“离线历史窗口均值”和“线上实时均值”差异超过 5% 就打回。5.2 特征覆盖率离线 98%线上只有 60%现象模型线上 push 之后部分广告的排序分特别低排查发现广告侧有几个特征的覆盖率离线 98%线上只有 60%。原因离线特征是从数据仓库的宽表取的很多历史广告都有填充线上的特征服务只接了几个实时数据源有一部分广告没有实时数据特征缺失被编码成默认值 0。模型学到的 0 对应低 CTR线上大部分广告都被压到了底部。解决特征上线前必须有覆盖率对比报告。我先拉一份线上特征服务对历史日志的回放结果和离线训练特征做覆盖率、均值、分位数的对比差异超过阈值就检查特征来源。默认值要单独设成一个可学习的 embedding不能简单填 0。5.3 点击率校准pCTR 离线 2%线上跑出来 0.1%现象模型预估的点击率分布和线上实际点击率分布严重不一致校准曲线是歪的导致出价策略把预算花在了低质流量上。原因离线训练样本的负采样比例改变了正样本的先验概率。负采样只保留 10% 之后模型学到的概率不是真实的点击率而是被放大过的。如果不对 logit 做校准分布必然漂移。解决训练时记录负采样率推理时对 logit 做逆变换校准。常见做法是保序回归或 Platt Scaling。我一般用保序回归在线上拿最近 7 天的日志拟合校准曲线每周更新一次。这一步不做出价系统一定会被模型带偏。5.4 冷启动新广告没有历史统计特征现象新广告刚上线时没有点击率统计、没有转化统计模型给出的排序分很低拿不到曝光形成死循环。原因统计特征对广告侧含义很强模型对“缺失”这个状态没有学到合理的先验。新广告的 embedding 是随机初始化的输出向量没有方向内积相似度自然低。解决第一层用广告主类目级别的统计特征做回退。新广告拿不到自身 CTR就用所属类目平均 CTR、相似广告的平均 CTR 顶上。第二层在训练时给“特征缺失”单独一个 mask 标识让模型学会“缺失不等于差”。第三层是上线后做小流量探索给新广告一个最低排序分保证拿曝光再逐步切换到自身统计特征。5.5 训练和推断不一致线上分数比离线低一截现象同一个样本离线打分和线上打分对不上差一截但不是随机差而是成比例的系统偏差。原因特征拼接顺序不一致、模型量化精度、多个 ensemble 模型参数更新不同步都可能导致线上和离线分数不一致。最常见的是特征服务返回的特征和离线训练时拼接的顺序不一样embedding lookup 时特征 id 对不上模型其实在拿错误数据打分。解决上线前在测试环境跑 1000 条样本的“离线打分 vs 线上打分”对比画散点图要求拟合线斜率接近 1、R² 大于 0.99。我踩过这个坑之后立了规矩线上模型服务必须内置特征版本号每次请求都打印版本对比时先确认版本一致再排查其他。6. 模型部署与增量迭代离线 NDCG 不是终点线上验证才是6.1 上线前的验证顺序离线回放、小流量灰度、AB 实验模型训练完成之后不要直接全量上线。我通常按三步走。第一步是离线回放用线上特征服务对最近 7 天的日志打分对比模型输出分数和线上实际排序的差异这一步能挡住前文说的特征覆盖率和特征一致性问题。第二步是小流量灰度新模型切 5% 流量跑 24 小时观察点击率、转化率、广告收入、用户负面反馈指标有没有异常波动。第三步是正式 AB 实验实验周期至少 7 天覆盖一周中的工作日和周末。AB 实验的观测指标里最容易忽略的是“广告收入”和“用户满意度”之间的平衡。有些模型提升了点击率但降低了客单价广告收入不一定涨。我一般设核心指标为千次搜索广告收入辅助指标为点击率、转化率、平均点击价格。核心指标正向、辅助指标没有显著负向才允许全量。6.2 增量训练与模型回滚给线上模型留后悔药搜索广告的分布变化很快节假日、电商大促、突发新闻都会让模型效果快速衰减。常见的做法是周级全量训练加日级增量训练。全量训练在周末跑覆盖最近 14 天数据增量训练每天跑只用最近 24 小时数据在最新全量模型基础上继续训一小段时间。增量训练的难点是学习率要调小防止把全量模型学到的信息覆盖掉。我会把增量训练的学习率设成全量训练的十分之一训练步数控制在几百步以内。模型回滚机制必须提前做好。上线新模型时保留旧模型的参数文件和服务配置一旦 AB 实验或灰度阶段出现核心指标负向能在 5 分钟内切回旧模型。回滚操作经常在深夜发生所以步骤必须写成脚本不能依赖人工记忆。我自己的习惯是每版模型上线前写一份简短的验证清单离线 NDCG 对比、线上回放一致性、覆盖率差异、AB 实验起止日期、回滚脚本路径。这份清单在多次紧急上线时救过我。增量训练还有一个细节值得做就是对线上日志做抽样缓存。增量训练需要最近 24 小时的数据但实时日志系统经常会有数据延迟晚到 1 到 2 个小时是常态。如果训练时直接取“当前时间往前 24 小时”窗口末尾会缺一段数据。我用的是先缓存到 HDFS等 2 小时后再触发训练任务确保日志对齐。最后说一个教训。有一版模型离线的 NDCG 提升很多我以为找到宝了AB 实验跑了一周广告收入纹丝不动。后来定位到是样本穿越加特征覆盖率两个问题叠加。从那以后我给自己立了条规矩离线指标好看不算数线上数据验证过了才算数。这个思路贯穿了我后来做搜索广告排序的所有项目也希望帮到你。本文还有配套的精品资源点击获取
返回列表