MarkTechPost(RSS)
精选
71AI 编辑部评分,满分 100

用 Google Meridian 构建端到端贝叶斯营销组合模型:媒体测量、ROI 分析与预算优化

2026-08-06 05:56· 1小时前· Sana Hassan
AI 导读

本教程使用 Google Meridian 构建完整的贝叶斯营销组合建模工作流,涵盖数据加载、ROI 先验配置、NUTS 采样拟合及收敛性评估。通过 Analyzer API 提取渠道贡献、ROI、边际 ROI、adstock 与饱和曲线等后验指标,并计算渠道间 ROI 比较概率。最后用 BudgetOptimizer 优化固定与灵活预算,生成可分享的 HTML 报告并保存模型复用。

推荐理由

完整工作流把先验设定、后验采样、诊断和预算优化串联为可执行步骤,让营销分析团队能自行量化渠道贡献与 ROI 不确定性。

正文 · AI 翻译

在本教程中,我们使用 Google Meridian 构建一个完整的贝叶斯营销组合建模工作流。我们首先安装所需的库、验证 GPU 可用性,并探索一个地理层级营销数据集,其中包含媒体曝光量、花费、控制变量、促销活动、转化量、人口和收入。然后,我们将原始列映射到 Meridian 的数据模式,定义可解释的基于 ROI 的先验分布,并在使用先验和后验 NUTS 采样拟合模型之前配置模型。训练完成后,我们评估收敛性和预测准确性,检查渠道贡献、ROI、边际 ROI、有效性、广告库存效应、饱和度和响应曲线,并使用 Analyzer API 提取自定义后验指标。最后,我们通过优化固定和灵活预算、生成可共享的 HTML 报告以及保存拟合模型以供复用,来完成整个工作流。

!pip install --upgrade -q "google-meridian[and-cuda]"
import numpy as np
import pandas as pd
import altair as alt
import tensorflow as tf
import tensorflow_probability as tfp
from IPython.display import display, HTML
from meridian import constants
from meridian.data import load
from meridian.model import model
from meridian.model import spec
from meridian.model import prior_distribution
from meridian.analysis import analyzer
from meridian.analysis import visualizer
from meridian.analysis import optimizer
from meridian.analysis import summarizer
def show(chart_or_obj, title=None):
   if title:
       display(HTML(f"<h3 style='font-family:sans-serif'>{title}</h3>"))
   display(chart_or_obj)
print("TensorFlow:", tf.__version__)
gpus = tf.config.experimental.list_physical_devices("GPU")
print("GPUs detected:", gpus if gpus else "NONE — sampling will be slow on CPU!")
CSV_URL = (
   "https://raw.githubusercontent.com/google/meridian/refs/heads/main/"
   "meridian/data/simulated_data/csv/geo_all_channels.csv"
)
df = pd.read_csv(CSV_URL)
print("\nShape:", df.shape)
print("Geos:", df["geo"].nunique(), "| Weeks:", df["time"].nunique())
print("Date range:", df["time"].min(), "->", df["time"].max())
display(df.head())
spend_cols = [c for c in df.columns if c.endswith("_spend")]
spend_share = df[spend_cols].sum().rename("total_spend").reset_index()
spend_share["share_%"] = 100 * spend_share["total_spend"] / spend_share["total_spend"].sum()
display(spend_share)
kpi_by_week = df.groupby("time")["conversions"].sum().reset_index()
show(
   alt.Chart(kpi_by_week).mark_line().encode(
       x=alt.X("time:T", title="Week"),
       y=alt.Y("conversions:Q", title="Total conversions (all geos)"),
   ).properties(width=700, height=250),
   "National KPI over time",
)

我们安装支持 GPU 版 TensorFlow 的 Google Meridian,并导入建模、可视化和分析所需的库。我们验证运行时环境、检测可用的 GPU,并加载 Meridian 的模拟地理层级营销数据集。我们还通过查看数据维度、日期覆盖范围、花费分布和全国转化趋势来进行初步探索性分析。

