从方法论到上线
6 月我们写过 LightGBM + SHAP 在 MMM 中的应用边界——那时它还停留在"方法论探讨":树模型能捕捉线性回归抓不到的非线性交互,SHAP 让黑盒变可解释。三个月后,它从一段讨论变成了 SparkX 里一个能跑、能出报告、和 Ridge/PyMC 站在同一层的正式引擎。这篇文章不讲"为什么用 LightGBM"(那篇已经讲过),只讲生产级实现里那些为不翻车而做的设计。
一句话定位:它是引擎矩阵中唯一来自机器学习(而非统计建模)路线的引擎——覆盖"线性可加假设不够用"的场景,同时用 SHAP 保持 MMM 需要的可解释性。
引擎做了什么
整个 pipeline 分四步,全部在 lgbm_engine.py 里完成:
- 特征准备:对每个渠道在 θ=0.1…0.9 的 9 档上做几何 Adstock 网格搜索,按"与目标 KPI 的相关性"选最优 θ——衰减不是预设的,是数据选的
- 建模:Adstock 化的媒体特征 + 控制变量(节假日、价格等)喂给 LightGBM,MAPE 为目标,early stopping 控制树的数量
- 归因:用
shap.TreeExplainer算每个观测上每个渠道的 SHAP 值,渠道贡献 = 该渠道 SHAP 之和,ROI = 贡献 / 花费 - 输出:每个渠道的 ROI 均值 + 置信区间、贡献占比、响应曲线、边际 ROI、健康度评分——和其它引擎的 ChannelResult 同结构,可直接进预算优化器和 Champion 对比
关键在于:它输出的字段和 Ridge/PyMC 完全对齐。所以你可以在 Expert Mode 里同时跑 Ridge 和 LightGBM,在 Champion 看板里并排比较两个引擎对同一渠道的 ROI 估计——如果排序一致,线性假设够用;如果不一致,数据里有非线性结构。
先 Adstock,再喂树
这里有一个和 6 月那篇方法论描述的重要修正:当时我们说"把原始花费直接喂给树,让它自己学衰减"。实际生产实现里我们没这么做——而是先做几何 Adstock,再喂树。
原因很实在:
- 树的衰减学习不稳定:决策树对"上周的花费影响本周的销售"这种时序衰减,需要大量数据才能学稳。先做 Adstock 等于把这个强先验显式注入,树只需要学残差里的非线性
- 和其它引擎可比:所有引擎共享同一套 Adstock 语义,渠道的
adstock_halflife才能跨引擎横向比 - θ 是选出来的不是猜的:9 档网格搜索按"与 KPI 的相关性"挑 θ,比手动填一个 0.5 更有依据
实现细节:代码里theta_grid = np.linspace(0.1, 0.9, 9),每个渠道独立选 θ,选完用adstock_geometric转换后再进特征矩阵。控制变量(control_cols)不经过 Adstock,直接拼接。
抗过拟合的五道防线
树模型在 MMM 里最大的风险不是"不够准",而是"假装很准"——训练 R² 很高、外样本一塌糊涂。生产实现里我们叠了五层防护:
- 时序 holdout:按时间切,最后 20% 做验证集,而不是随机切——MMM 是时间序列,随机切等于把未来泄露进训练
- Early stopping:验证集 MAPE 连续 30 轮不降就停,
num_boost_round=500只是上限,实际往往几十轮就停 - 保守超参:
num_leaves=15(限制树的表达力)、learning_rate=0.05(慢学)、min_child_samples按数据量自适应、subsample=0.8 / colsample_bytree=0.8(行/列采样)、reg_alpha=0.1 / reg_lambda=1.0(L1/L2 正则) - 同时报 in-sample 和 OOS R²:诊断里两个都给(
in_sample_r2+oos_r2+oos_mape)。差距大就是过拟合信号,用户看得见 - SHAP 贡献剪枝:SHAP 贡献占比 < 1% 的渠道会被降权,防止树在噪声渠道上硬找模式
树模型不像 Bayesian 有后验散度天然量化不确定性。它的"可信度"只能靠外样本表现反推。所以我们把 OOS R² 放进诊断面板——它不是装饰,是这个引擎能不能信的依据。如果 OOS R² 远低于 in-sample,这个 run 的结论就该打折看。
SHAP 归因 + bootstrap 置信区间
归因部分是这个引擎最值得讲的设计。线性回归天然给 β 和置信区间,树模型不给。我们的做法:
- 渠道贡献 = SHAP 之和:
shap_values[:, m].sum()就是该渠道在整个观测期内的总贡献(有正有负,方向性保留) - ROI = 贡献 / 总花费:和其它引擎的 ROI 定义一致,可进优化器
- 置信区间靠 bootstrap:对 SHAP 值做 200 次重采样,取 2.5% / 97.5% 分位作为 ROI 的 95% 置信区间——
roi_ci_low / roi_ci_high - 响应曲线非参数:不假设 Hill 形状,按花费分箱取平均 SHAP,画出"从数据学出来的饱和曲线"——
_shap_response_curve把花费分 20 档,每档取该档内 SHAP 均值 - 边际 ROI:取最近 13 个观测的 SHAP 均值算当前边际,回答"再投 1 块钱能赚多少"
这套输出的字段(roi_mean / roi_ci_low / roi_ci_high / contribution_pct / response_curve / marginal_roi / health_score)和 Ridge、PyMC 引擎同结构。这是它能进同一张 Champion 对比表的前提。
交互检测:协同还是替代
线性模型不显式建模交互,树模型天然会。我们用 shap_interaction_values 把它量化出来(为速度取前 50 个观测子采样):
- 强度:每对渠道的交互 SHAP 绝对值均值,按强度排序,输出 top 10
- 方向:交互均值 > 0 标
synergy(协同,1+1>2),< 0 标cannibalization(替代,互相抢量)
典型例子:TV 和品牌搜索常常是 synergy(TV 起量后搜索转化率上升);搜索和联盟营销可能是 cannibalization(同一拨人被两个渠道重复归因)。这类信号线性 MMM 给不出,是树模型路线的独有价值。
注意:交互检测是"线索"不是"结论"。SHAP interaction 在小样本上方差大,50 个观测的子采样是为速度牺牲了精度。把它当成探索性发现,配合业务判断看。
何时选 LightGBM,何时选别的
它不是"总是更好"。给个实用的决策框架:
- 选它:10+ 渠道、怀疑有显著协同/替代、数据 200+ 时间点、不确定 Adstock/Hill 函数形状、想给线性假设做对照检验
- 不选它:数据 < 100 时间点(树容易过拟合)、需要严格后验不确定性(用 PyMC)、需要外推到训练范围外(树不能外推,模拟"预算翻倍"该用 Hill)、需要可审计的数学推导(线性 + Adstock + Hill 更易解释)
推荐工作流:Instant Mode 先跑 Ridge 基线 → Expert Mode 同数据跑 LightGBM → Champion 对比两引擎的渠道 ROI 排序。一致 = 线性够用;不一致 = 数据有非线性,LightGBM 的结论更可信。
怎么跑
LightGBM 引擎现在在 Expert Mode 可选。进入引擎选择页时,平台会调 GET /api/modeling/engines 探测每个引擎的依赖是否装好——LightGBM 需要 lightgbm 和 shap 两个包,没装会显示安装提示而不是直接报错。选好后跑一次,诊断面板里能看到 in_sample_r2 / oos_r2 / oos_mape、交互列表、特征重要性和预测 vs 实际曲线。
典型耗时 3–10 秒(取决于渠道数和数据量),是矩阵里最快的引擎之一——适合做快速对照和探索。
想看它的方法论底座?读 6 月那篇 ML 方法论:当机器学习遇上营销组合建模。想理解它和其它引擎的差异?看 Ridge vs PyMC vs Stan:引擎选择指南。