Skip to content
Charles Shao
Go back

Pacing 目标曲线:时段消耗权重怎么设计

views

本文属于 Bidder 竞价服务架构 系列,是 预算 Pacing:PID 控制与消耗平滑 的姊妹篇。

Pacing 那篇讲的是控制问题:给定一条目标消耗曲线 r(t),如何用 PID 闭环把实际消耗跟住它。但它把「r(t) 到底怎么画」一笔带过——只说了「流量加权最常用」。本篇专门补上这半张图:目标曲线 r(t) 背后的时段消耗权重 w(τ) 该怎么设计。建议先读 Pacing 那篇 的第二节,再回到这里深入。

Pacing 有一句反直觉的经验:很多 pacing 跑不稳,根子不在控制器,而在目标曲线画错了。 PID 再好,也只是在「执行纠偏」——它假设你给的目标曲线本身是合理的。如果 r(t) 让 Bidder 在一个根本没多少流量的时段去追一个很高的目标,控制器只会徒劳地把出价系数 α 拉满、追不上、积分饱和,最后在流量回来时剧烈过冲。

所以「怎么定权重」是 pacing 的上半场。这是一个预测问题:预估「今天每个时段值得花多少预算」,把它累积、归一化成目标曲线。本篇就围绕这个 w(τ) 展开。

TL;DR

Table of contents

Open Table of contents

一、先厘清:目标曲线和权重是什么关系

Pacing 的目标曲线 r(t) 定义了「到时刻 t,累计消耗应该达到日预算的百分之几」,它是控制器的设定值(setpoint)。而 r(t) 不是凭空画的,它是把每个时段的消耗权重 w(τ) 累积、再归一化得来:

         Σ(τ ≤ t) w(τ)              w(τ) = 分配给 τ 时段的消耗权重(相对量)
r(t) = ──────────────────  ∈ [0,1]   分母 = 全天权重之和 → 保证 r(T) = 1(收盘恰好花完)
         Σ(all τ)  w(τ)

所以:

设计 pacing 目标曲线,就等于设计 w(τ) 剩下的问题只有一个:w(τ) 该正比于什么?

二、权重对齐什么:量、可得量、还是价值量

「值得花多少」有三种口径,直接决定曲线往哪个时段倾斜:

三种加权口径下的时段权重形状对比图:横轴为投放时段 0~24 小时,纵轴为该时段权重占日预算的百分比;蓝线『量(请求数)』呈上午与晚间双峰、触达相对均匀,绿线『可得(请求 × 赢率)』形状类似但晚间被放大(夜间竞争小、赢率高),紫线『价值(请求 × 赢率 × 转化价值 ÷ 价格)』明显向晚间高转化时段倾斜、上午被压低;三条曲线下的面积都等于 100%,即花完同一笔日预算,差别只在时段分配 三种口径下的 w(τ) 形状:面积都等于 100%(花完同一笔预算),差别在「往哪个时段倾斜」。纯量触达均匀但高峰更贵;价值向晚间高 ROI 倾斜但更集中。

口径w(τ)曲线特征适合风险
量(流量)该时段可竞价请求数触达均匀、节奏平稳品牌 / 覆盖类;默认起点高峰流量大但也最贵,单位成本偏高
可得量请求数 × 预估赢率贴合「真能买到的量」竞争激烈、赢率时段差异大依赖赢率预估
价值量请求数 × 赢率 × 转化价值 ÷ 成交价向高 ROI 时段倾斜效果 / 转化类易塌缩成 ASAP,丢覆盖、丢样本

2.1 为什么纯流量加权不是最优

直觉上「按流量分布花钱」很合理——流量多的时段多花。但流量多的时段(上午/晚间高峰)也是竞争最激烈、成交价最高的时段。纯按量加权,等于主动把更多预算投进最贵的窗口,单位成本吃亏。

2.2 为什么纯价值加权也危险

那干脆按「每块钱能买到多少价值」加权?从单次优化看这最优——钱该往边际 ROI 最高的时段堆。但它会带来两个隐患:

2.3 实践:价值打底 + 向均匀回拉

生产里几乎都是折中:以可得量或价值量为骨架体现「往哪倾斜」,再用一个混合系数 λ均匀回拉,保证覆盖与学习。写成一步就是:

w(τ) = λ · w_value(τ)  +  (1 − λ) · uniform          # λ ∈ [0,1]
       └ 效率:向高价值倾斜 ┘   └ 稳健:保覆盖/保样本 ┘

λ 越小越安全(越接近均匀),越大越激进(越向价值集中)。从保守起步、看效果再逐步加大,是稳妥的上线路径。

三、离线构造管线:从历史日志到 r(t)

把上面的想法工程化,就是一条离线批处理管线,每天(或更细)跑一次,产出次日各 campaign 的目标曲线:

