Skip to content

Commit

Permalink
pymc marketing intro
Browse files Browse the repository at this point in the history
  • Loading branch information
juanitorduz committed Jan 11, 2025
1 parent f0a353e commit 61c5209
Show file tree
Hide file tree
Showing 8 changed files with 306 additions and 13 deletions.
189 changes: 181 additions & 8 deletions Presentations/amld_2025/amld_2025.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,71 @@
margin: 0 0.8em 0.2em -1em; /* quarto-specific, see https://github.com/quarto-dev/quarto-cli/issues/4556 */
vertical-align: middle;
}
/* CSS for syntax highlighting */
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
.sourceCode { overflow: visible; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { display: inline-block; text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
}
pre.numberSource { margin-left: 3em; padding-left: 4px; }
div.sourceCode
{ color: #e1e4e8; background-color: #24292e; }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span { color: #e1e4e8; } /* Normal */
code span.al { color: #ff5555; font-weight: bold; } /* Alert */
code span.an { color: #6a737d; } /* Annotation */
code span.at { color: #f97583; } /* Attribute */
code span.bn { color: #79b8ff; } /* BaseN */
code span.bu { color: #f97583; } /* BuiltIn */
code span.cf { color: #f97583; } /* ControlFlow */
code span.ch { color: #9ecbff; } /* Char */
code span.cn { color: #79b8ff; } /* Constant */
code span.co { color: #6a737d; } /* Comment */
code span.cv { color: #6a737d; } /* CommentVar */
code span.do { color: #6a737d; } /* Documentation */
code span.dt { color: #f97583; } /* DataType */
code span.dv { color: #79b8ff; } /* DecVal */
code span.er { color: #ff5555; text-decoration: underline; } /* Error */
code span.ex { color: #f97583; font-weight: bold; } /* Extension */
code span.fl { color: #79b8ff; } /* Float */
code span.fu { color: #b392f0; } /* Function */
code span.im { color: #9ecbff; } /* Import */
code span.in { color: #6a737d; } /* Information */
code span.kw { color: #f97583; } /* Keyword */
code span.op { color: #e1e4e8; } /* Operator */
code span.ot { color: #b392f0; } /* Other */
code span.pp { color: #f97583; } /* Preprocessor */
code span.re { color: #6a737d; } /* RegionMarker */
code span.sc { color: #79b8ff; } /* SpecialChar */
code span.ss { color: #9ecbff; } /* SpecialString */
code span.st { color: #9ecbff; } /* String */
code span.va { color: #ffab70; } /* Variable */
code span.vs { color: #9ecbff; } /* VerbatimString */
code span.wa { color: #ff5555; } /* Warning */
</style>
<link rel="stylesheet" href="amld_2025_files/libs/revealjs/dist/theme/quarto-4b2d1750cd98acf9ce31f523000b3309.css">
<link rel="stylesheet" href="amld_2025_files/style.css">
Expand Down Expand Up @@ -226,6 +291,10 @@ <h2>Saturation Effect</h2>
</div>

<img data-src="amld_2025_files/static/images/saturation.png" class="quarto-figure quarto-figure-center r-stretch" width="1000"></section>
<section id="media-transformations" class="slide level2">
<h2>Media Transformations</h2>

<img data-src="amld_2025_files/static/images/media_transformations.png" class="quarto-figure quarto-figure-center r-stretch" width="1000"></section>
<section id="additional-effects" class="slide level2">
<h2>Additional Effects</h2>

Expand All @@ -237,23 +306,127 @@ <h2>MMM as a Causal Model</h2>
<p><a href="https://www.pymc-marketing.io/en/latest/notebooks/mmm/mmm_roas.html">PyMC-Marketing Example: Unobserved Confounders, ROAS and Lift Tests</a></p>
</div>
</section>
<section id="why-bayesian-mmms" class="slide level2">
<section id="why-bayesian-mmms" class="slide level2 smaller">
<h2>Why Bayesian MMMs?</h2>
<h3 id="some-mmm-challenges">Some MMM Challenges</h3>
<div>
<ul>
<li class="fragment"><p>Limited data (typically 2-3 years of data, sometimes weekly granularity).</p></li>
<li class="fragment"><p>Media variables are generally very correlated.</p></li>
<li class="fragment"><p>Unobserved confounders (e.g.&nbsp;competitors investments).</p></li>
</ul>
</div>
<h3 id="bayesian-mmms">Bayesian MMMs</h3>
<div>
<ul>
<li></li>
<li class="fragment"><p>Uncertainty quantification.</p></li>
<li class="fragment"><p>Domain knowledge through priors.</p></li>
<li class="fragment"><p>Lift test calibration (e.g.&nbsp;geo-tests or switch-back experiments).</p></li>
<li class="fragment"><p>Time-varying parameters with Bayesian regularization (e.g.&nbsp;strong priors or hierarchies).</p></li>
<li class="fragment"><p>Risk-based budget optimization.</p></li>
</ul>
</div>
</section>
<section id="pymc-marketing" class="slide level2">
<h2>PyMC-Marketing</h2>
<ul>
<li></li>
</ul>

<img data-src="amld_2025_files/static/images/logos/marketing-logo-light.jpg" class="quarto-figure quarto-figure-center r-stretch" width="1000"><blockquote>
<p>Bayesian marketing toolbox in PyMC. Media Mix (MMM), customer lifetime value (CLV), buy-till-you-die (BTYD) models and more.</p>
</blockquote>
<div class="footer">
<p><a href="https://www.pymc-marketing.io/">PyMC-Marketing</a></p>
</div>
</section>
<section id="parameter-recovery-example" class="slide level2">
<h2>Parameter Recovery Example</h2>
<section id="pymc-marketing-api" class="slide level2">
<h2>PyMC-Marketing API</h2>
<div class="sourceCode" id="cb1" data-code-line-numbers="|1-3|4-11|13-23|25-36"><pre class="sourceCode numberSource python number-lines code-with-copy"><code class="sourceCode python"><span id="cb1-1"><a href=""></a><span class="im">from</span> pymc_marketing.mmm <span class="im">import</span> MMM, GeometricAdstock, LogisticSaturation</span>
<span id="cb1-2"><a href=""></a><span class="im">from</span> pymc_marketing.prior <span class="im">import</span> Prior</span>
<span id="cb1-3"><a href=""></a></span>
<span id="cb1-4"><a href=""></a><span class="co"># Define priors</span></span>
<span id="cb1-5"><a href=""></a>my_model_config <span class="op">=</span> {</span>
<span id="cb1-6"><a href=""></a> <span class="st">"intercept"</span>: Prior(<span class="st">"Normal"</span>, mu<span class="op">=</span><span class="fl">0.5</span>, sigma<span class="op">=</span><span class="fl">0.1</span>),</span>
<span id="cb1-7"><a href=""></a> ...,</span>
<span id="cb1-8"><a href=""></a> <span class="st">"likelihood"</span>: Prior(</span>
<span id="cb1-9"><a href=""></a> <span class="st">"TruncatedNormal"</span>, lower<span class="op">=</span><span class="dv">0</span>, sigma<span class="op">=</span>Prior(<span class="st">"Exponential"</span>, lam<span class="op">=</span><span class="dv">1</span>)</span>
<span id="cb1-10"><a href=""></a> ),</span>
<span id="cb1-11"><a href=""></a>}</span>
<span id="cb1-12"><a href=""></a></span>
<span id="cb1-13"><a href=""></a><span class="co"># Define the model</span></span>
<span id="cb1-14"><a href=""></a>mmm <span class="op">=</span> MMM(</span>
<span id="cb1-15"><a href=""></a> model_config<span class="op">=</span>my_model_config,</span>
<span id="cb1-16"><a href=""></a> date_column<span class="op">=</span><span class="st">"date_week"</span>,</span>
<span id="cb1-17"><a href=""></a> adstock<span class="op">=</span>GeometricAdstock(l_max<span class="op">=</span><span class="dv">8</span>),</span>
<span id="cb1-18"><a href=""></a> saturation<span class="op">=</span>LogisticSaturation(),</span>
<span id="cb1-19"><a href=""></a> channel_columns<span class="op">=</span>channel_columns,</span>
<span id="cb1-20"><a href=""></a> control_columns<span class="op">=</span>control_columns,</span>
<span id="cb1-21"><a href=""></a> time_varying_intercept<span class="op">=</span><span class="va">True</span>,</span>
<span id="cb1-22"><a href=""></a> yearly_seasonality<span class="op">=</span><span class="dv">2</span>,</span>
<span id="cb1-23"><a href=""></a>)</span>
<span id="cb1-24"><a href=""></a></span>
<span id="cb1-25"><a href=""></a><span class="co"># Fit the model</span></span>
<span id="cb1-26"><a href=""></a>_ <span class="op">=</span> mmm.fit(</span>
<span id="cb1-27"><a href=""></a> X<span class="op">=</span>X,</span>
<span id="cb1-28"><a href=""></a> y<span class="op">=</span>y,</span>
<span id="cb1-29"><a href=""></a> target_accept<span class="op">=</span><span class="fl">0.85</span>,</span>
<span id="cb1-30"><a href=""></a> chains<span class="op">=</span><span class="dv">4</span>,</span>
<span id="cb1-31"><a href=""></a> draws<span class="op">=</span><span class="dv">2_000</span>,</span>
<span id="cb1-32"><a href=""></a> nuts_sampler<span class="op">=</span><span class="st">"numpyro"</span>, <span class="co"># &lt;- We can use many samplers and backends!</span></span>
<span id="cb1-33"><a href=""></a> random_seed<span class="op">=</span>rng,</span>
<span id="cb1-34"><a href=""></a>)</span>
<span id="cb1-35"><a href=""></a><span class="co"># Sample from the posterior predictive distribution</span></span>
<span id="cb1-36"><a href=""></a>_ <span class="op">=</span> mmm.sample_posterior_predictive(X, extend_idata<span class="op">=</span><span class="va">True</span>, combined<span class="op">=</span><span class="va">True</span>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</section>
<section id="prior-specification" class="slide level2">
<h2>Prior Specification</h2>

<img data-src="amld_2025_files/static/images/beta_priors.png" class="quarto-figure quarto-figure-center r-stretch" width="1000"><div class="footer">
<p><a href="https://www.pymc-marketing.io/en/latest/notebooks/general/prior_predictive.html">PyMC-Marketing Example: Prior Predictive Modeling</a></p>
</div>
</section>
<section id="attribution-decomposition" class="slide level2">
<h2>Attribution Decomposition</h2>
<div class="sourceCode" id="cb2"><pre class="sourceCode numberSource python number-lines code-with-copy"><code class="sourceCode python"><span id="cb2-1"><a href=""></a>mmm.plot_components_contributions(original_scale<span class="op">=</span><span class="va">True</span>)<span class="op">;</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>

<img data-src="amld_2025_files/static/images/model_components.png" class="quarto-figure quarto-figure-center r-stretch" width="1000"></section>
<section id="return-on-ad-spend-roas" class="slide level2">
<h2>Return on Ad Spend (ROAS)</h2>

<img data-src="amld_2025_files/static/images/roas_posterior.png" class="quarto-figure quarto-figure-center r-stretch" width="1000"></section>
<section id="out-of-sample-forecasting" class="slide level2 smaller">
<h2>Out-of-sample Forecasting</h2>
<div class="columns">
<div class="column" style="width:50%;">
<div class="quarto-figure quarto-figure-center">
<figure>
<p><img data-src="amld_2025_files/static/images/tscv.png" class="quarto-figure quarto-figure-center" width="500"></p>
</figure>
</div>
</div><div class="column" style="width:50%;">
<div title="Model Evaluation Framework">
<div class="callout callout-tip callout-titled callout-style-default">
<div class="callout-body">
<div class="callout-title">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<p><strong>Model Evaluation Framework</strong></p>
</div>
<div class="callout-content">
<p>Evaluate the model on the holdout set:</p>
<ul>
<li></li>
<li>Prediction accuracy (CRPS)</li>
<li>Parameter Stability</li>
</ul>
</div>
</div>
</div>
</div>
<div class="quarto-figure quarto-figure-center">
<figure>
<p><img data-src="amld_2025_files/static/images/tscv_alpha.png" class="quarto-figure quarto-figure-center" width="900"></p>
</figure>
</div>
</div></div>

</section>
</div>
Expand Down
130 changes: 125 additions & 5 deletions Presentations/amld_2025/amld_2025.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ $$

![](amld_2025_files/static/images/saturation.png){fig-align="center" width="1000"}

## Media Transformations

![](amld_2025_files/static/images/media_transformations.png){fig-align="center" width="1000"}

## Additional Effects

Expand All @@ -103,15 +106,132 @@ $$
[PyMC-Marketing Example: Unobserved Confounders, ROAS and Lift Tests](https://www.pymc-marketing.io/en/latest/notebooks/mmm/mmm_roas.html)
:::

## Why Bayesian MMMs?
## Why Bayesian MMMs? {.smaller}

### Some MMM Challenges

::: incremental

- Limited data (typically 2-3 years of data, sometimes weekly granularity).

- Media variables are generally very correlated.

- Unobserved confounders (e.g. competitors investments).

:::

### Bayesian MMMs

::: incremental

- Uncertainty quantification.

- ...
- Domain knowledge through priors.

- Lift test calibration (e.g. geo-tests or switch-back experiments).

- Time-varying parameters with Bayesian regularization (e.g. strong priors or hierarchies).

- Risk-based budget optimization.

:::

## PyMC-Marketing

- ...
![](amld_2025_files/static/images/logos/marketing-logo-light.jpg){fig-align="center" width="1000"}

> Bayesian marketing toolbox in PyMC. Media Mix (MMM), customer lifetime value (CLV), buy-till-you-die (BTYD) models and more.
::: footer
[PyMC-Marketing](https://www.pymc-marketing.io/)
:::

## PyMC-Marketing API

```{.python code-line-numbers="|1-3|4-11|13-23|25-36"}
from pymc_marketing.mmm import MMM, GeometricAdstock, LogisticSaturation
from pymc_marketing.prior import Prior
# Define priors
my_model_config = {
"intercept": Prior("Normal", mu=0.5, sigma=0.1),
...,
"likelihood": Prior(
"TruncatedNormal", lower=0, sigma=Prior("Exponential", lam=1)
),
}
# Define the model
mmm = MMM(
model_config=my_model_config,
date_column="date_week",
adstock=GeometricAdstock(l_max=8),
saturation=LogisticSaturation(),
channel_columns=channel_columns,
control_columns=control_columns,
time_varying_intercept=True,
yearly_seasonality=2,
)
# Fit the model
_ = mmm.fit(
X=X,
y=y,
target_accept=0.85,
chains=4,
draws=2_000,
nuts_sampler="numpyro", # <- We can use many samplers and backends!
random_seed=rng,
)
# Sample from the posterior predictive distribution
_ = mmm.sample_posterior_predictive(X, extend_idata=True, combined=True)
```

## Prior Specification

![](amld_2025_files/static/images/beta_priors.png){fig-align="center" width="1000"}

::: footer
[PyMC-Marketing Example: Prior Predictive Modeling](https://www.pymc-marketing.io/en/latest/notebooks/general/prior_predictive.html)
:::

## Attribution Decomposition

```{.python}
mmm.plot_components_contributions(original_scale=True);
```

![](amld_2025_files/static/images/model_components.png){fig-align="center" width="1000"}


## Return on Ad Spend (ROAS)

![](amld_2025_files/static/images/roas_posterior.png){fig-align="center" width="1000"}

## Out-of-sample Forecasting {.smaller}

::: {.columns}

::: {.column width="50%"}

## Parameter Recovery Example
![](amld_2025_files/static/images/tscv.png){fig-align="center" width="500"}

- ...
:::


::: {.column width="50%"}

::: {.callout-tip title="Model Evaluation Framework"}

Evaluate the model on the holdout set:

- Prediction accuracy (CRPS)
- Parameter Stability

:::

![](amld_2025_files/static/images/tscv_alpha.png){fig-align="center" width="900"}

:::

:::
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 61c5209

Please sign in to comment.