Skip to content
Charles Shao
Go back

冷启动:程序化栈每一层都在解同一个问题

views

有一类问题:你必须现在就决策,却没有用来决策的信息。整个 AdTech 栈,不过是同一个问题的不同变体。

如果你在程序化广告团队待过,多半坐过这种会的某个版本:

产品:新广告已经上线 24 小时了,CPI 是预算的 3 倍。模型是不是坏了? 算法:还在学习期,再给两天。 运营:客户说今天下班前 CPA 必须回到目标值。 SRE(小声) 我们还在追昨天 bidder 重启时的 p99 尖刺呢。

四个人,一间屋,给同一件事起了四个名字:冷启动(cold start)

它们看上去毫不相干。结构上却是同一个:**现在就得决策,手里却没有同分布的历史可依。**这篇把四层收进一个统一框架,讲清先验从哪来、决策怎么熬过空窗期、以及”走出冷启动”到底意味着什么。

范围说明:本篇是一篇跨层的方法论,把四副面孔收进一个框架。各层底下的协议细节——RTA 的实时决策模式SKAN / ATT / MMP 归因OpenRTB 的延迟预算——各有专文,这里只在与冷启动直接相关处引用。

TL;DR

Table of contents

Open Table of contents

1. 一个统一的定义

给”冷启动”一个你真能拿来做工程的定义:

你必须现在就决策,手里却没有同分布的历史观测。

三块,每一块都承重:

任何同时命中这三条的东西,都会触发同样的三个问题:先验从哪来、要为探索付多少、兜底是什么。这种”现在就得决策”的处境,和 RTA 里那套实时决策同源——只是这里缺的是数据,那里缺的是回查的时间。

2. 四层:信息在哪里缺失

按”缺的是什么信息”来排,冷启动在 AdTech 栈的四层各自现身:

缺什么时间尺度替代信息典型学习税
广告(业务)本广告内的转化历史天 — 周相似广告先验、行业基线头 50 个转化的预算
模型(算法)逐用户 / 逐物料的特征观测分钟 — 小时元数据、人群均值、look-alike早期决策的次优性
系统(运行时)JIT 编译 / 缓存热度 / 连接池秒 — 分钟预热流量、预热镜像头几分钟的 p99 尖刺
测量(归因)postback / 转化数据周 — 月前置域数据、粗粒度信号头几周次优的 CV 配置

这张表就是本文后半程的导航图。

读这张表的一个提醒:四层里有三层(广告 / 模型 / 归因)缺的是数据,时间尺度是天到月、解法在模型层;只有系统层缺的是运行时热度,时间尺度是秒到分、解法在基建。它和另外三层同名不同源——本文为完整性把它收进同一个框架,但它该走另一条值班链路(§5.3 专门讲这个切割)。如果你只关心”数据稀缺型”冷启动,可以先跳过 §5。

3. 广告冷启动(业务层)

场景:一款新游戏上线,CPI 目标 $3。上线首日实际 $8,运营开始问要不要砍预算。

这是产品和运营聊得最多、也最常误解的那种冷启动。

3.1 为什么”学习期”是真实存在的

Meta 的 Learning Phase(学习期) 经验法则是:一个 ad set 在 7 天内攒够 50 个优化事件,才算”走出学习期”。Google Ads 的 Smart Bidding 和 TikTok 的 ALP(Audience Learning Phase)也有类似阈值。

这些数字不是平台在和稀泥。它们对应的是:一个竞价模型在它的 CTR / CVR 估计的置信区间窄到可以拿来行动之前,所需的最小样本量。

简单算一下。假设真实 CVR 在 1% 左右,要把 95% 置信区间钉在 ±0.5%(50% 相对误差),大约需要

[ n \approx \frac{1.96^2 \cdot p(1-p)}{(0.005)^2} \approx 1500 ]

最少约 1,500 次点击,才能得到一个值得拿来行动的 CVR 估计。1,500 次点击 × ~1% CVR ≈ 15 个转化——而这刚够一个决策拿到一个粗估。要在多个切片维度(geo × 版位 × 人群 × 时段)上都有把握,样本需求会按维度的乘积放大。50 个转化已经是平台为了不让客户放弃而软化过的阈值了。

关键洞察:Meta 和 Google 不是在偷懒。冷启动期间一个”表现失常”的模型,是数学,不是 bug。

3.2 三件套:先验、探索、兜底

