第 26 章 / 共 57 章 · 约 6 分钟
ML 案例 3:广告点击率预估
先花 10 分钟自己设计:需求、核心实体、API 和高层架构,各写几行。写完或者跳过之后,下面的讲解才会展开。
先写你的思路
只存在这台设备上;登录后会存进你的账号。
在广告系统里,模型预测的点击率(pCTR)不只用来排序,还直接决定钱:按点击计费的广告按“出价 × pCTR”排名,扣费也和 pCTR 有关。所以这道题和推荐最大的不同是:预测值必须校准,0.02 就得真的意味着 2% 的点击概率。另外,数据量极大、正样本极少、广告和用户兴趣变化很快,模型需要频繁更新。
1. 需求澄清
功能需求
- 每次广告请求,对通过定向筛选的候选广告预测点击概率
- 预测结果用于竞价排序、计费和预算控制
- 新广告上线后,尽快得到合理的预估
非功能需求
- 规模:峰值 50 万次广告请求/秒,每次给几百个候选打分,即每秒上亿次预测
- 延迟:打分阶段 10~20 ms
- 校准:整体和各个分群(广告主、版位、设备)上,预测点击数与实际点击数接近
- 模型新鲜度:小时级甚至分钟级更新
2. 问题形式化
二分类:给定(用户,广告,上下文),预测这次展示在归因窗口内被点击的概率。
为什么校准这么重要:按点击计费(CPC)的广告,排序依据是 eCPM = 出价 × pCTR × 1000。如果模型系统性地高估某类广告的点击率,这类广告会赢下本不该赢的竞价,平台少赚钱,广告主也投不出效果。在广义第二价格(GSP)拍卖里,扣费还取决于下一名的 eCPM 与自己 pCTR 的比值,预测偏差会直接变成扣费偏差。推荐排序只关心相对顺序,广告还要求绝对值准确。
查看图表源码
flowchart LR
accTitle: 广告请求的打分与竞价流程
req["广告请求"] --> target["定向检索:候选数千"]
target --> pre["轻量模型粗筛:约 300"]
pre --> ctr["pCTR 模型"]
ctr --> auction["竞价:按出价 × pCTR 排序"]
auction --> pacing["预算与频控检查"]
pacing --> show["展示并记录曝光"]
show --> join["曝光与点击流式拼接"]
join --> train["训练样本"]
train -.->|"增量更新"| ctr
3. 数据与特征
样本:每天几十亿次曝光,点击率约 1%,正样本极少。
负样本降采样:保留全部正样本,负样本按比例 w(比如 10%)随机保留,训练快很多。但模型学到的是采样后的分布,输出要校正回去:
q = p / (p + (1 − p) / w)
其中 p 是模型在采样数据上的输出,q 是校正后的真实点击率估计。
特征:
| 类别 | 例子 |
|---|---|
| 用户 | 兴趣标签、近期行为、设备、地区 |
| 广告 | 广告主、广告计划、创意、类目、落地页 |
| 上下文 | 版位、页面内容、时间、网络 |
| 交叉 | 用户 × 广告类目、版位 × 创意尺寸 |
| 历史统计 | 广告、广告主、版位在近 1 天 / 7 天的点击率 |
高基数 ID(广告 ID、用户 ID)用哈希分桶或 embedding。历史点击率要平滑:(点击 + α) / (曝光 + α + β),否则一个只曝光了 3 次、碰巧被点了 1 次的新广告会得到 33% 的点击率。所有统计特征都只能用曝光时刻之前的数据。
标签的时间窗口:点击通常发生在曝光后几分钟内。曝光日志和点击日志用曝光 ID 做流式拼接,等待窗口(比如 30 分钟)内没有点击就记为负样本。窗口越长,迟到点击被误标为负样本的情况越少,但训练数据也越晚到达。转化(购买、下载)可能晚几天,预测转化率时这个问题更突出。
4. 模型
- 基线:逻辑回归加人工特征交叉。配合 FTRL 这类在线学习算法,可以在海量稀疏特征上逐条更新,长期是业界主力
- GBDT + LR:把树的叶子节点当作新特征,再交给逻辑回归,自动学到特征组合
- 深度模型:embedding 加特征交叉层(FM、DeepFM、DCN 一类),对稀疏 ID 和高阶交叉更强
持续训练:广告上下线、预算变化、节假日,数据分布每小时都在变。常见做法是每天用较长的窗口全量训练一次,再用最新数据每小时(或流式)增量更新。新广告冷启动依赖广告主、类目、创意内容这类泛化特征。
5. 训练与评估
离线(用前几天训练,后一天验证):
- Log loss:主指标,对概率的绝对值敏感
- 归一化熵(NE):log loss 除以“永远预测平均点击率”时的 log loss。NE < 1 说明比这个基线好,越小越好;它不受整体点击率高低的影响,方便跨时间比较
- AUC:只衡量排序能力
- 校准比:预测点击总数 / 实际点击总数,整体和每个分群都应接近 1
线上 A/B:
- 主指标:千次展示收入(RPM)
- 护栏:点击率、广告主的转化成本、用户体验(广告隐藏率、页面停留时长)
- 注意实验组之间会争抢同一批广告主的预算,结果可能有偏,必要时按广告主或预算拆分实验
6. 服务与监控
- 用户特征在请求开始时取一次,所有候选广告共用;广告特征常驻内存
- 轻量模型先砍掉大部分候选,重模型只给几百个打分
- 曝光和点击拼接成样本后进入流式训练,新模型验证通过后热更新
监控:
- 每小时按分群检查校准比,偏离超过阈值(比如 ±10%)自动告警,必要时回滚到上一个模型
- 特征缺失率:上游日志延迟时,统计特征会大面积缺失
- 预测点击率与实际点击率的分布
- 异常流量:机器人点击会污染标签,进入训练前先过滤无效流量
7. 回答分级
目标与校准
- Bad:当作普通排序题,只看 AUC。
- Good:说明 pCTR 进入 eCPM 和扣费,所以必须校准;用 log loss 和校准比评估。
- Great:讲清 GSP 扣费和 pCTR 的关系,按分群监控校准;负样本降采样后用公式校正概率。
数据与特征
- Bad:直接用原始点击率作特征,不考虑小样本噪声和数据穿越。
- Good:用户、广告、上下文、交叉特征齐全;高基数 ID 用哈希或 embedding;历史点击率做平滑。
- Great:设计曝光和点击的流式拼接与等待窗口,讨论迟到点击和数据新鲜度的取舍;提到转化类目标的延迟反馈。
训练与上线
- Bad:每周离线训练一次就上线。
- Good:每天全量训练加小时级增量更新,A/B 看收入和用户体验。
- Great:在线学习、校准监控与自动回滚、无效流量过滤;指出实验组之间争抢预算带来的偏差。
8. 不同级别的期望
| 级别 | 期望 |
|---|---|
| Mid(L4) | 在提示下给出二分类建模、主要特征和 log loss / AUC 评估,知道样本极不均衡 |
| Senior(L5) | 主动说明校准的必要性,处理负样本降采样的校正、统计特征平滑、样本拼接和持续训练 |
| Staff(L6+) | 把模型放进整个竞价系统里讨论:扣费、预算、广告主效果、实验偏差、监控与回滚,以及新鲜度和成本的取舍 |