MMM validation: how to test a marketing mix model beyond its fit

A marketing mix model that fits last year's sales beautifully can still give you the wrong budget. Validation means checking prediction, stability and causal credibility separately, because they fail separately.

Mike Taylor By Mike Taylor · Updated · 9 min read

MMM validation is the work of checking a marketing mix model against evidence it wasn’t fitted to: its error on held-out weeks, how stable its channel estimates are when the data changes slightly, and whether its channel effects agree with independent experiments. A high R² or a low MAPE on the training data is where validation starts, not where it ends, because a model can fit history closely while giving the wrong answer about which channel drove the sales.

I’ve reviewed a lot of marketing mix models over the years, my own for clients like Booking.com and Time Out included, and the most common failure I see is a good fit being treated as proof that the model is right. Every library reports fit statistics and they’re easy to put on a slide, so they get mistaken for a certificate of accuracy. This page walks through the validation checks that actually separate a useful model from a well-fitted one, with an overfitting demo you can play with and a calculator for your own model’s errors.

Training fit, holdout fit and causal validity are different questions

The lesson in the Vexpower course on this is called “Validate beyond the fitted line”, and its core idea is that three questions get collapsed into one:

QuestionUseful evidence
Does the model describe the outcomes it trained on?In-sample fit (R², MAPE, NRMSE) and residual patterns
Does it describe outcomes it hasn’t seen?Holdout error, time-series cross-validation
Does its channel story support a causal decision?Experiments, stability, plausibility, sensitivity to assumptions

You can pass the first and fail the second (overfitting), and you can pass both and still fail the third. Two channels that always run together can swap credit between them with almost no change in predictive accuracy, so the model predicts sales well using the wrong allocation of credit. That’s why no single number validates a marketing mix model, and why Google’s Meridian documentation says outright that out-of-sample prediction error shouldn’t be the primary way model fit is assessed, since the goal of MMM is causal inference rather than forecasting.

In-sample fit: R², MAPE and NRMSE

The three fit statistics you’ll meet most often all measure how far predictions are from actuals, scaled differently. R² (r squared, the coefficient of determination) is the share of variance in sales the model explains, one minus the sum of squared errors divided by the total sum of squares around the mean. MAPE (mean absolute percentage error) is the average of the absolute errors as a percentage of actual sales, which makes it easy to explain (“we’re off by 4 percent in a typical week”) and is covered in more depth in the MAPE brief. NRMSE (normalized root mean squared error) is the RMSE divided by the range of the outcome, and it’s the error Robyn minimizes:

NRMSE=1n∑t(yt−y^t)2max⁡(y)−min⁡(y)\text{NRMSE} = \frac{\sqrt{\tfrac{1}{n}\sum_t (y_t - \hat{y}_t)^2}}{\max(y) - \min(y)}

All three have the same weakness in-sample: you can improve them by adding variables. Every extra control, dummy or knot can only lower the training error, which is the kitchen-sink model problem. Adjusted R² penalizes the parameter count a little, but the honest fix is to measure error on data the model didn’t see. MAPE has an extra quirk worth knowing: it divides by actual sales, so weeks with very low sales get enormous percentage errors, and a week with zero sales breaks it entirely. Meridian reports wMAPE (weighted MAPE, total absolute error divided by total actuals) alongside R² and MAPE, which sidesteps that problem.

Holdout validation and out-of-sample error

Holdout validation means setting some observations aside, fitting the model on the rest, and measuring error only on the ones it never saw. The gap between training error and holdout error tells you how much of the model’s fit is signal and how much is memorized noise. The Vexpower course uses a deliberately simple demo for this: 32 invented observations along an S-shaped response curve, with every fourth one held out, and a polynomial whose flexibility you control.

Interactive
Give the curve more freedom and watch the holdout error

Pick a polynomial degree. The curve is fitted to the 24 training points only; the eight holdout points never enter the fit. Training error always falls as the degree rises. Holdout error doesn't.

Answer key
Training RMSE
-
Holdout RMSE
-
Holdout ÷ training
-
Training R²
-

Download the holdout CSV
Data: holdout-model-comparison-synthetic.csv from the Vexpower MMM course (32 invented observations, every fourth one held out). Fit: ordinary least squares on polynomial features of spend scaled to [-1, 1]. Degrees 3 and 14 reproduce the file's restrained_prediction and flexible_prediction columns. Curve values outside the chart are clipped for display. A teaching fit, not an MMM.

Drag the degree from 3 up to 14 and the training RMSE falls steadily, from about 6.6 to about 3.3, because every extra degree of freedom lets the curve pass closer to the training points. The holdout RMSE does the opposite: it sits between about 14.5 and 15.7 from degree 3 to degree 9, then climbs past 20 from degree 10, and reaches about 37.6 at degree 14, where the curve swings wildly between the points it was fitted to. A team choosing the model with the best fit would choose the worst one. In a real marketing mix model the equivalent of “more degrees” is more channels split out, more flexible adstock and saturation, more seasonality terms and more dummies, and the same trade-off applies (the bias-variance trade-off brief covers the theory).