**先验(prior)。**一条新广告其实不是从零开始。几乎总有一条相似的已经在跑——同广告主、同垂类、同 geo、同广告形式。把那条老广告的 CVR / CTR 当先验,配上伪计数平滑:

def smoothed_cvr(conversions, clicks, prior_cvr=0.02, prior_strength=500):
    """
    用贝叶斯平滑给冷启动 CVR 估计兜底。

    prior_strength = 把先验当作相当于这么多次历史试验。
    没有数据的广告由先验主导;随着真实样本累积,
    先验的权重会自动衰减。
    """
    return (conversions + prior_cvr * prior_strength) / (clicks + prior_strength)

prior_strength 是唯一真正要紧的旋钮:

常用经验:把 prior_strength 设成”相当于一天的预期点击量”,这样到第二天,真实信号就开始占上风。

**探索(explore)。**两种常见姿态:

import numpy as np

def thompson_sample_arm(alpha, beta):
    """
    每个 arm 维护一个 Beta(alpha, beta) 后验。
    新 arm 初始化为 alpha=1, beta=1(均匀先验 =
    最大不确定性 = 最高的被探索概率)。

    随着 arm 累积试验,它的 Beta 后验收紧,
    探索概率自动衰减。
    没有工程师需要去调一条 epsilon 衰减表。
    """
    return np.random.beta(alpha, beta)

在长尾广告和素材上,Thompson Sampling 几乎总是赢过 epsilon-greedy。原因很简单:**它自己安排自己的探索强度。**一个新上线的 arm 不确定性高,就被频繁采样;几千次拉动之后,不确定性下降,探索概率自行衰减。

**兜底(fallback)。**每一条新广告都在保守约束下上线:

这些不是为了省钱,而是为了封住爆炸半径。万一竞价模型认定某个高 CPM 人群”高质量”、开始猛冲,损失也有界。

3.3 三个经典反模式

反模式为什么错该怎么做
因为第一周难看就砍掉广告冷启动多半还没结束。重开一条新广告,你只是把冷启动从头再来一遍——永远循环。设一个最小观测窗:≥ 50 个转化或 7 天,先到先算。
冷启动期就上细粒度目标(如 tROAS、价值优化)样本需求比 install / 注册优化高几十倍,两周根本不够。冷启动期先优化一个稠密事件,之后再切到细粒度。
拿冷启动数据下归因结论出价、人群、竞价格局全在动,转化数据方差极大。任何 A/B 对比开始前,先跳过学习期。

本节要点:广告冷启动是一道给”搞清楚”这件事定价的题——你愿意花多少钱,去学清楚这条广告值不值得跑。

4. 模型冷启动(算法层)

场景:双十一前两周,一个电商客户上传 5,000 个新 SKU。打开推荐看板——每个新 SKU 的 CTR 都是零,因为还没人点过。

这是推荐系统的经典话题,但 AdTech 版本更难缠。不只是新用户,还有新素材、新媒体、新版位、新人群包。

4.1 三个子类

子类场景模型看到的
用户冷启动新设备 / 新 GAID / 新 IDFA用户侧 embedding 是默认值;个性化坍缩成人群均值
物料冷启动新素材上线、新商品入库物料侧 embedding 未训练;CTR 预测严重偏向冷启动先验
上下文冷启动新媒体接入、新广告位、新供应路径上下文 embedding 缺失;竞价格局估计退化

三者都把模型输出坍缩成”垃圾分数”。倒也不算错——**预测分布坍缩到了先验上。**在这种状态下排序,本质就是按先验偏置排序,模型本该提供的判别力一点都没用上。

4.2 特征缺失时,怎么让模型还能用

**贝叶斯平滑。**永远别给 CTR / CVR 算裸的 (\frac{\text{click}}{\text{impression}}):

def smoothed_ctr(clicks, impressions, prior_ctr=0.01, prior_strength=1000):
    """
    给 CTR 估计做贝叶斯平滑——
    AdTech 入门第一课,也是最常被跳过的一课。
    """
    return (clicks + prior_ctr * prior_strength) / (impressions + prior_strength)

这是 AdTech 入门第一课。可每年都有线上事故出在:某人在一个新看板、新归因报告或新排序器里忘了平滑,把”100% CTR”当成真信号、灌进了下游某个东西。

**人群(cohort)兜底。**当用户级特征没数据时,回退到 device × geo × app × hour 的人群均值。还太稀疏,再往下回退到 geo × hour。连这个样本都太少,回退到全局均值。

