竞价策略:从 GSP 到 VCG
约 3938 字大约 13 分钟
2025-11-05
🎯 出价策略的角色:连接预测与竞价
在第三章里,我们已经用 Wide&Deep、DeepFM、DIN/DIEN 等模型学会了如何预测 pCTR/pCVR; 在本章前两节里,我们又用 eCPM、Score、GSP/VCG、RTB 搭起了“竞价 + 计费 + 生态”这套框架。
现在还差最后一块拼图:
在预算和 ROI 目标已定、pCTR/pCVR 已有的前提下,DSP/广告主到底该出多少价?
换句话说,本节要回答的是:
- 固定给一个 CPC/CPA 出价,到底意味着什么?
- 如何从 目标 ROI/ROAS/CPA 反推“合理的出价区间”?
- oCPC/oCPA 这种“托管式出价”在数学上是在解什么问题?
- 在多创意、多广告系列和不确定环境中,如何在“探索”与“利用”之间平衡?
本节会以“直觉 → 数学 → 数值示例 → 工程实现”的节奏,系统梳理常见出价策略,并和前文的 pCTR/pCVR、eCPM、RTB 机制形成一个闭环。
🧱 出价策略概览:从拍脑袋到算出来
从简单到复杂,常见的出价策略大致可以分为几类:
- 固定出价 (Fixed Bid):广告主直接给一个固定 CPC/CPA 价格。
- 基于 ROI/ROAS 约束的出价:根据目标 ROI/ROAS 反算出“理论最优 bid”。
- oCPC/oCPA:广告主只给目标 CPC/CPA 或 ROI,由平台自动调节具体出价。
- 动态出价 (Dynamic Bidding):出价随时间段、流量质量、库存压力等实时变化。
- 带探索的出价 (Bandit/RL):在多创意、多策略之间做探索与利用权衡,如 UCB、Thompson Sampling、强化学习策略等。
下面逐个展开。
🧮 固定出价(Fixed Bid):最朴素也最容易踩坑
直觉与场景
最简单的策略是:
不管流量好坏、时段差异、设备差异,永远以同一个单价出价。例如:CPC 恒定为 1 元。
这在小预算、冷启动或强人工投放场景下很常见,也便于理解和控制。
数学刻画
假设采用 CPC 模式,单次点击出价固定为 b,模型给出的 pCTR 为 pCTR,则:
eCPM(术语篇公式):
eCPM=b⋅pCTR⋅1000.
若每次转化价值为 v,pCVR 为 pCVR,则 期望 ROI 为:
ROI=b⋅pCTRv⋅pCTRpCVR−b⋅pCTR=bvpCVR−b.
可以看到,如果 pCVR 偏低或出价 b 偏高,ROI 很容易变成负数。
数值示例
假设:pCTR=1%,pCVR=5%,每次转化价值 v=100 元,固定出价 b=1 元。
- eCPM:
eCPM=1×0.01×1000=10元/千次曝光.
- 期望 ROI:
ROI=1100×0.05−1=4.
ROI=4(400%)看起来很美好,但前提是 pCTR/pCVR 估计准确;一旦模型高估 pCVR,真实 ROI 可能大打折扣。
优点:简单可控;缺点:不会自动适配流量质量和业务目标,容易在高质量流量“出价偏低拿不满”、低质量流量“出价偏高亏钱”。
🧮 基于 ROI/ROAS 约束的出价:从目标倒推 bid
在术语篇里我们已经推导过:在 CPC + 转化价值 v 的场景下,对单次点击而言:
ROI=bvpCVR−b.
若广告主给出 期望 ROI 阈值 R∗,并要求:
ROI≥R∗,
则可以反推出 合理的最大出价:
bvpCVR−b≥R∗⟺vpCVR≥b(1+R∗)⟺b≤1+R∗vpCVR.
我们可以把右侧记作:
b∗(pCVR)=1+R∗vpCVR,
这就是在目标 ROI 约束下的理论最优 CPC 出价上界。
数值示例
假设:pCVR=10%,每次转化价值 v=100 元,目标 ROI R∗=1.5(即希望每花 1 元赚回 2.5 元营收)。
则:
b∗=1+1.5100×0.1=2.510=4元/点击.
如果再结合 pCTR = 2%,则对应的 eCPM 为:
eCPM=4×0.02×1000=80元/千次曝光.
在工程上,很多 手工出价 + 智能建议 产品,其实就是在帮广告主把“目标 ROI → 推荐出价区间”这一步算出来。
🧠 oCPC / oCPA:把复杂度交给平台
直觉与业务形态
在 oCPC/oCPA 模式下,广告主通常只需要设定:
- 目标 CPC/CPA(例如目标 CPA = 20 元);
- 或 目标 ROI/ROAS;
平台则会根据实时流量质量、历史表现和预算消耗节奏,自动调整实际出价 bt:
bt=mt⋅bbase,
其中 bbase 可以是上一小节推导的 b∗(pCVR),mt 则是一个随时间动态调整的 出价系数。
基于 CPA 偏差的简单控制律
设:
- 某一时间窗口内观测到的 历史 CPA 为 CPAhist;
- 目标 CPA 为 CPAtgt;
- 当前出价系数为 mt。
一个直观的更新方式是:
mt+1=mt⋅(CPAhistCPAtgt)α,0<α≤1.
- 若 CPAhist>CPAtgt(实际 CPA 偏高),则括号小于 1,mt+1<mt,出价整体下调;
- 若 CPAhist<CPAtgt(实际 CPA 偏低),则出价略有提升,以换取更多流量;
- 指数 α 控制调节的“激进程度”,α 越小,调整越平滑。
实际系统中还会对 mt 加上下限与上限(如 mt∈[0.2,3.0]),避免出价振荡过大。
数值示例
假设目标 CPA 为 20 元,上一个窗口观测到的实际 CPA 为 25 元,当前 mt=1.0,α=0.5。
则
mt+1=1.0⋅(2520)0.5≈1.0⋅0.894=0.894.
即下一个窗口的整体出价系数约为 0.894,相当于出价整体下调约 10.6%。
下面给出一个极简的 oCPA 出价更新示例代码,便于在实验环境中快速跑通。注意:真实线上实现会复杂得多,这里只是“教科书级”示意。
💻 oCPA 出价系数更新示例(点击展开)
def update_bid_multiplier(m_t, cpa_hist, cpa_target, alpha=0.5,
m_min=0.2, m_max=3.0):
"""根据历史 CPA 与目标 CPA 更新出价系数 m_t。"""
if cpa_hist <= 0 or cpa_target <= 0:
return m_t
ratio = cpa_target / cpa_hist
m_next = m_t * (ratio ** alpha)
# 限制在合理区间,防止出价振荡
m_next = max(m_min, min(m_max, m_next))
return m_next
if __name__ == "__main__":
m = 1.0
history = [25, 22, 19, 18] # 不同时间窗口观测到的 CPA
target = 20
for cpa in history:
m = update_bid_multiplier(m, cpa, target)
print(f"CPA={cpa:.1f}, new m={m:.3f}")🔄 动态出价:跟随流量质量与时间变化
在真实系统中,流量质量随 时间段(早/晚高峰)、设备(iOS/Android)、人群特征 等剧烈变化。理想情况下,我们希望:
同一个广告,在高质量流量上出价更高,在低质量流量上出价更低。
最朴素的形式是:
b(x)=mt⋅1+R∗v⋅pCVR(x),
其中 x 表示当前曝光的上下文(时间、设备、人群等),pCVR(x) 为在该上下文下预测的转化率。
为避免出价过于抖动,通常会对原始出价进行 指数平滑:
b~t=(1−β)b~t−1+βbtraw,0<β≤1,
其中 btraw 为按公式直接计算的出价,b~t 为实际生效的平滑出价。
💻 简单的出价平滑函数(点击展开)
def smooth_bid(b_prev_smooth, b_raw, beta=0.3):
"""对原始出价做指数平滑,减少抖动。"""
if b_prev_smooth is None:
return b_raw
return (1 - beta) * b_prev_smooth + beta * b_raw
if __name__ == "__main__":
b_smooth = None
raw_bids = [1.0, 1.8, 0.5, 1.2]
for b in raw_bids:
b_smooth = smooth_bid(b_smooth, b)
print(f"raw={b:.2f}, smooth={b_smooth:.2f}")动态出价在工程上还需要与 Pacing(预算节奏控制) 联动:常见做法是将 Pacing 的乘子 mt(pacing) 与 ROI/oCPA 的乘子 mt(ROI) 相乘:
bt=mt(pacing)⋅mt(ROI)⋅bbase,
通过两个维度的控制同时保证“花得稳”和“花得值”。
🎲 探索 vs 利用:多创意、多策略下的出价试探
当同一广告系列下有多个创意、多种定向配置时,问题就变成了一个典型的 多臂老虎机 (Multi-Armed Bandit, MAB):
每个“臂”(创意/策略)有未知的真实收益率 μk,我们一边拉臂一边估计 μk,目标是在有限曝光/点击预算下,使累计收益最大。
UCB:上置信界算法
设:
- 第 t 轮时,第 k 个臂被尝试的次数为 nk(t);
- 历史平均收益(如平均点击率或转化率)为 μ^k(t)。
UCB1 的经典形式为:
UCBk(t)=μ^k(t)+cnk(t)2lnt,
其中 c>0 控制探索强度。每一轮选择 UCB 最大的臂:
kt=argkmaxUCBk(t).
数值示例(三条创意 CTR UCB):
在第 t=100 轮时,有三条创意 A/B/C:
- A:nA=60,μ^A=3%;
- B:nB=30,μ^B=4%;
- C:nC=10,μ^C=5%。
取 c=1,则:
UCBA≈0.03+602ln100,UCBB≈0.04+302ln100,UCBC≈0.05+102ln100.
因为 C 的试验次数最少,其置信区间更宽,上置信界更大,算法会继续“多给它一些机会”。
Thompson Sampling:贝叶斯视角下的探索
另一种常用策略是 Thompson Sampling。以 CTR 为例,可对每个创意 k 维护一个 Beta 分布:
θk∼Beta(αk,βk),
- 其中 αk 代表历史点击次数 + 先验;
- βk 代表历史未点击次数 + 先验。
每一轮:
- 对每个创意 k 采样 θ~k∼Beta(αk,βk);
- 选择采样值最大的臂 kt=argmaxkθ~k;
- 观察是否被点击,若点击则 αk+=1,否则 βk+=1。
Thompson Sampling 自然地在高不确定性臂上分配更多探索流量,并在证据积累后逐渐收敛到最佳臂。
💻 简单的 CTR Thompson Sampling 示例(点击展开)
import random
from collections import defaultdict
def choose_arm(alpha, beta):
"""根据当前 Beta(α, β) 参数,为每个臂采样并选择最大者。"""
sampled = {}
for k in alpha.keys():
# 这里用 random.betavariate 作为 Beta 采样
sampled[k] = random.betavariate(alpha[k], beta[k])
return max(sampled, key=sampled.get)
if __name__ == "__main__":
# 三个创意的真实 CTR
true_ctr = {"A": 0.02, "B": 0.03, "C": 0.05}
alpha = defaultdict(lambda: 1.0)
beta = defaultdict(lambda: 1.0)
for t in range(1000):
arm = choose_arm(alpha, beta)
click = random.random() < true_ctr[arm]
if click:
alpha[arm] += 1.0
else:
beta[arm] += 1.0
print("Posterior alpha:", dict(alpha))
print("Posterior beta:", dict(beta))在真实 DSP 中,可以把“臂”理解为“创意 × 人群 × 出价策略”组合,通过这种方式在不确定环境下 在线优化出价方案。
🧪 强化学习视角:把出价当作一个动作决策
从更宏观的视角看,一场广告投放可以抽象成一个 马尔可夫决策过程 (MDP):
- 状态 st:包括当前预算已消耗比例、时间段、累积 ROI、流量环境特征等;
- 动作 at:例如选择一个出价系数 mt,或在几个出价策略中二选一;
- 回报 rt:某个时间窗口内的利润、转化数、或 ROI 的函数;
- 转移:预算消耗与流量到达共同决定了 st+1。
目标是在给定时间范围内最大化期望总回报:
πmaxEπ[t=0∑Tγtrt],
其中 π(a∣s) 是出价策略(策略网络),γ 为折扣因子。
在工程上,常见的做法包括:
- 用 Q-learning / DQN 近似 Q(s,a),选择出价动作;
- 用 策略梯度 / Actor-Critic 直接优化参数化策略 πθ(a∣s);
- 在模拟环境中预训练,再上线做小流量试验。
本节不展开具体 RL 算法细节,但需要记住:
强化学习本质上是在做“长程回报最大化”,特别适合同时考虑“短期转化 vs 长期预算 vs 用户体验”的多目标出价问题。
🛠 工程实践:出价平滑、防抖与预算联动
在真实系统中,出价策略需要和多种工程因素协同:
平滑与防抖动:
- 对 bt 或 mt 做指数平滑(见前文公式);
- 限制相邻时间窗口出价变化比例,如 bt/bt−1∈[0.5,1.5]。
与 Pacing 联动:
- Pacing 控制“花得快不快”,出价控制“花得值不值”;
- 常见做法是把 Pacing 的乘子与 ROI/oCPA 的乘子合并到同一个 bt 公式中(见前文)。
冷启动与数据稀疏:
- 新创意/新广告系列历史样本少,pCTR/pCVR 不稳定;
- 可通过 增加先验(Beta 先验) 或 提高探索权重(UCB/TS) 给更多试验机会;
- 同时在出价策略上设置保守上下限,防止因少量样本导致极端调价。
模型偏差的鲁棒性:
- 若发现 pCTR/pCVR 系统性高估,可在出价公式中加入“安全折扣因子” κ<1:
bsafe(x)=κ⋅1+R∗v⋅pCVR(x).
- 通过对比线上/离线 ROI,定期校准 κ。
- 若发现 pCTR/pCVR 系统性高估,可在出价公式中加入“安全折扣因子” κ<1:
📖 延伸阅读
- A Survey on Bidding Strategies in Real-Time Bidding 系统综述 RTB 场景下的各种出价策略和在线学习方法。
- Optimizing Cost per Click in Real-Time Bidding 聚焦 oCPC/oCPA 背后的优化问题与算法设计。
- Multi-Armed Bandit Algorithms and Empirical Evaluation 对 UCB、Thompson Sampling 等探索算法做了详尽分析与实验。
- 若对强化学习方向感兴趣,可参考 Reinforcement Learning: An Introduction 中关于策略梯度和 Actor-Critic 的章节,并思考如何迁移到预算约束出价场景。
🧠 思考题
- 在 oCPC 模式下,如果观测到实际 CPA 持续高于目标 CPA,你会如何区分是 出价策略问题,还是 pCVR 模型高估 所致?针对这两种情况,你分别会采用什么调整手段?
- 针对冷启动创意,你会如何设计 UCB 或 Thompson Sampling 的先验参数,使其既能获得足够探索,又不会因为少量负样本被过早“打入冷宫”?
- 在预算有限且强约束 ROI 的场景下,如何设计一个把 Pacing 乘子 与 ROI 乘子 统一到同一公式中的出价策略?其目标函数和约束分别是什么?
- 如果发现 pCTR/pCVR 模型在某些人群上系统性偏高,你会如何通过 分段折扣因子 或 人群级别的校准模型 把这种偏差反映到出价策略中?
- 在多广告主、多 DSP 竞争的环境下,如果所有参与者都采用激进的 UCB/高出价策略,是否可能出现类似“囚徒困境”的恶性竞价?你会如何通过机制设计或策略约束避免这种情况?
🎉 章节小结
本节从 “给多少出价” 这个看似简单的问题出发,系统梳理了多种出价策略及其数学本质:
- 从固定出价,到基于 ROI/ROAS 约束反推最优 bid,再到 oCPC/oCPA,把广告主的业务目标转化为可执行的出价规则;
- 结合 pCTR/pCVR 预测与 eCPM/Score 公式,说明了出价策略如何嵌入 RTB 与 GSP/VCG 的竞价机制之中;
- 通过多臂老虎机与强化学习视角,把多创意、多人群、长期预算约束下的出价优化,统一到“在线决策与探索”框架下;
- 在工程实践部分,强调了平滑、防抖、预算联动与模型偏差鲁棒性,帮助出价策略从“纸面公式”走向稳定可用的线上系统。
掌握本节内容后,你可以把第三章的 pCTR/pCVR 模型、第四章术语篇的指标体系、生态篇的 DSP/SSP/ADX 结构,与本节的出价策略组合起来,构建起一整套 “会预测、懂计费、能出价”的现代计算广告系统心法”。