Two cautions come with this demo. First, eight holdout points is a small sample: degrees 3 to 6 differ by a fraction of an RMSE unit, which is noise, not a ranking. Second, the holdout set is only an honest test while you don’t use it to make choices. If you try twenty model specifications and keep the one with the best holdout error, the holdout has become part of the fitting process, and you need another untouched set to report. The demo’s data are observations along a curve, not a time series, so they also skip the question of which weeks to hold out, which matters a lot for MMM.

csv
Holdout model comparison datasetThe 32 observations above, with split labels, the known signal (an answer key, not a fitting input) and the degree-3 and degree-14 predictions. Compute RMSE per split yourself.
Download

Time-series cross-validation with a rolling origin

Holding out random weeks from a weekly sales series leaks information, because adstock and trend mean each week’s neighbors tell you a lot about it. The more common approach for time series is to hold out the end of the series, train on everything before it, and test on the weeks after. Rolling-origin cross-validation repeats that several times: train on weeks 1-60 and test on 61-72, then train on 1-72 and test on 73-84, and so on, so you get several out-of-sample errors instead of one, and you can see whether the model’s accuracy is stable or depends on one lucky period.

Fold 1Fold 2Fold 3Fold 4week 1week 104traintest
Rolling-origin time-series cross-validation: each fold trains on everything up to a cut-off and tests on the block that follows, so the model is always predicting forward. Source: Vexpower.

The libraries take different positions here, which is worth knowing before you compare their numbers. Robyn’s time-series validation splits the series into consecutive train, validation and test slices (with train_size = 0.7, the other two get 15 percent each), optimizes the NRMSE on the validation slice and reports the test-slice NRMSE as the out-of-sample check. Meridian’s guidance goes the other way on large contiguous blocks: its trend and seasonality use knots that need nearby data, so a long held-out block at the end leaves those knots driven by the prior and produces poor forecasts that say more about the time effects than the media effects. It recommends a roughly balanced holdout across geos and time periods instead. Neither is wrong, they’re answering slightly different questions, but it does mean a “holdout MAPE” from one tool isn’t comparable with one from another.

DECOMP.RSSD and Robyn’s business error

Robyn adds a second objective alongside NRMSE that isn’t about prediction at all. DECOMP.RSSD (decomposition root sum of squared distance) measures how far the model’s split of credit is from the split of spend, across the paid channels:

DECOMP.RSSD=∑i(effect sharei−spend sharei)2\text{DECOMP.RSSD} = \sqrt{\sum_{i} \left(\text{effect share}_i - \text{spend share}_i\right)^2}

If search gets 50 percent of spend but the model gives it 80 percent of the media effect, that channel adds 0.09 to the sum inside the square root. Robyn’s code also multiplies the result by one plus the share of channels with zero effect by default, to penalize models that switch channels off. Robyn then shows you a Pareto front of models that trade off NRMSE against DECOMP.RSSD, and you choose among them (the Robyn page has a Pareto-front explorer).

I wrote about this metric for Recast under the title “Decomp RSSD: Optimizing for politics”, and that’s the honest way to read it. It rules out decompositions so extreme that nobody in the business would accept them, which has real value, because a model that says one channel did everything will be ignored however well it fits. However, it’s a plausibility constraint, not validation. It pulls every model toward the status quo spend split, so if your current budget is badly wrong, DECOMP.RSSD is quietly pushing the model to agree with the mistake. Use it to filter out absurd models, not as evidence the remaining ones are right.

Parameter stability and refresh stability

A model whose channel ROIs change dramatically when you drop a few weeks, change a random seed or add a month of new data isn’t telling you much about those channels. Stability checks are cheap and revealing. Refit on the data minus the last eight weeks, or minus a different quarter each time, and compare each channel’s ROI and adstock rate across the fits. Rerun a Bayesian model with a different seed and check the chains still agree. For Robyn, look at how much the ROIs vary across the models on the Pareto front: if equally good models disagree by a factor of three on a channel, the data haven’t identified that channel.

Refresh stability is the same idea over time. When you refresh a model monthly or quarterly, the new data should move estimates by roughly the amount the new information justifies. If TV’s ROI goes from 0.8 to 2.4 between two refreshes with nothing different in the TV plan, the change is coming from the model, not the market, and every budget decision made on the earlier number was built on sand. I’d track each channel’s estimate across refreshes on a simple line chart and treat large unexplained jumps as a bug to investigate, often a multicollinearity problem or a weakly informed prior in a Bayesian MMM.

Experiment agreement is the strongest validation you can get

Everything above checks the model against itself or its own training data. The only check against the truth is an experiment. If a geo-lift test or a conversion lift study measured a channel’s incremental return, the model’s estimate for that channel and period should be in the same neighborhood, with overlapping intervals. When they disagree, the disagreement is information: either the model is misattributing (often to a correlated channel or an omitted confounder), or the experiment measured a different quantity (a different period, region, budget level or counterfactual).