不可妥协的那一点:**每一级都得有足够样本才能被用。**规则不是”找到第一级就用”,而是”用第一个样本够多的那一级”。

**元数据驱动的 embedding。**一条新素材没有曝光历史,但它有元数据:广告类型、垂类、品牌、尺寸、视频时长、调性。用这些算一个 zero-shot embedding——取同垂类历史素材 embedding 的均值,给新素材播种。几千次曝光之后,在线学习会把它拉向真实位置。

**look-alike 播种。**用户冷启动的默认动作。挑几百个现有用户做种子(高 LTV 用户、已转化用户、白名单用户),扩展到几十万个相似用户,把种子标签当作扩展集的伪标签。look-alike 是冷启动期的安全网;一旦真实交互数据流进来,让在线模型覆盖它。

4.3 探索 vs. 利用:为什么 Thompson Sampling 几乎总赢

冷启动期的广告排序,天生是为多臂老虎机准备的。和写死 ε = 5% 的 epsilon-greedy 相比,Thompson Sampling 的核心优势是自己安排探索强度

没有工程师需要去调衰减表。而且根本不存在”记得要关掉的探索窗口”,因为压根没有”关”这个动作。

代价是:你得在生产里维护每个 arm 的 ((\alpha, \beta)) 状态。那是一条 KV 写路径,高 QPS 下要批量刷写——典型做法是把写聚合成每秒一次刷进 Redis 或 Aerospike,避开热 key。

4.4 修不掉的代价:选择偏差

冷启动数据有一个绕不开的问题:**你只能看到你竞胜了的那些曝光的反馈。**模型从冷启动里学到的一切都带着选择偏差。这个偏差会持续把它往回推——在冷启动早就结束之后——推回到它当初恰好赢下的那批流量上。

你能缓解,消不掉:

本节要点:模型冷启动是给”特征缺失”造一张理性悲观的安全网。别让”没数据”变成”随机出价”。

5. 系统冷启动(运行时层)

场景:一个新 bidder 构建灰度上线,金丝雀 1% 流量。p99 从 30ms 跳到 120ms,冲破 SSP 的 100ms 硬超时。运营立刻回滚。

这一节我们从”算法说的冷启动”切到”SRE 说的冷启动”。这是完全不同的工程问题,而且它们绝不该共用一个看板

5.1 长驻服务:bidder / 排序器 / 特征服务

对常驻服务,启动后头几分钟的 p99 通常是稳态的 3–5 倍。四个根因:

根因症状时间尺度
JIT 预热HotSpot 默认:C1 编译阈值 1,500、C2 阈值 10,000。热路径要在解释器里跑几万次才完全优化头 1–5 分钟
连接池冷启动首个请求触发 TCP 三次握手 + TLS 握手 + 池初始化;到 Redis / DB / 下游 RTA 的首请求延迟高 1–2 个数量级头几秒到几分钟
本地缓存冷启动内存里的用户状态 / 特征 / 出价历史缓存全空;每个请求都打到下游存储看缓存大小,通常 5–30 分钟
GC 启发式未收敛G1 / ZGC 的自适应启发式要观察几个 GC 周期才能定下 region 大小和新生 / 老年比头几个 GC 周期

生产实践(按推荐顺序):

  1. 预热流量 + 就绪探针:启动后、加入 LB 之前,回放一片影子流量——镜像的生产请求,响应丢弃。就绪探针只在 JIT 预热、连接打开、缓存填好之后才返回 200。
  2. 慢放量(slow ramp):新实例进了 LB 后,把它的权重在 5–10 分钟内从 1% 线性拉到 100%。比一把跳到 100% 安全得多。
  3. 预热镜像:把 JIT 缓存(CDS / AppCDS)和 PGO 产物烤进容器镜像,让新实例跳过一部分预热。
  4. 粘性路由:用一致性哈希把同类请求送到同一组实例,最大化每个实例的缓存命中率。

5.2 短生命周期函数:serverless 冷启动

对 Lambda / Cloud Run / Cloud Functions,“冷启动”指的是从容器拉起到首个请求返回的时间。典型数字:

运行时典型冷启动延迟
Node.js(小包)100 – 300 ms
Python(小包)150 – 400 ms
Go(编译型)100 – 300 ms
JVM(Spring Boot)1 – 5 s
大镜像 / VPC ENI10 s+

