1. CVR预估为什么总在和时间较劲做广告算法的人多半都有过这种经历模型线上效果明明很稳可一换到电商大促场景CVR的预估就开始发飘。你查特征、查样本、查线上日志折腾一圈后大概率会发现——问题不在模型本身而是转化信号根本没按时回来。在展示广告场景里用户看到广告到真正下单之间存在一个时间差。这个时间差可以短到几秒冲动消费品也可以长到几周高客单价决策类商品。如果模型把未来才会发生的转化当成今天不会发生那所有样本的label都是错的离线指标自然和线上体验对不上。这事在业内有个专门称呼延迟反馈问题Delayed Feedback Problem。传统做法也简单粗暴给转化设置一个固定观察窗口。比如统一看30天30天内发生转化就记为正样本超过30天一律当作负样本。窗口够长正样本漏得少但上线后新数据积累慢模型对实时变化的捕捉能力会明显变差窗口缩短到3~5天样本新鲜度上来了可大量晚归因的转化都被错杀成负样本模型学到的分布是扭曲的。另一条路线是样本重排Sample Reordering。既然转化回传有延迟那干脆把正样本按照真实的转化时间重新归位让模型在正确的时间点看到它。思路没错但落地时会被一个现实问题卡住广告系统是流式训练的样本一旦进过训练管道就很难回炉重排重排的工程成本非常高而且多轮重排会带来样本重复更新的问题。这些都是单层延迟假设下的解法。真实广告系统的延迟远比这复杂。我在实际项目中遇到过这样的情况同一个广告主的一个广告计划在开屏、信息流、搜索三个位置同时投放转化回流的速度差异极大——开屏位置的曝光到点击可能要几小时信息流位置用户可能反复看了三四次才点点击到成交又可能横跨好几天。这种多重延迟叠加在一起的场景再用一个全局窗口或者一个全局延迟分布去建模天然就是错配的。某大型电商平台的广告技术团队在这方面做了一次系统性的尝试核心思路是把曝光→点击→转化拆成级联的延迟阶段每个阶段单独建模再组合成整体预估。说实话第一次看到这个方案时我有种早就该这样的感觉——我们之前所有延迟反馈模型都默认延迟的是点击到转化这一段却忽略了曝光到点击本身的延迟也在干扰建模。这篇博文就把这套级联延迟反馈建模框架掰开揉碎来讲包括它解决的问题、模型设计、样本处理以及工程落地时那些文档里不会写的事。2. 多重延迟到底多在哪不止点击到转化那点事延迟反馈在教科书里通常被描述成一个简单场景用户在时刻t点击广告在时刻tΔ发生转化Δ就是延迟。模型要学的是这条样本最终会不会转化而不是在观测时刻是否已转化。但这个描述在真实展示广告系统里至少漏了三层信息。第一层转化路径本身是多跳的。用户不是点一下就直接买。曝光之后可能过几个小时才点击点击之后可能收藏、加购、对比竞品真正成交已经是几天后。如果只对点击→转化建延迟模型那曝光到点击这一跳的延迟就会静默地污染样本用户在凌晨刷到广告深夜没点白天上班摸鱼时点了晚上才下单——从曝光视角看这条样本已经经历了两次延迟叠加。第二层不同广告位、不同商品品类的延迟分布差异巨大。拿开屏广告和搜索广告对比开屏是强曝光弱意图用户可能在看到广告的瞬间产生兴趣也可能在几小时后才被某个场景唤起搜索广告恰好相反用户带着明确意图进来点击往往发生在曝光后几十秒内但转化延迟可能很长比如搜了冰箱货比三家过了一周才下单。用同一套时间衰减函数去拟合这两种流量等于让模型去平均两个完全不同的世界。第三层延迟本身就是动态变化的。大促期间用户决策加速延迟整体会缩短新品首发期用户处于观望状态延迟会被拉长某个爆款短视频带火了商品用户涌进来的路径从搜索变成刷到即买延迟分布又变了。固定窗口和固定参数化分布的方案在这种动态环境下天然会滞后。我们之前踩过的坑也出在这里线上CVR预估一直偏低排查到样本层才发现某一大类商品的平均转化延迟被低估了将近一倍但另一类冲动消费品的延迟被高估了。全局模型把两者平均之后两类商品的预估都不准。这不是单纯增加模型容量能解决的——你需要的是显式地把延迟结构建模出来。还有一个常被忽略的点广告曝光和点击本身存在时效性。用户对广告的记忆会随时间衰减一条广告展示完如果用户没有立刻点击那么越往后点击概率越低。传统延迟反馈模型把所有曝光样本等权看待可过去半小时内曝光未点击和三天前曝光未点击的样本含义完全不同后者几乎可以确定是负样本前者则还有翻盘可能。级联建模的一个关键好处就是能把未点击但可能后续点击的样本也纳入延迟建模的范畴。在给出解决方案前我们需要先明确一个判断延迟反馈建模的最大难点不在于怎么处理正样本晚归问题而在于如何区分未转化与未及转化。这个区分做好了窗口长短、样本权重、分布假设都是水到渠成的事。多重延迟环境下这个区分尤其难因为你根本不知道当前处于哪一跳的延迟之中。3. 级联延迟建模框架把延迟拆成两段来学先说结论这套框架的核心是把曝光→转化这条完整路径拆成两个延迟阶段——曝光到点击、点击到转化每个阶段分别建模延迟概率分布最后级联得到整体的转化概率。听起来不复杂但真正的难点在于两个阶段的延迟概率如何关联、联合分布怎么构建、以及训练时如何让两条链路共享信息不互相干扰。框架的底层结构可以理解成一个两阶段概率图模型。曝光样本进来后先经过一个点击延迟模型估计这条曝光在观测时刻前是否已发生点击、以及如果未点击后续点击的概率转化延迟模型则处理已点击样本估计点击到转化的延迟分布。最终的预估结果是两者的级联组合P(转化) P(点击) × P(转化 | 点击)这里有个关键细节P(点击) 不是传统CTR预估里的曝光后是否点击的二分类概率而是带时间维度的生存概率——它在观测时刻未点击的条件下仍然要估计后续点击的可能性。这也是为什么这个框架能处理曝光很久仍未点击的样本它不会直接把这类样本判死而是给一个随时间衰减的存活概率。级联结构的实际价值在特征利用上体现得很直接。曝光阶段的特征广告位、素材样式、用户上下文和点击阶段的特征点击位置、点击序列、停留时长本来就不在同一层级共享进一个模型里反而会互相稀释。拆成级联之后每个阶段可以用自己最相关的特征子集训练目标也更干净。再往细看两个阶段的延迟模型都采用参数化的延迟分布拟合。比较常见的做法是使用对数正态分布或威布尔分布来刻画延迟的概率密度——这两个分布都能比较好地模拟前期快速上升、后期长尾衰减的实际延迟形态。但分布参数不是直接用MLP吐出来的而是通过一个延迟感知模块结合用户和广告的上下文特征动态生成。这样不同用户、不同品类、不同广告位就能拥有各自适配的延迟形态而不是共享同一套全局分布。训练时用了多任务学习的思路点击延迟模型和转化延迟模型各自有监督信号但两个模块的embedding层共享底层参数。为什么要共享底层因为曝光到点击和点击到转化在用户意图上高度关联——一个用户对某类广告的点击偏好大概率会影响他后续的转化速度。共享embedding让两个任务互相正则尤其在某个阶段样本稀少时另一个阶段的信息能起到补充作用。最终在线服务时模型不会真的等延迟分布跑完才出分而是直接输出一个可微分的期望转化率。具体做法是把延迟分布的累积函数在观测时刻处求值结合已观测状态生成校准后的条件概率。这样既保留了延迟信息又避免了在线推理时需要积分采样的尴尬。这里顺便说一个我最初理解上的误区我一度以为级联就是按时间先后串行训练两个模型先训练曝光→点击再拿点击输出去训练点击→转化。实际上这套框架是端到端联合训练的两个模块在训练时是一体的只是推理路径上呈现出级联形态。端到端的好处是梯度可以回流贯穿两个模块让点击模块学到的表征主动去适配转化模块的需求而不是各自为政。4. 训练样本构造与标签处理的四个关键动作模型结构再好样本一乱全白搭。级联框架对训练数据的要求比传统方案苛刻得多核心原因在于整个模型依赖的是观测时刻和真实转化时刻两套时间信息任何一处错位都会级联放大。第一个关键动作是记录的锚定。传统方案习惯用日志回传时间作为转化时间但在级联框架下必须以广告曝光时刻为锚点分别记录点击相对曝光的时间偏移、转化相对点击的时间偏移。工程上需要把曝光日志、点击日志、转化日志做一次多键关联中间任何一步丢日志都会产生一条不完整的中间态样本。第二个关键动作是动态窗口。不再使用固定观察窗口而是为每个样本根据其所在的广告位、商品类目、流量来源确定一个观测截止时间。大促期、日常期、新品期分别配置不同的延迟上限这个上限不是人为拍脑袋定的而是根据近期流水的延迟分布分位数自动计算的。我们当时取的是p95延迟时间——保证至少95%的正样本在窗口内能被观测到剩余5%的长尾通过模型纠偏消化。第三个关键动作是样本赋权。级联框架训练时曝光未点击、已点击未转化、已点击已转化三类样本的含义完全不同不能一视同仁地进Loss。常规做法是对曝光未点击样本不直接标记为负样本而是按时间衰减赋予一个小于1的负样本权重对已点击未转化样本则根据其在延迟分布下的条件概率动态调整权重。这个权重计算很敏感初期我们直接把权重设成常数离线指标一度非常好——但后来发现那是因为模型学了个掩耳盗铃式的捷径把不置信的样本全部压低线上反而变差了。第四个关键动作是负样本的延迟感知。一条已点击样本在观测时刻未转化它可能真的是负样本也可能只是还没到转化时间。传统的做法是在窗口到期后才把它标记为负但级联方案更进一步负样本标记不等同于二值翻转而是给这条样本打上一个剩余转化概率的软标签。这个软标签来自当前模型的延迟分布估计形式上有点类似自训练self-training但需要做好防回声控制——不能让模型拿自己输出的概率去训练自己否则会越练越自信走向偏差。我们的做法是使用延迟一定步数的旧模型版本来生成软标签人为切断回声回路。处理完样本另一个容易翻车的地方是特征的时间一致性。级联框架里同一个用户在曝光时刻、点击时刻、转化时刻的状态完全不同如果在特征拼接时混用三个时间点的用户状态模型会学到一堆虚假的相关性。正确做法是曝光→点击阶段所有特征都取曝光时刻的用户状态点击→转化阶段取点击时刻的状态。线上推理和离线训练必须严格对齐这套取数逻辑否则离线评测看着上涨的AUC上线后却是另一回事。5. 离线评测与在线AB效果验证的完整链路延迟反馈模型的离线评测本身就是个需要小心设计的事。用普通的分位点切分样本去做AUC、GAUC在多延迟环境下给出的结论往往是误导性的。评测集必须模拟观测时刻的真实状态。具体来说把测试样本按某个时刻T做截断所有在T前曝光的样本进入评测集但label不采用最终转化结果而只采用T时刻的已观测状态。这样做才能还原线上推理的真实条件——线上模型看到的就是此刻状态而非最终结局。如果评测集用的是全量回填的最终label那等于让模型开卷考试高估了它的真实能力。级联框架的评测维度也比普通模型多一层要分别评测曝光→点击延迟模型的准确性、点击→转化延迟模型的准确性以及整体转化预估的校准度。我见过一些人只看整体AUC结果两个子模型一个过拟合一个欠拟合整体指标居然还说得过去纯属错误抵消。所以我们的评测报表里至少包含三组指标两阶段各自的延迟分布拟合损失可以用负对数似然、两阶段各自的条件CVR预估精度、以及最终级联后的校准曲线和AUC。在线AB时最需要关注的不是CVR涨了几个点而是预估分布的形状变化。级联模型和多延迟结构的引入很容易让模型在某些流量上给出极度自信的预估——比如预估CVR直接从1%跳到5%这往往不是模型变准了而是延迟分布参数在某个特征组合上外推失效了。我们在AB期间会额外监控分位数分布如果p90/p99的预估值出现异常抬升基本可以判定是分布参数出了边界问题。另一个AB要注意的点是样本新鲜度。级联模型对近期流量的拟合更好因为它显式建模了延迟的动态变化。这意味着AB测试的前几天模型优势明显但会随着窗口推移慢慢衰减——不是模型退化而是对照组的老模型也在随着时间积累更多最终label而逐渐逼近真实。正确评估需要拉长观察周期至少覆盖一个完整的延迟周期比如高客单品类可能需要14~21天否则你评估的只是冷启动期优势。冷启动期优势这个事我再展开说一下。它几乎是大促期间所有延迟反馈模型的通病模型上线时最新样本全是未及转化的正样本候选如果延迟分布建模不准模型会误以为所有样本都在快速转化冷启动阶段的预估会整体偏高。级联框架因为对曝光≠点击的样本也建模了延迟理论上冷启动偏差会更小——但这个优势的前提是你对曝光→点击延迟的分布先验设得足够合理。我们第一次上线时用的先验参数来自全量流量统计等定向到某个垂直品类时明显偏了后来改成按流量分桶分别初始化冷启动偏差才压下来。6. 部署环节最容易翻车的三个细节模型结构再怎么先进最后online serving那一哆嗦出问题前面全白干。这套级联框架部署时我遇到过三个特别容易翻车的细节这里单独列出来。细节一分布求值的在线计算开销。训练时求延迟分布累积函数可以在TensorFlow/PyTorch里用autograd搞定但线上服务通常只有几毫秒的推理预算不可能为了一个累积分布去做数值积分。实际落地时要预先把延迟分布的累积函数在离线算成查找表线上只做查表和线性插值。这里有个精度取舍问题查找表的步长太粗会损失梯度信息步长太细则表太大、缓存命中率下降。我们最终在p50周围步长取0.1小时、长尾区取1小时在精度和性能间找到了平衡点。细节二时间特征的动态漂移。级联模型天然依赖时间信息曝光时刻、距离曝光的时长、距离点击的时长等但线上特征系统往往存在时间特征泄漏或缺失的问题。比如一条样本已经观察了72小时未转化特征系统却只给了距曝光时长24小时的截断值因为只更新了一次缓存模型就会拿着错的时间戳去做延迟分布求值结果完全失真。部署时一定要核对time-related特征在曝光、点击、转化三个阶段各自的更新频率确保送入模型的时间戳和日志系统对齐。细节三模型灰度期间的吞吐波动。级联结构相比单模型增加了一个阶段的推理计算但更隐蔽的坑在于特征取数链路的延长——线上推理需要同时读取曝光时特征和点击时特征意味着特征服务要多一次交互。我们第一次灰度时RT直接飙升了40%排查后发现是特征服务连接池配置没跟上而不是模型本身算力不够。这种问题在压测环境很难暴露因为压测流量和真实流量的特征分布不一样缓存命中率完全不同。稳妥的做法是提前在预发环境用真实流量回放做性能验证并且给特征服务单独设降级开关——一旦下游抖动系统能退回只用曝光特征做粗排不至于全链路雪崩。7. 写在最后的三条实操心得这套级联延迟反馈框架并不是解决延迟反馈问题的唯一路径更不是包治百病的银弹。但它在多重延迟场景下提供了一个非常自然的建模视角把复杂的问题拆成清晰的阶段每个阶段用恰当的数据和模型去刻画再通过级联方式组合。这种先拆解再组合的思路可迁移性很强。顺着这个方向再往前走我觉得有三个点值得持续跟进。第一是延迟分布的先验自适应——现在框架里分布参数由模型预测但先验还是靠统计分析初始化如果未来能做到在线实时更新先验对动态环境的适应会更好。第二是和多触点归因的结合——级联框架天然记录了用户从曝光到点击再到转化的完整路径这些路径信息完全可以反哺归因模型让预算分配更精准。第三是探索性的试探样本——当模型对某条新样本的延迟估计高度不确定时可以在策略上主动给更多探索流量让延迟分布参数更快收敛这个方向上还有不少可做的空间。最后说一个个人体会延迟反馈建模的改进很多时候不是靠更花哨的模型结构而是靠把时间这个维度在样本、特征、评测、部署各个环节都处理到位。级联框架只是一个壳真正让效果发生质变的是你是否愿意在每个细节上较真——动态窗口、样本赋权、时间特征对齐、冷启动初始化每一个单拎出来都不难但串起来做到位比堆模型复杂度要难得多。如果你正准备在团队里尝试这套方案我建议先从拆解自己流量的延迟分布开始——把多重延迟到底多在哪看清楚后续的建模和工程工作都会顺很多。