There’s one rule to keep this honest: an experiment can be used to calibrate the model or to validate it, but not both. If a lift test became a prior in Meridian or a calibration input in Robyn, the model agreeing with it later isn’t independent confirmation, it’s the model repeating what you told it. Keep an evidence register of which tests went in, and save at least one for checking. If you haven’t run any yet, the incrementality testing guide is the place to start.

Calculate MAPE and NRMSE for your own model

If you have a column of actual weekly sales and a column of model predictions (ideally for holdout weeks), paste them in to get the main error metrics side by side. The bias figure is worth more attention than it usually gets: a model that’s off by 5 percent at random is very different from one that’s 5 percent too high every week.

Calculator
MAPE and NRMSE calculator

Paste two columns from a spreadsheet: actual, then predicted (one row per week, separated by a tab, comma or space). Use your holdout weeks if you have them.

Rows used
-
MAPE
-
wMAPE
-
RMSE
-
NRMSE
-
R²
-
Mean error (bias)
-

MAPE = mean of |actual − predicted| ÷ actual. wMAPE = sum of absolute errors ÷ sum of actuals. NRMSE = RMSE ÷ (max − min of actuals), the definition Robyn uses. R² = 1 − SSE ÷ SST. Rows where actual is zero are skipped for MAPE only. Your data stays in your browser.

An MMM validation checklist to run before you trust the model

The course’s lesson ends with a question to ask at work: what was held out, what influenced model selection, and what evidence supports the causal interpretation beyond fit? Here’s that question turned into a checklist. If you want to see the residual side of validation, the companion page on MMM model diagnostics covers reading residual plots, autocorrelation and structural breaks, and the marketing mix modeling overview puts validation in the context of the full modeling process.

None of these checks proves a model is right; together they make it much harder for a wrong one to survive. The course works through this with the invented Northstar brand in its marketing mix modeling path, where a beautifully fitted line turns out to be the start of the review rather than the end of it.

Downloads

csv
Holdout model comparison dataset (CSV)32 invented observations with a train/holdout split, the known signal, and degree-3 and degree-14 predictions.
Download

Frequently asked questions

How do you validate a marketing mix model?

Check three things separately. Predictive accuracy on data the model never saw (a holdout or rolling time-series cross-validation), stability of the channel estimates when you refit on slightly different data or refresh the model, and agreement between the model's channel effects and independent experiments such as geo-lift or conversion lift tests. A good in-sample fit is only the first of these.

What is a good MAPE for a marketing mix model?

There is no universal threshold, because MAPE depends on how noisy your sales are and whether you measure it in-sample or on a holdout. What matters more is the holdout MAPE compared with the training MAPE, and whether the errors are random rather than concentrated in promotions or particular seasons. A low MAPE also says nothing about whether the channel contributions are causally right.

What is NRMSE in Robyn?

NRMSE is normalized root mean squared error: the RMSE of the model's predictions divided by the range (maximum minus minimum) of the actual outcome. Robyn calls it the statistical error and minimizes it alongside DECOMP.RSSD. With time-series validation switched on, Robyn optimizes the NRMSE on a validation slice and reports NRMSE on a final test slice.

What is DECOMP.RSSD?

DECOMP.RSSD, decomposition root sum of squared distance, is Robyn's business error. For each paid channel it takes the difference between the channel's share of modeled effect and its share of spend, squares it, sums across channels and takes the square root. Lower values mean the model's credit split looks more like the spend split, which rules out extreme decompositions but is not evidence of causal accuracy.

What is the difference between in-sample and out-of-sample validation?

In-sample validation measures how well the model fits the data it was trained on. Out-of-sample validation measures error on observations the model did not see during fitting, such as a held-out set of weeks. A model can always improve in-sample fit by adding flexibility, so only out-of-sample error tells you whether that flexibility is capturing signal or noise.

References

  1. Robyn source code: model.R (NRMSE and DECOMP.RSSD calculations) nrmse = RMSE / (max − min); decomp.rssd = sqrt(sum((effect_share − spend_share)²)), with an optional zero-effect penalty.
  2. Robyn documentation: features NRMSE as prediction error, DECOMP.RSSD as business error, time-series validation split.
  3. Runge, Skokan, Zhou and Pauwels, Packaging Up Media Mix Modeling: An Introduction to Robyn's Open-Source Approach Multi-objective optimisation across NRMSE, DECOMP.RSSD and MAPE.LIFT.
  4. Mike Taylor, Decomp RSSD: Optimizing for politics (Recast blog)
  5. Google Meridian: holdout observations Holdout specs; advice against large contiguous holdout blocks because of trend knots.
  6. Google Meridian: assess model fit R-squared, MAPE and wMAPE from predictive_accuracy; causal inference as the primary goal.
Mike Taylor
Mike Taylor

I've built marketing mix models for Monzo Bank, Booking.com, Time Out, The AA and others, advised Google, Appsflyer and Recast on MMM, and teach marketing mix modeling on LinkedIn Learning. Before that I co-founded a 50-person growth marketing agency. I write the Vexpower MMM course.

Keep reading

Learn MMM by doing itThe Vexpower course walks through thirty lessons on one invented brand, with simulators, Python labs and a decision case you can defend.
See the course