关键洞察:这就是为什么 AdTech 热路径几乎从不用 serverless。一个 bidder 总共 100ms 的预算,容不下一个 100ms+ 的冷启动。

AdTech 里的 serverless 通常被限制在异步路径上:

如果你非得把 serverless 放在延迟敏感路径上,两个真选项:provisioned concurrency,为永久容量付费——这本质上把 serverless 变成”一个会自动扩缩的常驻服务”;或者 GraalVM native image,把启动时间从秒级压到几十毫秒。

5.3 系统冷启动 ≠ 算法冷启动

这俩老被混为一谈,不该如此:

维度系统冷启动算法冷启动
来源运行时 / 基建数据稀疏
时间尺度秒 — 分钟天 — 周
在哪解基建(预热 / 放量)模型层(先验 / bandit / 人群)
指标签名p99 / GC 暂停 / 连接错误CTR / CVR / lift
值班SRE算法

它们偶尔同时发生——一次新服务部署叠上一次新模型部署,落在同一个窗口——看上去像一个事件。它们不是。根因定位的路径完全不同。把它们塞进同一个看板,事故排查就变成了猜谜。

本节要点:系统冷启动是基建问题,用预热 + 放量 + 预热镜像来解。**它不该出现在产品需求里。**如果出现了,那是产品团队在替 SRE 背锅。

6. 归因冷启动(测量层)

场景:一个 iOS 客户开了一条新 SKAN 广告,运营问 SKAN 4 的转化值(CV)该怎么配。算法团队查看板——这条广告一个 postback 都还没回来。

这一副面孔是 SKAN 时代特有的,但结构上就是广告冷启动。缺的不是转化,是反馈本身

6.1 SKAN 4 转化值配置

先快速复习。SKAN(SKAdNetwork)是 Apple 的隐私保护归因协议。在 iOS ATT 下,DSP 看不到 IDFA,转化数据经 SKAN postback 回流(这套端上承载详见 App 广告 SDK)。SKAN 4 同时支持细粒度 CV(64 桶)和粗粒度 CV(低 / 中 / 高),由广告主配置事件 → CV 的映射。

配 CV 归根到底是一个决策:哪些事件 + 哪些频次组合映射到高价值、哪些到低价值?这个决策取决于这条广告在这个 geo 的转化值分布——而对一条新广告,这个分布还不存在。

冷启动工程策略

  1. 从前置域数据迁先验:SKAN 之前的装机归因数据、同一广告在其他地区的表现、同一广告主的其他广告——按相似度加权,全部融成一个分布先验。
  2. 先粗后细:头两周只配粗粒度 CV(三桶)。等观测到的 postback 分布稳定后,再切到细粒度。粗粒度所需样本少一个数量级。
  3. 兜底 CV 槽:留一个通用事件槽,把所有未识别行为映射到单个 CV。这样冷启动期就不会仅仅因为没配对事件而把 postback 整个丢掉。
  4. 别急着追”最优 CV 配置”:SKAN 的 postback 延迟(随机 0–24h,再加上 window 1 的 24h)意味着每次改配置都有一个多天的反馈回路。头几次迭代注定次优——接受它。AppsFlyer、Adjust、Singular 都把这套”先粗后细、慢慢精修”的回路默认烤进了它们的 SKAN Conversion Studio。

6.2 增量实验冷启动

第一次跑 lift / 增量实验最难的地方:没有基线。holdout 该多大?实验该跑多久?

实用的近似:

6.3 MMM 冷启动

营销组合建模(MMM)有一种反直觉的冷启动:它主动需要你注入方差才能学到东西。如果一个渠道的历史花费从没有实质波动过,模型就没法识别这个渠道的边际效应。经济学家管这叫识别问题(identification problem)

这里没多少工程可做。活儿在前期设计花费的变动——有意地把每个渠道的预算来回摆几次(包括回撤),好让 MMM 有东西可比。这很难向产品团队推销,因为它意味着拿短期 ROAS 去换长期可解释性。教科书式的学习税。

本节要点:归因冷启动是要造一套**“早期粗糙信号不致命”的兜底配置**。别让”第一版 CV 配错了”变成”接下来 30 天的归因都错了”。

7. 先验从哪来?

每一层的解法都需要一个先验。先验从哪来?五个常见来源,摊在”便宜 vs. 无偏”这个平面上:

