展示广告的延迟反馈问题,我断断续续摸了快两年。最典型的一个场景:用户点了广告,进了落地页,逛了一圈没下单,你以为没戏了,结果三天后他回来直接付款。广告系统必须在点击发生后的几十毫秒内决定要不要继续出价、这笔流量值多少钱,可是真正能证明流量价值的转化信号往往要等好几天才回来。如果只是“晚一点”也就算了,麻烦在于从点击到最终转化之间往往还隔着加购、激活、留资这些中间动作,每一层都有自己的延迟节奏,叠在一起以后,整个“点击到付费”的等待时间根本不是一条漂亮的单峰曲线。
我们团队最近在展示推广场景里落地了一套级联延迟反馈建模框架,核心思路很直接:不再把“点击→最终转化”当成一个黑盒延迟来学,而是先预测中间行为(比如加购/激活)会不会发生,再预测中间行为发生后最终转化会不会发生,同时对这两段的延迟分布分别建模。听起来就是拆两步,但真要做到离线训练和在线推理都能稳定上线,中间全是坑。这篇把整个设计、样本处理、工程落地的细节和踩坑记录完整复盘一遍,给同样在做延迟反馈建模的朋友一个参考。
1. 多重延迟不是“等一等”那么简单:业务与模型的双重困境
1.1 一个点击的四次心跳
哪怕只看一次最简单的展示广告点击,用户的行为节奏也比大多数人想象中复杂得多。点击广告本身是第一个动作,通常在毫秒级;紧接着落地页加载、用户浏览、开始考虑,这是第二个动作,可能持续几十秒到几分钟;然后可能出现加购、注册、激活这类中间行为,时间可能发生在点击后几分钟到几天;最后才是支付、付费、成交这类最终转化,又要再隔一段时间。
对广告系统来说,真正的问题不是“转化回传得慢”,而是不同环节的延迟互相叠加。假设某类流量从点击到加购平均需要2小时,从加购到支付平均需要5小时,那么点击到支付的平均等待时间绝不是简单取这两个数字的某种平均值,而是两段分布卷积之后的结果。如果只用“点击→支付”这一个宏观标签去拟合延迟分布,相当于拿一条简单曲线去描述一个实际上的复合过程,参数会被两头拉扯,最后预测出来的延迟既不像快转化人群也不像慢转化人群。
我见过不少团队一开始都试图用一个大窗口不管三七二十一,把窗口内没回传的样本都标成负例。这种做法在延迟很低、数据量很小的场景下还能跑,但到了规模化展示广告以后会出大问题:上午点击的流量,下午可能陆续回传转化,可模型已经拿“未回传”当负例学了一轮,把大量原本能转化的样本学成了“低质量流量”。
1.2 单窗口建模的错误代价
先说说传统单窗口方案到底错在哪。假设我们把观察窗口设成7天,第7天没回传转化的一律标负。看起来合理,实际上第8天、第9天回传的转化也不少,尤其是高客单价的品类,用户从决策到下单周期长,晚期转化占比可能接近三成。
这些“晚到”的转化样本被当成了负样本,模型得到的学习信号就是:点击特征和早期转化特征有相关性,但和晚期转化特征没有相关性。于是模型对很多真实会转化的流量群体给了一个偏低的估计,后续预算分配自然也不合理。
后来业界的常规优化是引入延迟生存建模或者延迟校正,不对所有未转化样本一视同仁,而是估算“这个样本未来还会不会转化”。具体做法是根据已经等待的时间、转化延迟的分布密度,计算一个右截断权重,把负例的置信度调低。这一步确实有效,但它仍然没有充分利用中间行为信息。
1.3 级联思路:把“最终转化”拆成“中间行为”与“条件转化”
级联延迟反馈建模的核心,是引入一个甚至多个中间事件,把最终转化概率变成一个概率链。用最简单的一级级联举例,中间行为记为M,最终转化记为F,整体预测可以写成:
P(F=1|x) = P(M=1|x) × P(F=1|x, M=1) + P(M=0|x) × P(F=1|x, M=0)
如果只保留第一项,等于默认“没有中间行为就不会有最终转化”,这在很多场景下是错的,用户完全可能不经过加购直接支付。所以实务上通常保留M=0的分支,或者把两条路径合并成条件期望。
这看起来和常见的多任务学习没有本质区别?区别在于延迟结构的处理。多任务模型通常只是让中间行为和最终转化共享底层特征,然后各接一个分类头;级联延迟反馈建模还会为每一段延迟分别建模:点击到M的延迟时间T1,M到F的延迟时间T2。这样模型可以回答两个更具体的问题:这个流量大概多久会发生中间行为?发生了中间行为之后,又要多久才会走到最终转化?这种拆分在业务上有直接用途——比如有的流量中间行为很快但离成交很远,有的流量中间行为慢但一旦发生很快付款,两者在出价策略上应该完全不同。
2. 级联结构里的建模细节:概率链、延迟分布与样本权重
2.1 建模目标与符号定义
先把形式化定义理清楚。一个训练样本由以下信息组成:
- 特征向量 x:用户属性、媒体属性、广告创意属性、上下文特征等
- 是否点击:C,建模时通常限定在已点击样本上
- 是否发生中间行为:M,例如加购、下载激活、注册、留资
- 是否发生最终转化:F,例如支付、付费、有效线索
- 观察窗口内记录到的中间行为时间 t_m,以及最终转化时间 t_f
决策阶段需要输出的是 P(F=1 | x, C=1)。由于我们只在已点击样本上建模,后面省略C。目标是让这个概率既能用于排序,也能用于出价。
如果M和F都能被准确观察到,经验上可以用P(F=1|x) = P(M=1|x)·P(F|M=1,x)直接拆。但真实系统里M的观测是不完美的,所以必须处理缺失和右截断。
2.2 延迟分布怎么进入级联结构
延迟分布是整个框架最容易被低估的模块。很多人以为延迟分布就是给每个转化样本附一个“等待时间”然后回归,实际完全不是这么回事。我们需要的是:给定已经过去了t时长且尚未发生下一个事件,这个样本在未来某个时间点发生事件的概率。
对中间行为这一段,我倾向于用对数正态分布或Weibull分布来拟合T1的密度函数。对数正态的好处是参数可解释性强,均值μ和方差σ可以通过网络输出,且支持点击后早期概率很低、后期逐渐抬升再下降的形态。对最终转化这一段,类似地拟合T2。
处理“叠加”是级联建模的关键。观察到的“点击→最终转化”的总时长T = T1 + T2。如果T1和T2都服从已知参数分布,T的分布是卷积积分。常规实现不会真的去算闭式卷积,而是对每个样本做离散化积分:先把时间轴切成小时粒度的桶,从0到观察窗口逐步累加。比如点击后已经过了30小时还没发生M,那么需要计算在剩余时间T_max − 30内发生M的概率,同时考虑M发生后F的条件概率。
这个积分过程在训练时成本不低,但带来两个直接收益。第一,模型对“早期转化”和“晚期转化”的区分能力强很多,因为不同特征可以分别影响T1和T2的参数;第二,在线推理时可以回答“再等24小时,这条流量有多大可能完成转化”,这对接预算节奏非常有价值。
2.3 训练样本的分桶与权重设计
级联结构需要更精细的样本分类,不能只按“是否转化”分正负。我按观察窗口内的状态把样本拆成四类:
| 样本类型 | 观察到的状态 | 处理方式 |
|---|---|---|
| A | F=1,已观察到最终转化 | 中间行为M是否观察到都行,正样本权重1 |
| B | F=0,但M=1 | 弱负样本,权重需根据“M发生后F在剩余时间仍未发生”的概率来调 |
| C | F=0,M=0,且观察时间远超典型延迟 | 强负样本,可以放心给低权重 |
| D | F=0,M=0,但观察时间很短 | 不能当负样本,未来可能转化,需要右截断处理 |
C和D的处理是整个样本设计的核心。D这类样本在点击后几小时内就进入训练集,如果直接当负样本,会严重压制高转化但慢热的流量。实践里我用的是“软化负例权重”:针对每个样本,根据当前已等待时间t估计“未来仍可能转化”的概率,然后用它把负例损失打一个折扣。
具体公式不需要写得太复杂,可以直接借助前一轮模型输出:p_suppress = F_hat(x, T_max) − F_hat(x, t),也就是预计在完整窗口内转化概率减去截止当前已转化的概率。当p_suppress大时,负例权重就设小一些,避免模型把“还没到转化时间”的样本误学成“永远不会转化”。
2.4 损失函数为什么要拆开优化
很多团队上来就把两个分支的损失加起来做多任务联合训练,结果发现M分支学得稀烂。原因很朴素:最终转化F的标签本身就带延迟和噪声,把噪声反向传播到M分支,会让M分支学到一些奇怪的偏好。
我在实践中采用分阶段策略。第一阶段,只用M标签训练中间行为预测头,M标签的噪声远小于F标签,也相对及时;第二阶段,固定M头的梯度,训练F头,此时F头把M预测值当作特征或者门控乘子;上线稳定以后,再打开M头的梯度做端到端微调。这样既保留了级联带来的结构先验,又不至于让噪声毁掉中间行为信号。
损失函数这里还有一个容易被忽略的细节:M和F的样本分布差异可能很大。M相对高频,F相对低频,如果两个损失直接相加,最终模型的优化重心会偏向M。我在两个头的损失上各放了一个可学习的权重,或者直接按业务目标做权衡:如果业务关心“深度付费”,F头权重就要更高;如果业务关心“促活”,M头权重更高。
3. 离线评估与在线推理的落地细节
3.1 离线评估不能只看AUC
延迟反馈模型最误导人的地方在于离线评估。如果直接对第二天回流的数据跑一遍AUC,你会发现级联模型和普通多任务模型差别没有想象中大,因为排序能力确实提升有限。真正的差异在时间稳定性上。
我习惯用时间切片评估方式。取某一天点击的样本,分别在第1天、第3天、第7天、第15天各做一次快照,每次用同一份模型打分,然后观察预测的“完整窗口转化概率”是否随时间漂移。理想的模型应该是:样本刚点击进来时预测一个较高的未来转化率,随着时间推移,如果转化没发生,预测的剩余概率逐渐下降;如果转化发生了,预测值保持高位。那些只会“等转化发生才输出高概率”的模型,本质上是在记忆标签,对出价没有帮助。
除了概率校准,还要看延迟分布的拟合质量。例如预测的T1分布和实际回传的中间行为时间直方图之间,KL距离或者分组误差应该保持在一个稳定范围。如果某个渠道的中间行为回传时间明显变慢了,但模型分布没有跟上,那就是分布漂移信号,需要及时刷新分布参数。
3.2 在线推理的实时改造
在线点击发生时,绝大多数用户还没有产生中间行为,所以系统真正用来出价的分数是边缘化之后的组合概率。有两种近似做法。
第一种是把M=0路径直接算出来:
P(F) ≈ P(M=1|x)·P(F=1|x,M=1) + P(M=0|x)·P(F=1|x,M=0)
另一种做法是简化为只预测“点击后未来完整窗口内F=1”的边缘概率,并把M分支作为特征增强项。两者都可行,但要注意在线和离网络径必须保持一致,否则离线调参全部失效。
级联结构在在线侧有个天然优势:如果用户点击后短时间内真的发生了中间行为,系统可以实时切换分支。例如点击后10分钟检测到用户加购了,这时候再把“用户加购”这个状态写进特征,直接走M=1的路径,预测的转化概率会跳升一截,比重新跑完整模型要快得多。这种“按状态切换概率分支”的设计,在实时竞价场景下非常实用。
3.3 阈值的敏感性
广告模型输出的绝对值比排序排名更加重要,因为出价公式里要用CVR预测值去乘以期望扣费。级联结构有个天然的坑:两个小于1的概率相乘,结果会往小偏。P(M=1|x)如果是0.1,P(F|M=1)如果也是0.5,连乘下来只有0.05,而真实付费率可能是0.08。这个系统性低估会让出价变成过于保守。
解决办法不是回到单模型,而是在日志空间做组合:
log P(F) = log P(M=1|x) + β·log P(F=1|x,M=1) + C
其中β是压缩指数,用来调节两个概率的尺度。β偏小时,模型更依赖M分支做排序;β偏大时,更依赖F分支。这个参数我一般手动调节,用业务口径的校准损失来做选择,而不是网格搜索几十组,因为线上成本曲线太敏感。
3.4 监控体系要覆盖什么
级联模型上线以后,监控不能只看最终转化率的波动。我拆成三层监控:
- M层监控:中间行为回传率是否下降,M预测概率是否漂移,M延迟分布是否变化
- F层监控:F预测概率是否校准,最终转化延迟分布是否变化
- 组合层监控:最终输出的预算消耗速度、成本曲线、边际ROI
尤其是M层的不一致问题,很多时候线上成本波动并不是F模型变差了,而是某个渠道的中间行为回传率突然从80%降到40%,导致样本标签里的M大量缺失,M头预测概率跟着下降,连锁反应到最终输出。这种情况如果不分层监控,排查起来会非常痛苦。
4. 我们在真实流量中踩过的坑:四个典型问题
4.1 中间行为回传不一致导致样本噪声
这是我认为整套框架里最容易踩、也最隐蔽的坑。不同渠道对“加购”“激活”这类中间行为的定义和回传能力完全不一样。有的媒体SDK能稳定回传加购事件,有的渠道因为技术链路问题会漏传大部分中间行为。
如果我们把“没有收到M信号”直接当成“M=0”,高回传渠道会被模型偏爱,低回传渠道被严重低估。我当时的处理方式是给每个渠道单独估计一个M覆盖系数,训练时对该渠道的M标签做缺失修正。具体做法是统计每个渠道已转化样本里“观察到了M”的比例,用这个比例去校准未转化样本中M=0的可信度。如果某个渠道的已转化样本里M回传率只有20%,那它未转化样本里M=0的置信度也要打对应折扣。
4.2 时间戳倒挂与乱序
异步回传链路里,时间戳倒挂并不罕见。日志里可能出现点击时间比中间行为时间还晚的情况,比如中间行为记录了早上10点,点击时间记录却是10点半,其实是点击在前的。级联模型对时间顺序极其敏感,如果让这种倒挂样本直接进入训练,它的延迟会变成负数,分布拟合直接出问题。
我们最终在样本预处理阶段做了一次时间轴仲裁:以点击时间为基准,如果中间行为时间或转化时间早于点击时间,先看偏移量是否小于某个阈值;如果只是秒级偏移,通常是时区或时钟偏差,统一校正到点击时间之后;如果偏差大到无法解释,就把该样本的M标签置为缺失,而不是强行改时间。这个操作牺牲了一小部分样本,但换来了训练稳定。
4.3 级联连乘带来的数值稳定性
上边说到的概率低估问题,在训练阶段还有一个更现实的表现:连乘以后梯度太小。P(M)和P(F|M)都属于相对稀疏的事件,概率可能都在0.01到0.1之间。连乘得到的最终概率可能只有0.001到0.01这个量级,在反向传播时梯度会被压得几乎没有区分度。
我在实现里给两个头各自加了独立的输出校准,不直接用sigmoid输出相乘,而是让每个头输出logit,再在log空间做加法。这样数值范围稳定得多,而且可以通过调节两个头logit的缩放系数来控制最终概率分布形状。经验是,β设在0.8到1.5之间往往比较稳,具体要看业务真实转化率的高低。
4.4 窗口口径与大促脉冲的适配
最后一个坑来自业务侧。归因窗口在广告系统里通常被设为7天或者30天,但模型训练如果不做特殊处理,天然偏向近期转化样本,长期深度转化会被压薄。
大促期间更明显:流量密度短时间暴增,用户决策周期被压缩,延迟分布整体左移。如果模型还在用平时训练出来的延迟分布,会把很多“原本会在三天后转化、但因为大促当晚就转化”的样本判定为异常,反而拉高预测。应对方式是在训练特征里加入“活动脉冲标量”,比如当前流量是否处于大促期、距离大促开始多少小时;同时在在线侧对延迟分布参数做指数移动平均,近几小时的数据权重远高于前几周的数据。这些细节在论文里往往一笔带过,但对稳定上线非常关键。
一些个人体会
这套级联延迟反馈建模框架,核心并不高深,无非是把“点击到最终转化”这个复合延迟拆成了“点击到中间行为”和“中间行为到最终转化”两段,然后分别建模、分别监督、分别估计延迟分布。但落地过程中最花时间的反而是那些看起来不起眼的事情:样本分桶是否合理,渠道回传不一致有没有修正,延迟分布参数有没有跟随流量变化及时刷新。
如果你所在的团队也面临类似的延迟反馈问题,我的建议是不要一上来就堆模型结构。先把训练样本按“已转化/已中间未终转/未中间/短观察窗”分清楚,把右截断权重做对,再把延迟分布拆成级联的两层,效果往往比直接换一个大模型更明显。这个框架的下一步可以往更深方向扩,比如加购之后还有到店、支付之后还有复购,每多一层级联,都需要多一套监控和样本管理,收益和成本要仔细衡量。