M1. 营销组合模型 (MMM) 决策¶
案例来源: Robyn (Meta) + Google-Lightweight-MMM
决策类型: 营销预算分配优化
方法论: 贝叶斯 MMM + 渠道 ROI 归因
预计阅读时间: 18 分钟
1. 决策背景¶
业务场景¶
某电商公司年度营销预算¥5 亿,面临预算分配难题: - 营销渠道:15+ (搜索/信息流/社交/视频/KOL/...) - 归因困难:用户旅程平均 5-8 个触点 - 预算浪费:约 30% 预算效果难以衡量 - 部门冲突:各渠道团队争夺预算,缺乏客观依据
决策者角色¶
- 主要决策者: CMO/营销副总裁
- 执行团队: 渠道营销团队 (50+ 人)
- 影响对象:各渠道代理商/供应商
- 利益相关者: 财务部门、数据分析团队、CEO
决策频率¶
- 战略决策: 年度/季度预算规划
- 战术决策: 月度预算调整
- 运营决策: 周度/日度出价优化
当前痛点¶
2. 决策问题¶
核心问题 (5W1H)¶
| 维度 | 描述 |
|---|---|
| What | 如何科学分配¥5 亿营销预算? |
| Who | 15+ 营销渠道 |
| When | 季度规划 + 月度调整 |
| Where | 全渠道 (线上 + 线下) |
| Why | 提升 ROI,减少浪费 |
| How | 营销组合模型 (MMM) |
决策目标¶
定量目标: - 整体营销 ROI: 1:3 → 1:4 - 预算浪费:30% → < 10% - 获客成本:降低 20% - 预测准确度:> 85%
定性目标: - 预算分配有据可依 - 减少部门间冲突 - 提升决策透明度
约束条件¶
| 类型 | 约束 |
|---|---|
| 数据 | 仅能使用聚合数据 (隐私合规) |
| 时间 | 2 个月内完成首期模型 |
| 预算 | ¥2M 模型建设预算 |
| 组织 | 需获得各渠道团队认可 |
3. 决策框架¶
方法论:贝叶斯营销组合模型¶
选择贝叶斯方法理由: - ✅ 处理小样本 (相对渠道数量) - ✅ 量化不确定性 (置信区间) - ✅ 融入先验知识 (专家经验) - ✅ 输出可解释 (ROI 分布)
决策流程¶
graph LR
A[历史数据] --> B[模型构建]
B --> C[参数估计]
C --> D[ROI 计算]
D --> E[预算优化]
E --> F[执行监控]
F --> G[模型更新]
G --> C
核心公式¶
广告响应函数 (Adstock + Saturation):
参数含义:
- λ: 衰减率 (0-1,越大持续越久)
- γ: 饱和系数 (<1 表示边际收益递减)
- β: 渠道效率系数
不确定性管理¶
| 不确定性 | 来源 | 处理方法 |
|---|---|---|
| 参数不确定性 | 数据有限 | 贝叶斯后验分布 |
| 模型不确定性 | 函数形式 | 多模型对比 |
| 外部因素 | 季节/竞争 | 控制变量 + 敏感性分析 |
4. 数据与模型¶
数据来源¶
| 数据 | 粒度 | 时间跨度 | 质量 |
|---|---|---|---|
| 渠道花费 | 周度 | 2 年 | ✅ 95% |
| 销售数据 | 周度 | 2 年 | ✅ 98% |
| 促销活动 | 周度 | 2 年 | ✅ 90% |
| 节假日 | 日度 | - | ✅ 100% |
| 竞争对手 | 周度 | 1 年 | ⚠️ 70% |
| 季节性 | 周度 | - | ✅ 100% |
模型规格¶
Robyn 模型 (Meta 开源):
library(robyn)
# 模型构建
robyn_model <- robyn(
data = marketing_data,
date_var = "week",
dv = "sales",
spend_vars = c("search", "social", "display", "video", "kOL"),
p_expt = c("promotion", "seasonality"),
adstock_max_lag = 8 # 最大 8 周延迟
)
# 贝叶斯推断
robyn_output <- robyn_fit(
model = robyn_model,
iterations = 1000,
chains = 4
)
关键输出¶
1. 渠道 ROI 分布: | 渠道 | 中位数 ROI | 95% CI | 预算建议 | |------|-----------|--------|----------| | 搜索广告 | 1:4.2 | [3.8, 4.6] | 增加 30% | | 信息流 | 1:3.5 | [2.9, 4.1] | 保持 | | KOL | 1:2.8 | [2.1, 3.5] | 减少 20% | | 视频 | 1:2.5 | [1.9, 3.1] | 减少 30% | | 展示广告 | 1:1.8 | [1.2, 2.4] | 大幅削减 |
2. 饱和曲线参数: | 渠道 | 饱和点 (万元/周) | 半衰期 (周) | |------|-----------------|------------| | 搜索 | 800 | 4.2 | | 信息流 | 500 | 3.5 | | KOL | 200 | 2.8 | | 视频 | 600 | 5.1 |
3. 最优预算分配:
当前分配:搜索 25% / 信息流 30% / KOL 20% / 视频 15% / 展示 10%
最优分配:搜索 35% / 信息流 30% / KOL 15% / 视频 10% / 展示 5% / 其他 5%
预期 ROI 提升:1:3.0 → 1:4.2 (+40%)
5. 决策实施¶
实施路线图¶
阶段 1: 数据准备 (3 周)
阶段 2: 模型开发 (5 周)
阶段 3: 决策支持 (4 周)
工具栈¶
| 组件 | 工具 | 理由 |
|---|---|---|
| 模型 | Robyn (R) | Meta 开源,贝叶斯 MMM 最佳实践 |
| 替代 | Lightweight MMM (Python) | Google 开源,轻量级 |
| 优化 | scipy.optimize | 预算分配求解 |
| Dashboard | Streamlit | 快速原型 |
| 调度 | Airflow | 自动化更新 |
变更管理¶
利益相关者沟通: - 高层: 月度 ROI 报告 + 预算建议 - 渠道团队: 周度 Dashboard + 解读培训 - 财务: 模型审计 + 合规文档
决策透明化:
6. 决策结果¶
业务影响 (上线 6 个月后)¶
| 指标 | 基线 | 当前 | 改善 |
|---|---|---|---|
| 整体 ROI | 1:3.0 | 1:4.1 | +37% |
| 获客成本 | ¥180 | ¥142 | -21% |
| 预算浪费 | 30% | 12% | -60% |
| 预测准确度 | 65% | 88% | +35% |
| 部门冲突 | 高频 | 显著降低 | 定性改善 |
ROI 提升分解¶
财务价值¶
年度影响: - 营销预算:¥500M - ROI 提升:1:3.0 → 1:4.1 - 增量收入:¥500M × (4.1 - 3.0) = ¥550M
投入产出: - 模型建设:¥2M - 年度收益:¥550M - ROI: 275 倍
组织影响¶
正面: - ✅ 预算决策从"博弈"转向"数据驱动" - ✅ 渠道团队聚焦优化而非争夺预算 - ✅ 财务部门认可模型审计结果
挑战: - ⚠️ 初期渠道团队抵触 (担心预算被砍) - ⚠️ 需要持续教育 (模型不是万能) - ⚠️ 数据质量依赖 (垃圾进垃圾出)
7. 决策改进¶
反馈机制¶
监控指标:
# 每周计算
weekly_metrics = {
'mape': mean_absolute_percentage_error(actual, predicted),
'roi_stability': roi_this_week / roi_last_week,
'budget_variance': actual_spend / planned_spend
}
# 预警阈值
if mape > 0.20:
alert("预测误差超过 20%,需要检查模型")
if roi_stability < 0.8 or roi_stability > 1.2:
alert("ROI 波动异常")
迭代计划¶
V1.0 (当前): 聚合数据 + 周度更新 V2.0 (3 月): 区域细分模型 + 更细粒度 V3.0 (6 月): 实时数据 + 自动优化 V4.0 (12 月): 跨渠道归因 + 用户级建模
扩展场景¶
| 场景 | 优先级 | 预期价值 |
|---|---|---|
| 新品上市预算 | P0 | 成功率 +30% |
| 区域预算分配 | P0 | ROI +15% |
| 促销效果评估 | P1 | 促销 ROI +25% |
| 品牌广告评估 | P2 | 长期价值量化 |
8. 附录¶
Robyn 模型代码¶
library(robyn)
# 1. 数据准备
data <- read.csv("marketing_data.csv")
# 2. 模型配置
model_config <- robyn(
data = data,
date_var = "week",
dv = "sales",
spend_vars = c("search_branded", "search_nonbranded",
"social", "display", "video", "kOL"),
p_expt = c("promotion_flag", "seasonality_index"),
adstock_max_lag = 8
)
# 3. 模型拟合
output <- robyn_fit(
model = model_config,
iterations = 1000,
chains = 4,
seed = 123
)
# 4. 结果提取
roi_table <- robyn_decompose(output)
budget_optimizer <- robyn_allocator(output, budget = 500000000)
# 5. 可视化
robyn_plot(output, type = "decomposition")
robyn_plot(output, type = "response_curve")
关键假设检验¶
# 1. 收敛性诊断 (R-hat < 1.1)
r_hat <- summary(output)$rhat
if (any(r_hat > 1.1)) {
warning("部分参数未收敛,需要增加迭代次数")
}
# 2. 后验预测检验
pp_check(output) # 模型拟合度检查
# 3. 敏感性分析
sensitivity_results <- lapply(c(0.5, 0.8, 0.95), function(prior) {
robyn_fit(model, prior_weight = prior)
})
预算优化求解器¶
from scipy.optimize import minimize
def negative_roi(budget_allocation, roi_params):
"""负 ROI 函数 (最小化)"""
total_roi = 0
for i, channel in enumerate(channels):
spend = budget_allocation[i]
alpha, beta, gamma = roi_params[channel]
response = alpha + beta * spend ** gamma
total_roi += response
return -total_roi # 最小化负 ROI
# 约束条件
constraints = [
{'type': 'eq', 'fun': lambda x: sum(x) - total_budget}, # 预算花完
{'type': 'ineq', 'fun': lambda x: x - min_spend}, # 最低预算
]
# 求解
result = minimize(negative_roi, x0=initial_allocation,
args=(roi_params,), constraints=constraints)
optimal_allocation = result.x
案例版本: v1.0
最后更新: 2026-06-03
维护者: 营销科学团队