coord_to_columns = load.CoordToColumns(
   time="time",
   geo="geo",
   controls=["competitor_sales_control", "sentiment_score_control"],
   population="population",
   kpi="conversions",
   revenue_per_kpi="revenue_per_conversion",
   media=[
       "Channel0_impression",
       "Channel1_impression",
       "Channel2_impression",
       "Channel3_impression",
       "Channel4_impression",
   ],
   media_spend=[
       "Channel0_spend",
       "Channel1_spend",
       "Channel2_spend",
       "Channel3_spend",
       "Channel4_spend",
   ],
   organic_media=["Organic_channel0_impression"],
   non_media_treatments=["Promo"],
)
media_to_channel = {f"Channel{i}_impression": f"Channel_{i}" for i in range(5)}
media_spend_to_channel = {f"Channel{i}_spend": f"Channel_{i}" for i in range(5)}
loader = load.CsvDataLoader(
   csv_path=CSV_URL,
   kpi_type="non_revenue",
   coord_to_columns=coord_to_columns,
   media_to_channel=media_to_channel,
   media_spend_to_channel=media_spend_to_channel,
)
data = loader.load()
print("\nInputData loaded. Media tensor shape (geo, time, channel):", data.media.shape)
roi_mu = 0.2
roi_sigma = 0.9
prior = prior_distribution.PriorDistribution(
   roi_m=tfp.distributions.LogNormal(roi_mu, roi_sigma, name=constants.ROI_M)
)
model_spec = spec.ModelSpec(prior=prior)
mmm = model.Meridian(input_data=data, model_spec=model_spec)

我们使用 CoordToColumns 将原始数据集列映射到 Meridian 的预期模式。我们定义付费媒体、花费、自然渠道、控制变量、处理变量、人口、KPI 和收入相关字段,然后加载结构化输入数据。接着,我们配置基于 ROI 的先验分布,创建模型规格,并初始化 Meridian 模型。

mmm.sample_prior(500)
mmm.sample_posterior(
   n_chains=7,
   n_adapt=500,
   n_burnin=500,
   n_keep=1000,
   seed=1,
)
print("Sampling complete.")
model_diagnostics = visualizer.ModelDiagnostics(mmm)
show(model_diagnostics.plot_rhat_boxplot(), "R-hat convergence check (want < 1.05)")
show(
   model_diagnostics.plot_prior_and_posterior_distribution(),
   "Prior vs. posterior (ROI parameters)",
)
model_fit = visualizer.ModelFit(mmm)
show(model_fit.plot_model_fit(), "Model fit: expected vs. actual outcome")
display(model_diagnostics.predictive_accuracy_table())
media_summary = visualizer.MediaSummary(mmm)
display(media_summary.summary_table())
show(media_summary.plot_channel_contribution_area_chart(),
    "Outcome decomposition over time (baseline + channels)")
show(media_summary.plot_contribution_pie_chart(),
    "Share of outcome: baseline vs. media")
show(media_summary.plot_spend_vs_contribution(),
    "Spend share vs. contribution share (spot over/under-investment)")
show(media_summary.plot_roi_bar_chart(),
    "ROI by channel (with credible intervals)")
show(media_summary.plot_roi_vs_effectiveness(),
    "ROI vs. effectiveness (bubble = spend)")
show(media_summary.plot_roi_vs_mroi(),
    "ROI vs. marginal ROI — mROI drives optimization, not average ROI")

我们从先验分布中采样,并使用跨多条链的后验 NUTS 采样来拟合贝叶斯模型。我们使用 R-hat 诊断评估收敛性,比较先验和后验分布,并评估模型与观测结果的拟合程度。我们还分析预测准确性、渠道贡献、ROI、边际 ROI 和媒体有效性。

media_effects = visualizer.MediaEffects(mmm)
show(media_effects.plot_response_curves(),
    "Response curves (incremental outcome vs. spend)")
show(media_effects.plot_adstock_decay(),
    "Adstock decay by channel")
show(media_effects.plot_hill_curves(),
    "Hill saturation curves by channel")