来源便宜程度无偏程度典型用法
业务规则与行业基线★★★★★CTR ~1%、移动 CVR ~2%、视频完播 ~60%——一个零知识的初猜
跨域迁移★★★★★★同广告主的老广告、相似 geo / 垂类,按相似度加权
合作方数据★★★★★★DMP 人群包、二方数据交换、广告主 CRM 上传
合成数据★★★★新素材上线前,用图像 / 文本相似度模型估计初始 CTR
holdout / 探索流量★★★★★花钱买一份无偏样本——最贵,也最干净

一个冷启动策略,核心就是在”便宜但有偏的先验”和”昂贵但无偏的探索”之间做权衡。早期靠便宜的先验,随着探索数据累积,过渡到无偏估计。

8. 怎么判断冷启动结束了?

最常见的坏信号:**“今天的数据看着还行。“**那不是冷启动结束,那只是平稳的一天。

四个值得信的信号:

  1. 置信区间收敛了:CTR / CVR / CPA 的 95% 置信区间,窄于当前这个决策所需的宽度。典型门槛:相对误差 < 20%。可量化、可监控。
  2. 校准在主要分桶里贴合:把预测分桶,验证每个桶的预测均值与观测均值是否吻合。画校准曲线,主分位的偏离应 < 10%。冷启动早期这条曲线远离 y = x 对角线,只有收敛后才贴合。
  3. 时间序列稳定:连续 7 天 CPA 在目标 ±15% 以内,且日内方差不再呈现早期那种”单调上升 / 下降”的形状。
  4. 决策不再被探索流量主导:冷启动期探索可占 50% 的流量,稳态下通常 < 10%。

工程原则:把这些指标接进看板、配硬阈值告警。**别让”冷启动结束了”成为一个人的拍脑袋判断。**冷启动有清晰的触发点(广告上线 / 模型部署 / 服务重启),它也该有清晰的退出标准。

9. 反模式合集

把散在前面各节的坑收拢起来,按层分组。

广告层

模型层

系统层

测量层

10. 统一视角:先验、探索、兜底

四层看上去毫不相干,底下却是同一件事。折进一张表:

先验从哪来探索机制兜底机制
广告相似广告历史、行业基线划给随机探索 / Thompson Sampling 的预算低出价、窄人群、日预算上限
模型元数据 embedding、人群均值、look-alike探索日志、bandit贝叶斯平滑、分层回退
系统上一版本的 JIT 缓存、CDS 镜像影子流量 / 金丝雀慢放量、就绪探针、回滚预案
归因前置域数据、相似 geo兜底 CV、先粗后细通用事件槽、保守 holdout

每一层都跑同一个三件套:

  1. 先验:你不是从零开始。问自己:“我已经知道什么?“把它编码进决策。
  2. 探索:有意地交学习税。问自己:“我愿意为信息付多少?“把它落成一个 holdout / 探索预算。
  3. 兜底:探索失败了,也不会出灾难性后果。问自己:“如果先验和探索都错了,最大损失是多少?“把那个落成一道上限 / 放量 / 兜底策略。

工程师消不掉冷启动。那也不可能——只要业务还在不断上新广告、新模型、新服务、新转化事件,冷启动就会反复出现。工程师能做的,是把这三个机制设计好,让每一次新的冷启动都付出一个有界、可预测、付得起的代价。

11. 收尾

回到开头那场站会。新广告,上线 24 小时,CPI 是目标的 3 倍。产品经理问模型是不是坏了。

正确答案不是”是”或”否”,而是:

“我们在冷启动期。先验是 X(取自一条相似广告)。探索预算是 Y(花费的 8%)。兜底约束是 Z(日预算上限 $5,000、geo 仅美国)。置信区间应在 N 天内收敛到可行动的宽度。这个窗口内的表现波动,在我们商定的预算之内。稳住。”

如果你能这么回答,而且 X / Y / Z / N 全都被测量、被监控、被写进 runbook,你就把冷启动从”一种玄学之痛”挪成了一个有界的工程问题

这是 AdTech 工程师在这件事上能给业务的最大礼物。


延伸阅读

先看同系列里和本篇相邻的几篇:

再是论文与一手资料:

附录:术语表


views
Share this post on:

Previous Post
IDFA 之后的归因:MMP、SKAdNetwork 与 Postback 结算
Next Post
IAB TCF 与 GPP 深挖:每个 AdTech 工程师都绕不开的『同意』协议