End-to-End Bayesian Marketing Mix Modeling with Google Meridian: Media Measurement, ROI Analysis, and Budget Optimization
In this tutorial, we construct a whole Bayesian advertising combine modeling workflow utilizing Google Meridian. We start by putting in the required libraries, verifying GPU availability, and exploring a geo-level advertising dataset that features media impressions, spend, controls, promotions, conversions, inhabitants, and income. We then map the uncooked columns to Meridian’s information schema, outline interpretable ROI-based priors, and configure the mannequin earlier than becoming it with prior and posterior NUTS sampling. After coaching, we consider convergence and predictive accuracy, study channel contributions, ROI, marginal ROI, effectiveness, adstock, saturation, and response curves, and use the Analyzer API to extract customized posterior metrics. We conclude the workflow by optimizing each mounted and versatile budgets, producing shareable HTML stories, and saving the fitted mannequin for reuse.
!pip set up --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.show import show, HTML
from meridian import constants
from meridian.information import load
from meridian.mannequin import mannequin
from meridian.mannequin import spec
from meridian.mannequin import prior_distribution
from meridian.evaluation import analyzer
from meridian.evaluation import visualizer
from meridian.evaluation import optimizer
from meridian.evaluation import summarizer
def present(chart_or_obj, title=None):
if title:
show(HTML(f"<h3 type='font-family:sans-serif'>{title}</h3>"))
show(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 likely be gradual on CPU!")
CSV_URL = (
"https://uncooked.githubusercontent.com/google/meridian/refs/heads/important/"
"meridian/information/simulated_data/csv/geo_all_channels.csv"
)
df = pd.read_csv(CSV_URL)
print("nShape:", df.form)
print("Geos:", df["geo"].nunique(), "| Weeks:", df["time"].nunique())
print("Date vary:", df["time"].min(), "->", df["time"].max())
show(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()
show(spend_share)
kpi_by_week = df.groupby("time")["conversions"].sum().reset_index()
present(
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, top=250),
"National KPI over time",
)
We set up Google Meridian with GPU-enabled TensorFlow help and import the libraries required for modeling, visualization, and evaluation. We confirm the runtime setting, detect accessible GPUs, and load Meridian’s simulated geo-level advertising dataset. We additionally carry out preliminary exploratory evaluation by reviewing information dimensions, date protection, spend distribution, and nationwide conversion developments.
coord_to_columns = load.CoordToColumns(
time="time",
geo="geo",
controls=["competitor_sales_control", "sentiment_score_control"],
inhabitants="inhabitants",
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 vary(5)}
media_spend_to_channel = {f"Channel{i}_spend": f"Channel_{i}" for i in vary(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,
)
information = loader.load()
print("nInputData loaded. Media tensor form (geo, time, channel):", information.media.form)
roi_mu = 0.2
roi_sigma = 0.9
prior = prior_distribution.PriorDistribution(
roi_m=tfp.distributions.LogNormal(roi_mu, roi_sigma, identify=constants.ROI_M)
)
model_spec = spec.ModelSpec(prior=prior)
mmm = mannequin.Meridian(input_data=information, model_spec=model_spec)
We map the uncooked dataset columns to Meridian’s anticipated schema utilizing CoordToColumns. We outline paid media, spend, natural channels, controls, remedies, inhabitants, KPI, and revenue-related fields earlier than loading the structured enter information. We then configure ROI-based priors, create the mannequin specification, and initialize the Meridian mannequin.
mmm.sample_prior(500)
mmm.sample_posterior(
n_chains=7,
n_adapt=500,
n_burnin=500,
n_keep=1000,
seed=1,
)
print("Sampling full.")
model_diagnostics = visualizer.ModelDiagnostics(mmm)
present(model_diagnostics.plot_rhat_boxplot(), "R-hat convergence verify (need < 1.05)")
present(
model_diagnostics.plot_prior_and_posterior_distribution(),
"Prior vs. posterior (ROI parameters)",
)
model_fit = visualizer.ModelMatch(mmm)
present(model_fit.plot_model_fit(), "Model match: anticipated vs. precise consequence")
show(model_diagnostics.predictive_accuracy_table())
media_summary = visualizer.MediaAbstract(mmm)
show(media_summary.summary_table())
present(media_summary.plot_channel_contribution_area_chart(),
"Outcome decomposition over time (baseline + channels)")
present(media_summary.plot_contribution_pie_chart(),
"Share of consequence: baseline vs. media")
present(media_summary.plot_spend_vs_contribution(),
"Spend share vs. contribution share (spot over/under-investment)")
present(media_summary.plot_roi_bar_chart(),
"ROI by channel (with credible intervals)")
present(media_summary.plot_roi_vs_effectiveness(),
"ROI vs. effectiveness (bubble = spend)")
present(media_summary.plot_roi_vs_mroi(),
"ROI vs. marginal ROI — mROI drives optimization, not common ROI")
We pattern from the prior and match the Bayesian mannequin utilizing posterior NUTS sampling throughout a number of chains. We consider convergence utilizing R-hat diagnostics, evaluate prior and posterior distributions, and assess mannequin match towards noticed outcomes. We additionally analyze predictive accuracy, channel contributions, ROI, marginal ROI, and media effectiveness.
media_effects = visualizer.MediaResults(mmm)
present(media_effects.plot_response_curves(),
"Response curves (incremental consequence vs. spend)")
present(media_effects.plot_adstock_decay(),
"Adstock decay by channel")
present(media_effects.plot_hill_curves(),
"Hill saturation curves by channel")
evaluation = analyzer.Analyzer(mmm)
roi_draws = evaluation.roi()
roi_np = np.asarray(roi_draws)
channels = checklist(information.media_channel.values)
roi_table = pd.DataBody({
"channel": channels,
"roi_mean": roi_np.imply(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 abstract (customized, from uncooked attracts):")
show(roi_table)
p_better = (roi_np[..., 1] > roi_np[..., 0]).imply()
print(f"P(ROI Channel_1 > ROI Channel_0) = {p_better:.1%}")
summary_metrics = evaluation.summary_metrics()
print("nsummary_metrics() xarray variables:", checklist(summary_metrics.data_vars))
inc_outcome = np.asarray(evaluation.incremental_outcome())
print("Incremental consequence attracts form (chains, attracts, channels):", inc_outcome.form)
We study channel response curves, adstock decay, and Hill saturation conduct to grasp diminishing returns and carryover results. We use the Analyzer API to extract posterior ROI attracts and calculate channel-level means and credible intervals. We additionally compute probabilistic channel comparisons, examine abstract metrics, and retrieve incremental consequence estimates.
budget_optimizer = optimizer.BudgetOptimizer(mmm)
optimization_results = budget_optimizer.optimize()
present(optimization_results.plot_budget_allocation(),
"Optimized funds allocation")
present(optimization_results.plot_spend_delta(),
"Recommended spend change per channel")
present(optimization_results.plot_incremental_outcome_delta(),
"Incremental consequence gained by reallocating")
present(optimization_results.plot_response_curves(),
"Response curves with present vs. optimum spend factors")
flexible_results = budget_optimizer.optimize(
fixed_budget=False,
target_roi=1.5,
)
present(flexible_results.plot_budget_allocation(),
"Flexible-budget allocation at goal ROI = 1.5")
mmm_summarizer = summarizer.Summarizer(mmm)
mmm_summarizer.output_model_results_summary(
"model_results_summary.html", "/content material", "2021-01-25", "2024-01-15"
)
optimization_results.output_optimization_summary(
"budget_optimization_summary.html", "/content material"
)
print("Reports written to /content material/model_results_summary.html "
"and /content material/budget_optimization_summary.html")
save_path = "/content material/saved_mmm.pkl"
mannequin.save_mmm(mmm, save_path)
mmm_reloaded = mannequin.load_mmm(save_path)
print("Model saved and reloaded from", save_path)
roi_reloaded = np.asarray(analyzer.Analyzer(mmm_reloaded).roi()).imply(axis=(0, 1))
print("Reloaded ROI means:", np.spherical(roi_reloaded, 3))
print("n" + "=" * 70)
print("TUTORIAL COMPLETE
")
print("Next steps with YOUR information:")
print(" 1. Replace CSV_URL and CoordToColumns with your columns.")
print(" 2. Calibrate per-channel ROI priors with experiment outcomes.")
print(" 3. Check R-hat < 1.05 earlier than trusting any output.")
print(" 4. Use holdout_id in ModelSpec for out-of-sample validation.")
print("=" * 70)
We optimize advertising spend beneath each fixed-budget and target-ROI eventualities. We visualize really helpful allocations, spend modifications, anticipated consequence good points, and optimized positions on response curves. We then generate HTML stories, save and reload the fitted mannequin, and confirm that the restored mannequin reproduces the identical ROI estimates.
In conclusion, we developed an end-to-end framework for measuring media efficiency and translating Bayesian mannequin estimates into sensible advertising choices. We validated the mannequin utilizing convergence diagnostics and predictive metrics earlier than deciphering channel-level outcomes, serving to us keep away from counting on unstable or deceptive estimates. We assessed every channel utilizing contribution, ROI, marginal ROI, effectiveness, carryover, and saturation, and used posterior attracts to quantify uncertainty and evaluate channels probabilistically. We then transformed these insights into optimized funds allocations beneath fixed-budget and target-ROI eventualities. Finally, we exported the outcomes and continued the fitted mannequin, permitting us to repeat evaluation, check new eventualities, and adapt the workflow to actual enterprise information with out rerunning probably the most computationally costly steps.
Check out the FULL CODES here. Also, be at liberty to observe us on Twitter and don’t overlook to affix our 150k+ML SubReddit and Subscribe to our Newsletter. Wait! are you on telegram? now you can join us on telegram as well.
Need to companion with us for selling your GitHub Repo OR Hugging Face Page OR Product Release OR Webinar and so forth.? Connect with us
The put up End-to-End Bayesian Marketing Mix Modeling with Google Meridian: Media Measurement, ROI Analysis, and Budget Optimization appeared first on MarkTechPost.