analysis = analyzer.Analyzer(mmm)
roi_draws = analysis.roi()
roi_np = np.asarray(roi_draws)
channels = list(data.media_channel.values)
roi_table = pd.DataFrame({
   "channel": channels,
   "roi_mean": roi_np.mean(axis=(0, 1)),
   "roi_p05": np.quantile(roi_np, 0.05, axis=(0, 1)),
   "roi_p95": np.quantile(roi_np, 0.95, axis=(0, 1)),
})
print("\nPosterior ROI summary (custom, from raw draws):")
display(roi_table)
p_better = (roi_np[..., 1] > roi_np[..., 0]).mean()
print(f"P(ROI Channel_1 > ROI Channel_0) = {p_better:.1%}")
summary_metrics = analysis.summary_metrics()
print("\nsummary_metrics() xarray variables:", list(summary_metrics.data_vars))
inc_outcome = np.asarray(analysis.incremental_outcome())
print("Incremental outcome draws shape (chains, draws, channels):", inc_outcome.shape)

我们考察了渠道响应曲线、广告库存衰减(adstock decay)和希尔(Hill)饱和行为,以理解边际收益递减和结转效应。我们使用 Analyzer API 提取后验 ROI 抽样,并计算渠道层面的均值和可信区间。我们还进行了概率性渠道比较,检查汇总指标,并获取增量结果估计。

budget_optimizer = optimizer.BudgetOptimizer(mmm)
optimization_results = budget_optimizer.optimize()
show(optimization_results.plot_budget_allocation(),
    "Optimized budget allocation")
show(optimization_results.plot_spend_delta(),
    "Recommended spend change per channel")
show(optimization_results.plot_incremental_outcome_delta(),
    "Incremental outcome gained by reallocating")
show(optimization_results.plot_response_curves(),
    "Response curves with current vs. optimal spend points")
flexible_results = budget_optimizer.optimize(
   fixed_budget=False,
   target_roi=1.5,
)
show(flexible_results.plot_budget_allocation(),
    "Flexible-budget allocation at target ROI = 1.5")
mmm_summarizer = summarizer.Summarizer(mmm)
mmm_summarizer.output_model_results_summary(
   "model_results_summary.html", "/content", "2021-01-25", "2024-01-15"
)
optimization_results.output_optimization_summary(
   "budget_optimization_summary.html", "/content"
)
print("Reports written to /content/model_results_summary.html "
     "and /content/budget_optimization_summary.html")
save_path = "/content/saved_mmm.pkl"
model.save_mmm(mmm, save_path)
mmm_reloaded = model.load_mmm(save_path)
print("Model saved and reloaded from", save_path)
roi_reloaded = np.asarray(analyzer.Analyzer(mmm_reloaded).roi()).mean(axis=(0, 1))
print("Reloaded ROI means:", np.round(roi_reloaded, 3))
print("\n" + "=" * 70)
print("TUTORIAL COMPLETE ✔")
print("Next steps with YOUR data:")
print("  1. Replace CSV_URL and CoordToColumns with your columns.")
print("  2. Calibrate per-channel ROI priors with experiment results.")
print("  3. Check R-hat < 1.05 before trusting any output.")
print("  4. Use holdout_id in ModelSpec for out-of-sample validation.")
print("=" * 70)

我们在固定预算和目标 ROI 两种场景下优化营销支出。我们可视化推荐分配方案、支出变化、预期结果增益以及响应曲线上的优化位置。随后我们生成 HTML 报告,保存并重新加载拟合模型,并验证恢复后的模型能够复现相同的 ROI 估计。

总而言之,我们开发了一个端到端框架,用于衡量媒体表现并将贝叶斯模型估计转化为实际的营销决策。在解读渠道层面结果之前,我们使用收敛诊断和预测指标对模型进行了验证,从而避免依赖不稳定或具有误导性的估计。我们使用贡献度、ROI、边际 ROI、有效性、结转效应和饱和度来评估每个渠道,并利用后验抽样来量化不确定性并进行概率性渠道比较。随后,我们将这些洞察转化为固定预算和目标 ROI 场景下的优化预算分配方案。最后,我们导出了结果并持久化保存了拟合模型,从而能够在无需重新运行计算成本最高的步骤的情况下,重复分析、测试新场景,并将该工作流适配到真实业务数据中。