时段权重离线构造管线流程图:从左到右六个步骤依次相连——① 聚合历史(近 N 天、按时段 slot 统计请求数/赢率/价值)→ ② 原始加权(w_raw = 请求 × 赢率的 a 次方 × 价值的 b 次方)→ ③ 跨天平滑(EMA、剔除大促/故障/爬虫等异常日)→ ④ 混合回拉(λ 乘 w_raw 加 1 减 λ 乘 uniform)→ ⑤ 地板/天花板(把每格占比 clamp 到 floor 与 cap 之间)→ ⑥ 归一化(除以权重之和得到 r(t),保证 r(T)=1);下方两条旁注说明 a、b、λ、floor、cap 各是什么旋钮,以及这是天级慢更新、冷启动降级为 uniform 权重构造管线:聚合 → 加权 → 平滑 → 回拉 → 限幅 → 归一化a,b,λ,floor,cap 是把「效率 vs 稳健」拧到合适位置的旋钮。

逐步拆开:

  1. 聚合历史(近 N 天):按时段 slot 统计 {请求数, 赢率, eCPM/成交价, 转化率/价值}slot 粒度常取 15 分钟或 1 小时——细粒度更贴流量形状,但单格样本少、噪声大,小预算 campaign 宜用小时级。
  2. 原始加权w_raw(τ) = req(τ) × winrate(τ)^a × value(τ)^ba,b 控制向「可得/价值」倾斜的强度,a=b=0 就退回纯流量。
  3. 跨天平滑:单日数据噪声大,用跨天 EMA 或加权平均降噪;剔除异常日(大促、故障、爬虫刷量),否则一次异常会污染很多天的模板。
  4. 混合回拉w = λ·w_raw + (1−λ)·uniform,把曲线往均匀拉一点,保覆盖、保样本(见 2.3)。
  5. 地板 / 天花板clamp 每个 slot 的占比到 [floor, cap] 再重新归一化。floor 防某时段被压到 0(那段完全不投、留覆盖空洞);cap 防塌缩成 ASAP(单时段占比过高)。
  6. 归一化w(τ) / Σw(τ),累积即得 r(t),天然满足 r(T)=1(收盘恰好花完)。

一句话本质

这条管线做的,是把「哪个时段值得花」这个模糊判断,变成一条受约束的、可复现的目标曲线:用 a,b 表达倾向,用 λfloor/cap 兜住底线,最后归一化保证钱不多不少。

四、按日型与时区分别建模

一个极其常见、又极其致命的错误是:用一条曲线覆盖所有日子。一天的流量形状强烈依赖「今天是星期几」:

工作日与周末时段权重形状对比图:横轴为投放时段 0~24 小时、纵轴为该时段权重占日预算百分比;红线『工作日』呈早晚通勤双峰、起床早(约 8 点早高峰、19 点晚高峰);绿线『周末』起得晚、没有早高峰,白天缓慢抬升成一个从中午到傍晚的宽单峰;图注强调用工作日模板去投周末会把早高峰的预算砸进一个没有早高峰的日子 工作日与周末的流量形状截然不同:工作日早晚通勤双峰,周末起得晚、白天单个宽峰。权重必须按「日型」分别建。

进一步可以做「星期几」逐日建模(周五晚和周日晚的形态也不一样),但收益递减,工程上先把「工作日/周末 + 节假日覆盖」做好,性价比最高。

一句话本质

一天的形状取决于「今天星期几、受众在什么时区」。按日型 + 时区分别建模,是最便宜、收益最大的一步——它把预测误差从「结构性错配」压到「同型内的小抖动」。

五、预测与控制分工:残差归 PID,系统性偏差归权重

w(τ)预测,现实总会偏离预测。关键设计原则是:别让 PID 替预测的错误擦屁股。预测和控制是两条不同时间尺度的回路,各管各的:

预测与控制两条回路分工示意图:上方一条主链从左到右为——预测权重 w(τ)(离线、天级)→ 目标曲线 r(t)(归一化累积)→ PID 执行纠偏(出价系数 α、秒级)→ 实际消耗 y(t);从 y(t) 引出两条虚线反馈回路,一条蓝色快回路标注『小残差:秒级实时纠偏』直接回到 PID,一条粉色慢回路标注『系统性偏差』先到下方的『离线重估 w(τ):校正系统性偏差』节点、再『天级回灌重估』回到预测权重 w(τ);底部旁注说明预测负责把曲线画准、控制负责跟住曲线,系统性偏差回灌重估、随机残差交给 PID 两条回路:秒级快回路(残差 → PID 实时纠偏)与天级慢回路(系统性偏差 → 离线重估 w(τ))。分工清楚,pacing 才稳。

维度随机残差系统性偏差
特征无规律抖动、时正时负每天同一时段稳定同号偏离
例子某分钟赢率抖一下、今晚高峰略弱每天晚 8 点都追不上目标
归谁管PID(秒级快回路)重估 w(τ)(天级慢回路)
动作实时调 α 吸收回灌离线管线修正权重

六、伪代码:设计思路落地

