Prophet 对节假日效应敏感,如何引入促销事件特征

解读

在国内电商、零售、金融等场景中,促销(618、双11、年货节、会员日等)带来的流量峰值往往远高于普通节假日,且持续时间短、强度大。Prophet 原生只支持固定节假日列表,无法区分“普通节假日”与“大促日”对 KPI 的不同影响,直接套用会导致:

  1. 促销高峰被误判为异常点,预测值偏低;
  2. 普通节假日被过度放大,预测值偏高;
  3. 性能基线失真,容量评估出现数量级误差。

因此,需要把“促销事件”作为独立、可配置、可量化的外生变量注入模型,同时保证在性能测试脚本里可回放、可伸缩。

知识点

  1. Prophet 的 holiday 框架:holidays 参数接受 DataFrame,必须含 holiday(string)和 ds(date)两列,可选 lower_window、upper_window 表示影响前后偏移。
  2. 国内促销日历的不规律性:平台自己造节(如抖音818)、跨零点付尾款、预售尾款叠加,导致“促销日”可能是 1 天也可能是 0.5 天,需要精确到小时级。
  3. 性能测试视角的“特征”不仅是日期,还包含促销强度(GMV 目标、折扣深度)、渠道(APP/小程序/直播)、业务属性(预售、尾款、秒杀)。这些维度决定并发系数与业务模型。
  4. 外生回归器 add_regressor:Prophet 允许把任意时间序列作为额外回归器,可线性或非线性影响预测值,适合引入“促销强度指数”。
  5. 性能压测脚本联动:促销特征必须能被 Gatling/JMeter 的 CSV Data Set 读取,实现“日期-并发数-业务比例”三列映射,保证模型与压测场景同源。

答案

线上实战分三步落地:

第一步,构建促销事件表

promo_df = pd.DataFrame({
    'holiday': 'promo',
    'ds': pd.to_datetime(['2023-06-17', '2023-06-18', '2023-11-10',
                          '2023-11-11', '2024-01-15']),
    'lower_window': 0,
    'upper_window': 1,   # 尾款日延续效应
    'intensity': [0.8, 1.0, 0.9, 1.0, 0.7]  # 自定义强度
})

把 intensity 同时作为 add_regressor 注册:

m = Prophet(holidays=promo_df)
m.add_regressor('intensity', mode='multiplicative')

第二步,训练-验证-调参 用过去两年小时级流量训练,交叉验证采用“滚动窗口”方式,重点看促销日 MAPE 是否从 18% 降到 6% 以下;若仍偏高,再把“预售尾款小时标记”做成 0/1 哑变量继续加入。

第三步,与性能测试闭环

  1. 模型输出促销日 QPS 预测曲线 → 按 95% 置信上限取峰值;
  2. 通过 CI 流水线自动生成 “promo_load_profile.csv”,字段:time_of_day, concurrent_users, pct_checkout, pct_seckill;
  3. JMeter 的 Ultimate Thread Group 直接引用该 CSV,实现“模型-压测”同源;
  4. 压测后发现 CPU 热点,把瓶颈方法耗时回写至模型作为新的 regressor,迭代优化。

通过以上方法,Prophet 既能识别促销带来的阶跃,又能给性能测试提供可量化的负载输入,保证容量评估与线上 SLA 对齐。

拓展思考

  1. 如果促销强度无法提前确定,可改用贝塔-二项或销量目标反推强度,把不确定性用分布表示,再跑蒙特卡洛生成 100 条 QPS 曲线,性能测试用 P95 最大曲线做极限压测。
  2. 对于“尾款 0 点洪峰”这种小时级脉冲,Prophet 默认日粒度会平滑掉,需要把 ds 精确到小时并开启 uncertainty_samples=500,同时把 changepoint_prior_scale 调大到 0.08,让模型更快捕捉突变。
  3. 当业务线超过 10 条且促销日历差异大时,建议放弃单模型,改用分层 Prophet:先预测大盘,再按渠道-业务做比例拆分,性能测试脚本也分层,避免“一刀切”造成资源浪费。
  4. 最终目标是“模型-压测-监控”闭环:线上真实流量回写时,若发现促销日 MAPE>10%,自动触发压测脚本回归,形成 DevOps 级容量保障。