来源:MarkTechPost(RSS) · marktechpost.com

用 Google Meridian 构建端到端贝叶斯营销组合模型:媒体测量、ROI 分析与预算优化

MarkTechPost(RSS)·2026-08-06 05:56·1小时前·Sana Hassan
AI 导读

本教程使用 Google Meridian 构建完整的贝叶斯营销组合建模工作流,涵盖数据加载、ROI 先验配置、NUTS 采样拟合及收敛性评估。通过 Analyzer API 提取渠道贡献、ROI、边际 ROI、adstock 与饱和曲线等后验指标,并计算渠道间 ROI 比较概率。最后用 BudgetOptimizer 优化固定与灵活预算,生成可分享的 HTML 报告并保存模型复用。

正文 · AI 翻译

在本教程中,我们使用 Google Meridian 构建一个完整的贝叶斯营销组合建模工作流。我们首先安装所需的库、验证 GPU 可用性,并探索一个地理层级营销数据集,其中包含媒体曝光量、花费、控制变量、促销活动、转化量、人口和收入。然后,我们将原始列映射到 Meridian 的数据模式,定义可解释的基于 ROI 的先验分布,并在使用先验和后验 NUTS 采样拟合模型之前配置模型。训练完成后,我们评估收敛性和预测准确性,检查渠道贡献、ROI、边际 ROI、有效性、广告库存效应、饱和度和响应曲线,并使用 Analyzer API 提取自定义后验指标。最后,我们通过优化固定和灵活预算、生成可共享的 HTML 报告以及保存拟合模型以供复用,来完成整个工作流。

!pip install --upgrade -q "google-meridian[and-cuda]"
import numpy as np
import pandas as pd
import altair as alt
import tensorflow as tf
import tensorflow_probability as tfp
from IPython.display import display, HTML
from meridian import constants
from meridian.data import load
from meridian.model import model
from meridian.model import spec
from meridian.model import prior_distribution
from meridian.analysis import analyzer
from meridian.analysis import visualizer
from meridian.analysis import optimizer
from meridian.analysis import summarizer
def show(chart_or_obj, title=None):
   if title:
       display(HTML(f"<h3 style='font-family:sans-serif'>{title}</h3>"))
   display(chart_or_obj)
print("TensorFlow:", tf.__version__)
gpus = tf.config.experimental.list_physical_devices("GPU")
print("GPUs detected:", gpus if gpus else "NONE — sampling will be slow on CPU!")
CSV_URL = (
   "https://raw.githubusercontent.com/google/meridian/refs/heads/main/"
   "meridian/data/simulated_data/csv/geo_all_channels.csv"
)
df = pd.read_csv(CSV_URL)
print("\nShape:", df.shape)
print("Geos:", df["geo"].nunique(), "| Weeks:", df["time"].nunique())
print("Date range:", df["time"].min(), "->", df["time"].max())
display(df.head())
spend_cols = [c for c in df.columns if c.endswith("_spend")]
spend_share = df[spend_cols].sum().rename("total_spend").reset_index()
spend_share["share_%"] = 100 * spend_share["total_spend"] / spend_share["total_spend"].sum()
display(spend_share)
kpi_by_week = df.groupby("time")["conversions"].sum().reset_index()
show(
   alt.Chart(kpi_by_week).mark_line().encode(
       x=alt.X("time:T", title="Week"),
       y=alt.Y("conversions:Q", title="Total conversions (all geos)"),
   ).properties(width=700, height=250),
   "National KPI over time",
)

我们安装支持 GPU 版 TensorFlow 的 Google Meridian,并导入建模、可视化和分析所需的库。我们验证运行时环境、检测可用的 GPU,并加载 Meridian 的模拟地理层级营销数据集。我们还通过查看数据维度、日期覆盖范围、花费分布和全国转化趋势来进行初步探索性分析。