把前面的设计落成伪代码——重点是说明思路,不是实现细节。一个核心事实贯穿始终:权重构造是离线批处理(每天跑一次,产出次日各日型的目标曲线),在线只读——出价路径绝不做这些计算。分三块看:离线怎么构造 w(τ)、在线怎么查 r(t)、慢回路怎么判断该不该重估。

6.1 离线构造器:管线骨架

对应第三节那张管线图,每一步都标了「为什么这么做」:

# 离线批处理:为「每个日型」各产出一条归一化权重 w(τ)(Σ=1)
# 设计意图:脏活累活全在离线,用 a/b/λ/floor/cap 几个旋钮把「效率 ↔ 稳健」拧到位

FUNCTION build_weights(history, cfg) -> { day_type: w[n_slots] }:
    uniform ← 每格相等的均匀权重                       # 兜底 + 回拉的基准

    FOR EACH day_type IN { 工作日, 周末, 节假日 }:     # 关键:按日型分别建,别一条曲线通吃
        rows ← history 中属于该 day_type 的记录

        IF 历史天数 < 阈值:                            # 冷启动:宁可降级,也不用脏数据硬算
            w[day_type] ← 同类目模板 或 uniform
            CONTINUE

        # ── 管线六步 ──
        raw ← FOR EACH slot: req × 赢率^a × ROI^b      # ①② 聚合 + 加权:a,b 定倾向
                                                       #    req→量 · ×赢率→可得 · ×ROI→价值(含 ÷价格)
        raw ← EMA_跨天(raw) 且 剔除异常日              # ③ 平滑:大促/故障/刷量不许污染模板
        w   ← λ·normalize(raw) + (1−λ)·uniform         # ④ 向均匀回拉:λ 小=稳、λ 大=激进
        w   ← clamp 每格占比到 [floor, cap]×均匀       # ⑤ 限幅:floor 防覆盖空洞、cap 防塌成 ASAP
        w[day_type] ← normalize(w)                     # ⑥ 归一化:Σ=1 → 收盘恰好花完

    RETURN w

一眼能看出设计取舍全在几个旋钮上:a,b 决定往「可得 / 价值」倾斜多少,λ 决定往均匀回拉多少,floor/cap 兜住覆盖与集中度的底线。异常日剔除、跨天 EMA 这类实现细节,思路上只需记住「历史要抗噪、要抗突发」即可。

6.2 在线查询:把 w(τ) 变成此刻的 r(t)

在线路径只做一件事:查「到此刻累计该花到多少」,当作 PID 的设定值。slot 内线性插值,避免目标曲线出现台阶:

# 设计意图:复杂度留给离线,在线只留一次 O(1) 查表 + 插值

预热(服务启动时):
    FOR EACH day_type: cum[day_type] ← 前缀和(w[day_type])    # 累计曲线 r(t),cum[末]=1.0

FUNCTION target_ratio(now) -> r(t) ∈ [0,1]:
    w, cum ← 按 now 选对应日型的表            # 日型 / 时区在这里对齐受众
    (k, frac) ← now 落在第 k 个 slot 的 frac 处
    RETURN cum[k] + frac × w[k]              # slot 内线性插值,交给 PID 当设定值

6.3 天级慢回路:判断该不该重估

第五节的分工在这里落地:偶发抖动交给 PID,只有多天、同一时段、稳定同号的偏差才算系统性、才值得回去重估权重。

# 设计意图:把「随机残差」和「系统性偏差」分开 —— 判错了要么过度反应、要么长期跑偏

FUNCTION need_reestimate(近 N 天: 目标增量[day][slot], 实际增量[day][slot]) -> [坏 slot]:
    FOR EACH slot:
        resid ← 实际 − 目标                              # 该 slot 各天的残差
        IF |mean(resid)| > 阈值 AND 同号天数占比 > 70%:  # 又大又稳 → 系统性
            标记该 slot
    RETURN 被标记的 slot

# 命中的 slot → 下次离线 build 时修正(调其观测权重 / 排查预测源),
# 而不是让在线 PID 天天在这些时段硬顶 —— 硬顶治标不治本,还会诱发积分饱和。

一句话本质

全部设计意图都收敛到几个旋钮:a,b 定倾向、λ 定回拉、floor/cap 定底线、EMA/剔异常 定抗噪。而三条边界自始至终不变:离线算 / 在线读按日型建模残差归 PID · 系统性偏差归权重

七、几个必须处理的坑

八、速查表

设计一条好的 pacing 目标曲线,本质是回答「今天这笔钱,各个时段各值得花多少」——用价值量表达倾向、用均匀回拉与地板天花板兜住覆盖和稳健、按日型时区分别建模,再把它当一个天级慢更新的预测,把秒级残差留给 PID。目标不是把曲线预测得分毫不差,而是让它顺着真实价值分布走、同时不塌缩、不留空洞——剩下的偏差,交给 PID 闭环 去抹平。


延伸阅读

一手资料与背景:

附录:术语表


views
Share this post on:

Previous Post
程序化广告投放层级与定向配置:Campaign 层级模型、定向继承与缺省语义
Next Post
Bidder 预算 Pacing:PID 控制与消耗平滑