cswork / 系统设计

第 26 章 / 共 57 章 · 约 6 分钟

ML 案例 3:广告点击率预估

本页目录
  1. 1. 需求澄清
  2. 2. 问题形式化
  3. 3. 数据与特征
  4. 4. 模型
  5. 5. 训练与评估
  6. 6. 服务与监控
  7. 7. 回答分级
  8. 8. 不同级别的期望
  9. 章节测验

先花 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. 模型

  1. 基线:逻辑回归加人工特征交叉。配合 FTRL 这类在线学习算法,可以在海量稀疏特征上逐条更新,长期是业界主力
  2. GBDT + LR:把树的叶子节点当作新特征,再交给逻辑回归,自动学到特征组合
  3. 深度模型: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+)把模型放进整个竞价系统里讨论:扣费、预算、广告主效果、实验偏差、监控与回滚,以及新鲜度和成本的取舍

章节测验

6 道选择题
  1. 为什么广告点击率预估比推荐排序更强调校准?
  2. 负样本按 w = 0.1 降采样后,模型输出 p = 0.05。校正后的真实点击率约为多少?
  3. 归一化熵(NE)小于 1 说明什么?
  4. 一个新广告只曝光了 3 次、被点击 1 次。把它的历史点击率作为特征时应该怎么处理?
  5. 曝光和点击做流式拼接时,等待窗口设得越长,会怎样?
  6. 为什么广告点击率模型通常要小时级甚至流式更新?

章节讨论

正在加载讨论…