coord_to_columns = load.CoordToColumns(
   time="time",
   geo="geo",
   controls=["competitor_sales_control", "sentiment_score_control"],
   population="population",
   kpi="conversions",
   revenue_per_kpi="revenue_per_conversion",
   media=[
       "Channel0_impression",
       "Channel1_impression",
       "Channel2_impression",
       "Channel3_impression",
       "Channel4_impression",
   ],
   media_spend=[
       "Channel0_spend",
       "Channel1_spend",
       "Channel2_spend",
       "Channel3_spend",
       "Channel4_spend",
   ],
   organic_media=["Organic_channel0_impression"],
   non_media_treatments=["Promo"],
)
media_to_channel = {f"Channel{i}_impression": f"Channel_{i}" for i in range(5)}
media_spend_to_channel = {f"Channel{i}_spend": f"Channel_{i}" for i in range(5)}
loader = load.CsvDataLoader(
   csv_path=CSV_URL,
   kpi_type="non_revenue",
   coord_to_columns=coord_to_columns,
   media_to_channel=media_to_channel,
   media_spend_to_channel=media_spend_to_channel,
)
data = loader.load()
print("\nInputData loaded. Media tensor shape (geo, time, channel):", data.media.shape)
roi_mu = 0.2
roi_sigma = 0.9
prior = prior_distribution.PriorDistribution(
   roi_m=tfp.distributions.LogNormal(roi_mu, roi_sigma, name=constants.ROI_M)
)
model_spec = spec.ModelSpec(prior=prior)
mmm = model.Meridian(input_data=data, model_spec=model_spec)

我们使用 CoordToColumns 将原始数据集列映射到 Meridian 的预期模式。我们定义付费媒体、花费、自然渠道、控制变量、处理变量、人口、KPI 和收入相关字段,然后加载结构化输入数据。接着,我们配置基于 ROI 的先验分布,创建模型规格,并初始化 Meridian 模型。

mmm.sample_prior(500)
mmm.sample_posterior(
   n_chains=7,
   n_adapt=500,
   n_burnin=500,
   n_keep=1000,
   seed=1,
)
print("Sampling complete.")
model_diagnostics = visualizer.ModelDiagnostics(mmm)
show(model_diagnostics.plot_rhat_boxplot(), "R-hat convergence check (want < 1.05)")
show(
   model_diagnostics.plot_prior_and_posterior_distribution(),
   "Prior vs. posterior (ROI parameters)",
)
model_fit = visualizer.ModelFit(mmm)
show(model_fit.plot_model_fit(), "Model fit: expected vs. actual outcome")
display(model_diagnostics.predictive_accuracy_table())
media_summary = visualizer.MediaSummary(mmm)
display(media_summary.summary_table())
show(media_summary.plot_channel_contribution_area_chart(),
    "Outcome decomposition over time (baseline + channels)")
show(media_summary.plot_contribution_pie_chart(),
    "Share of outcome: baseline vs. media")
show(media_summary.plot_spend_vs_contribution(),
    "Spend share vs. contribution share (spot over/under-investment)")
show(media_summary.plot_roi_bar_chart(),
    "ROI by channel (with credible intervals)")
show(media_summary.plot_roi_vs_effectiveness(),
    "ROI vs. effectiveness (bubble = spend)")
show(media_summary.plot_roi_vs_mroi(),
    "ROI vs. marginal ROI — mROI drives optimization, not average ROI")

我们从先验分布中采样,并使用跨多条链的后验 NUTS 采样来拟合贝叶斯模型。我们使用 R-hat 诊断评估收敛性,比较先验和后验分布,并评估模型与观测结果的拟合程度。我们还分析预测准确性、渠道贡献、ROI、边际 ROI 和媒体有效性。

media_effects = visualizer.MediaEffects(mmm)
show(media_effects.plot_response_curves(),
    "Response curves (incremental outcome vs. spend)")
show(media_effects.plot_adstock_decay(),
    "Adstock decay by channel")
show(media_effects.plot_hill_curves(),
    "Hill saturation curves by channel")
