Skip to main content

informative-priors-for-mmm

Expert guide on calculating and setting informative priors for PyMC-Marketing MMM models based on data characteristics and domain knowledge. Use when configuring priors for intercept, channel effects, or adstock parameters.

Aller à l'installation

Informations de source

Dépôt
pymc-labs/decision-lab
Dernière activité de la source
31 mars 2026 à 12:08
Langue détectée de SKILL.md
anglais
Étoiles
190
Forks
14

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
Informative Priors for MMM
description
Expert guide on calculating and setting informative priors for PyMC-Marketing MMM models based on data characteristics and domain knowledge. Use when configuring priors for intercept, channel effects, or adstock parameters.
# Guide: Calculating Informative Priors for MMM This guide describes how to calculate tighter, more informative priors for PyMC-Marketing MMM based on data characteristics. **Important:** The default wide priors (`intercept: Normal(mu=0, sigma=2)`) often lead to better-calibrated posteriors. Only use informative priors when you have strong domain knowledge or specific requirements. Overly tight priors can lead to overconfident (underdispersed) posteriors. ## When to Use Informative Priors - You have domain knowledge about expected ROAS ranges, decay rates, or effect sizes - You have results from previous studies or models to inform your beliefs - Business constraints require certain parameter ranges - You're doing sensitivity analysis to understand prior impact ## Step 1: Calculate Scaled Statistics PyMC-Marketing uses MaxAbsScaler for target and channels (divides by max absolute value). ```python # Calculate how your data looks in scaled space y_scaled_max = 1.0 # By definition (MaxAbsScaler) y_scaled_mean = y.mean() / y.max() # Typically 0.6-0.8 y_scaled_min = y.min() / y.max() # Typically 0.4-0.6 print(f"Scaled y statistics:") print(f" min: {y_scaled_min:.3f}") print(f" mean: {y_scaled_mean:.3f}") print(f" max: {y_scaled_max:.3f}") ``` ## Step 2: Reason About Intercept Prior The intercept represents predicted y when all channels = 0 (no marketing spend). ```python # Intercept should be LESS than scaled mean (marketing contributes positively!) # Reasonable range in scaled space: 0.3 to 0.7 # If you believe ~20% of sales come from marketing: intercept_mu = y_scaled_mean * 0.8 # Sigma controls uncertainty - smaller = more confident # 0.2 = tight (use if you have strong beliefs) # 0.5 = moderate # 1.0+ = wide (closer to default) intercept_sigma = 0.2 print(f"Intercept prior: Normal(mu={intercept_mu:.2f}, sigma={intercept_sigma})") ``` ## Step 3: Reason About Channel Effect Priors `saturation_beta` represents the effect size per channel in scaled space. ```python # If 3 channels share ~20% of y, each contributes ~0.07 on average n_channels = len(channel_columns) expected_total_channel_contribution = 1 - intercept_mu # What's left after baseline expected_per_channel = expected_total_channel_contribution / n_channels # Prior sigma: allow 2-3x expected value for exploration beta_sigma = expected_per_channel * 2.5 print(f"Channel beta prior: HalfNormal(sigma={beta_sigma:.2f})") print(f" Expected per-channel contribution: ~{expected_per_channel:.2f}") ``` ## Step 4: Set model_config ```python from pymc_extras.prior import Prior model_config = { "intercept": Prior("Normal", mu=intercept_mu, sigma=intercept_sigma), "saturation_beta": Prior("HalfNormal", sigma=beta_sigma), # adstock_alpha: Beta(2,3) favors values 0.3-0.6, good default "adstock_alpha": Prior("Beta", alpha=2, beta=3), } mmm = MMM( date_column=date_column, channel_columns=channel_columns, adstock=GeometricAdstock(l_max=l_max), saturation=LogisticSaturation(), model_config=model_config, # ... other parameters ) ``` ## Step 5: Validate with Prior Predictive Checks After setting informative priors, always validate: ```python mmm.build_model(X=X, y=y) mmm.sample_prior_predictive(X=X, samples=1000, random_seed=None # Set for reproducibility) # Check prior predictive coverage from mmm_lib import check_prior_predictive_coverage coverage = check_prior_predictive_coverage(mmm, y, hdi_prob=0.94) # With informative priors, expect: # - HDI should be ~2-5x observed range (tighter than defaults) # - Some negative predictions OK, but <10% of samples # - Coverage should still be high (>80%) ``` ## Adjusting Based on Prior Predictive | Observation | Action | |-------------|--------| | HDI too wide (>10x observed range) | Decrease sigma values | | HDI too narrow (<2x observed range) | Increase sigma values | | Too many negative predictions (>10%) | Increase intercept mu or use HalfNormal | | Coverage too low (<50%) | Loosen priors (increase sigmas) | ## Example: Full Workflow ```python import pandas as pd import numpy as np from pymc_marketing.mmm.multidimensional import MMM from pymc_marketing.mmm import GeometricAdstock, LogisticSaturation from pymc_extras.prior import Prior # Load data df = pd.read_parquet('cleaned_data.parquet') y = df[target_column] channel_columns = ['tv', 'digital', 'radio'] # Step 1: Calculate scaled statistics y_scaled_mean = y.mean() / y.max() print(f"Scaled y mean: {y_scaled_mean:.3f}") # Step 2: Set intercept (assume 25% from marketing) intercept_mu = y_scaled_mean * 0.75 intercept_sigma = 0.3 # Step 3: Set channel effects n_channels = len(channel_columns) expected_per_channel = (1 - intercept_mu) / n_channels beta_sigma = expected_per_channel * 2 # Step 4: Create model config model_config = { "intercept": Prior("Normal", mu=intercept_mu, sigma=intercept_sigma), "saturation_beta": Prior("HalfNormal", sigma=beta_sigma), "adstock_alpha": Prior("Beta", alpha=2, beta=3), } print(f"Model config:") print(f" intercept: Normal(mu={intercept_mu:.2f}, sigma={intercept_sigma})") print(f" saturation_beta: HalfNormal(sigma={beta_sigma:.2f})") print(f" adstock_alpha: Beta(alpha=2, beta=3)") # Create MMM with informative priors mmm = MMM( date_column='date', channel_columns=channel_columns, adstock=GeometricAdstock(l_max=8), saturation=LogisticSaturation(), model_config=model_config, ) # Step 5: Validate X = df[['date'] + channel_columns] mmm.build_model(X=X, y=y) mmm.sample_prior_predictive(X=X, samples=1000, random_seed=None # Set for reproducibility) # Check and adjust if needed... ``` ## Warning: Posterior Calibration Informative priors that are too tight can cause **underdispersed posteriors** (overconfident uncertainty estimates). Signs of this: - 94% HDI coverage << 94% (e.g., 50-60%) - Posterior intervals that don't contain true values If you observe this, **loosen your priors** by increasing sigma values, or revert to defaults. ## Additional Prior Types ### Beta Distribution for Bounded Parameters For parameters that must be between 0 and 1 (like adstock decay): ```python # Beta(2, 3) - mode around 0.33, favors lower values # Beta(2, 2) - symmetric around 0.5 # Beta(3, 2) - mode around 0.67, favors higher values model_config = { "adstock_alpha": Prior("Beta", alpha=2, beta=3), } ``` ### HalfNormal for Positive Parameters For strictly positive parameters: ```python model_config = { "saturation_beta": Prior("HalfNormal", sigma=0.5), "sigma": Prior("HalfNormal", sigma=1.0), } ``` ### LogNormal for Positive Parameters with Heavy Tails When you expect occasional large values: ```python model_config = { "saturation_lam": Prior("LogNormal", mu=0, sigma=1), } ``` ## Domain Knowledge Integration ### From Historical Data If you have previous MMM results: ```python # Use posterior mean from previous model as prior mean # Use posterior std * 2 as prior std (to allow for model differences) previous_intercept_mean = 0.65 previous_intercept_std = 0.1 model_config = { "intercept": Prior("Normal", mu=previous_intercept_mean, sigma=previous_intercept_std * 2), } ``` ### From Industry Benchmarks If you have industry benchmarks for ROAS or effect sizes: ```python # Example: TV typically has ROAS of 2-5x # In scaled space, this might translate to beta of 0.05-0.15 model_config = { "saturation_beta": Prior("TruncatedNormal", mu=0.1, sigma=0.05, lower=0), } ``` ### From Business Constraints If business logic requires certain constraints: ```python # Example: Marketing cannot contribute more than 50% of sales # Set intercept prior to be at least 0.5 model_config = { "intercept": Prior("TruncatedNormal", mu=0.7, sigma=0.1, lower=0.5), } ``` ## When to Use This Skill - Setting up initial priors for a new MMM - Adjusting priors based on prior predictive checks - Incorporating domain knowledge into the model - Debugging overly wide or narrow posterior distributions - Sensitivity analysis to understand prior impact on results
Voir sur GitHub