在本教程中,我们使用 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)
我们检查渠道响应曲线、广告库存衰减和 Hill 饱和行为,以了解边际收益递减和结转效应。我们使用 Analyzer API 提取后验 ROI 样本,并计算渠道层面的均值和置信区间。我们还计算了概率性的渠道比较,检查了汇总指标,并检索了增量结果的估计值。
复制代码 使用其他浏览器 budget_optimizer = optimizer.BudgetOptimizer(mmm) optimization_results = budget_op