analysis = analyzer.Analyzer(mmm)
roi_draws = analysis.roi()
roi_np = np.asarray(roi_draws)
channels = list(data.media_channel.values)
roi_table = pd.DataFrame({
   "channel": channels,
   "roi_mean": roi_np.mean(axis=(0, 1)),
   "roi_p05": np.quantile(roi_np, 0.05, axis=(0, 1)),
   "roi_p95": np.quantile(roi_np, 0.95, axis=(0, 1)),
})
print("\nPosterior ROI summary (custom, from raw draws):")
display(roi_table)
p_better = (roi_np[..., 1] > roi_np[..., 0]).mean()
print(f"P(ROI Channel_1 > ROI Channel_0) = {p_better:.1%}")
summary_metrics = analysis.summary_metrics()
print("\nsummary_metrics() xarray variables:", list(summary_metrics.data_vars))
inc_outcome = np.asarray(analysis.incremental_outcome())
print("Incremental outcome draws shape (chains, draws, channels):", inc_outcome.shape)

我们考察了渠道响应曲线、广告库存衰减(adstock decay)和希尔(Hill)饱和行为,以理解边际收益递减和结转效应。我们使用 Analyzer API 提取后验 ROI 抽样,并计算渠道层面的均值和可信区间。我们还进行了概率性渠道比较,检查汇总指标,并获取增量结果估计。

budget_optimizer = optimizer.BudgetOptimizer(mmm)
optimization_results = budget_optimizer.optimize()
show(optimization_results.plot_budget_allocation(),
    "Optimized budget allocation")
show(optimization_results.plot_spend_delta(),
    "Recommended spend change per channel")
show(optimization_results.plot_incremental_outcome_delta(),
    "Incremental outcome gained by reallocating")
show(optimization_results.plot_response_curves(),
    "Response curves with current vs. optimal spend points")
flexible_results = budget_optimizer.optimize(
   fixed_budget=False,
   target_roi=1.5,
)
show(flexible_results.plot_budget_allocation(),
    "Flexible-budget allocation at target ROI = 1.5")
mmm_summarizer = summarizer.Summarizer(mmm)
mmm_summarizer.output_model_results_summary(
   "model_results_summary.html", "/content", "2021-01-25", "2024-01-15"
)
optimization_results.output_optimization_summary(
   "budget_optimization_summary.html", "/content"
)
print("Reports written to /content/model_results_summary.html "
     "and /content/budget_optimization_summary.html")
save_path = "/content/saved_mmm.pkl"
model.save_mmm(mmm, save_path)
mmm_reloaded = model.load_mmm(save_path)
print("Model saved and reloaded from", save_path)
roi_reloaded = np.asarray(analyzer.Analyzer(mmm_reloaded).roi()).mean(axis=(0, 1))
print("Reloaded ROI means:", np.round(roi_reloaded, 3))
print("\n" + "=" * 70)
print("TUTORIAL COMPLETE ✔")
print("Next steps with YOUR data:")
print("  1. Replace CSV_URL and CoordToColumns with your columns.")
print("  2. Calibrate per-channel ROI priors with experiment results.")
print("  3. Check R-hat < 1.05 before trusting any output.")
print("  4. Use holdout_id in ModelSpec for out-of-sample validation.")
print("=" * 70)

我们在固定预算和目标 ROI 两种场景下优化营销支出。我们可视化推荐分配方案、支出变化、预期结果增益以及响应曲线上的优化位置。随后我们生成 HTML 报告,保存并重新加载拟合模型,并验证恢复后的模型能够复现相同的 ROI 估计。

总而言之,我们开发了一个端到端框架,用于衡量媒体表现并将贝叶斯模型估计转化为实际的营销决策。在解读渠道层面结果之前,我们使用收敛诊断和预测指标对模型进行了验证,从而避免依赖不稳定或具有误导性的估计。我们使用贡献度、ROI、边际 ROI、有效性、结转效应和饱和度来评估每个渠道,并利用后验抽样来量化不确定性并进行概率性渠道比较。随后,我们将这些洞察转化为固定预算和目标 ROI 场景下的优化预算分配方案。最后,我们导出了结果并持久化保存了拟合模型,从而能够在无需重新运行计算成本最高的步骤的情况下,重复分析、测试新场景,并将该工作流适配到真实业务数据中。


来源:MarkTechPost(RSS)· marktechpost.com