learn-marketing-attribution-with-phoebe / Builder session 6 of 10
Learn Marketing Attribution with Phoebe · Builder session 6 of 10

ML and deep-learning attribution: let a model split the credit

You built Shapley from scratch last session. Now you let a trained model do the heavy lifting: fit a gradient-boosted classifier that predicts whether a journey converts, then read per-channel credit off it with SHAP - which is the same Shapley math you already coded, applied to the model's features. Then we look up the ladder at the deep sequence models everyone name-drops (LSTM + attention, DNAMTA, DeepMTA) and get honest about when they earn their keep and when they just overfit your sparse conversions.

🟠 Advanced Builders · DS / ML engineers LightGBM + SHAP 45 min
0-4 · The ML ladder 4-20 · GBM + SHAP 20-33 · Deep sequence models 33-45 · Build + tradeoff
Part 0

The ML ladder for attribution

There is a ladder of sophistication for data-driven attribution, and most teams climb one rung too high. Rung one is heuristics (B2). Rung two is a learned credit method - Markov (B4), exact Shapley (B5). Rung three is the one this session lives on: train a machine-learning model to predict conversion, then attribute credit by asking how much each channel-feature moved that prediction. Rung four is deep sequence models. The trap is thinking a taller ladder is always a better ladder. It is not. The right rung is the one your data volume can hold your weight on.

Live - coded in session Self-study - read after class ★ Build-along - everyone runs it The data: Lumen Skincare
★ What you ship today A LightGBM conversion model trained on Lumen's featurized journeys, a SHAP-derived channel attribution you can defend line by line, and a clear-eyed rule for when to reach past gradient boosting for an LSTM - and when reaching for it would quietly overfit you into a worse answer.
Part 1 · the production path

Gradient boosting + SHAP: the pragmatic default 6 min live

Here is the workflow that quietly runs in most serious attribution stacks. You do not model the sequence at all - you featurize each journey (which channels touched it, how often), train a tree ensemble to predict conversion, and then read credit with SHAP. The important thing to internalize: SHAP is not a different idea from Session 5. SHAP is Shapley. It computes each feature's average marginal contribution to a specific prediction - the exact same coalition math you coded by hand - but over a trained model's features instead of over channel presence directly. That is what makes it production-friendly: the model handles interactions and non-linearity, and SHAP hands you back interpretable per-channel credit.

Journeys raw touchpoints per customer Feature matrix channel counts + presence flags LightGBM predict P(convert) trees + boosting SHAP credit per-channel share of the prediction SHAP = the Shapley math from B5, applied to the trained model's features. Same axioms, same "average marginal contribution". The model absorbs interactions and non-linearity for you; SHAP translates its logic back into credit a marketer can read. No sequence modelling here - order is dropped. That is the tradeoff you accept for robustness on modest data.
🔍 Click to zoom - the production path: featurize, boost, then read credit with SHAP (Shapley on the model)
LiveWhy gradient boosting is the right default3 min

Tree ensembles - LightGBM, XGBoost - are the workhorse of applied attribution for reasons that have nothing to do with fashion:

  • They handle interactions natively. "Paid social only converts when email also touched" is exactly the kind of split a boosted tree finds without you engineering the interaction term. That is the synergy Shapley cares about, learned for free.
  • They tolerate modest, messy, tabular data. Thousands of journeys is plenty. No sequence padding, no embedding layers, no GPU.
  • SHAP closes the interpretability gap. The classic objection to ML attribution is "it's a black box". SHAP answers it: every prediction decomposes into additive per-feature contributions that sum back to the model output. You can defend each channel's number.
Real world

A retail team replaced their linear-attribution report with LightGBM + SHAP and found paid social's credit nearly doubled - not because they changed the rule by hand, but because the model saw that social touches consistently preceded the conversions that email later closed. The heuristic could never see that; the tree split on it in the first few rounds.

Self-studyMean absolute SHAP as a channel weight3 min read

SHAP gives you a contribution value for every feature on every row. To turn that into a single channel-level attribution, you take the mean of the absolute SHAP values per channel across all journeys, then normalize to 100%. Absolute, because a channel that reliably pushes the prediction (up or down) is an informative channel - you want its total influence, not its net sign. Normalizing lets you compare directly against your Markov and Shapley numbers from B4 and B5 on the same Lumen data.

StepWhat it produces
Train LightGBM on featurized journeysA conversion probability model
TreeExplainer over the eval setPer-row, per-channel SHAP contributions
Mean absolute SHAP per channelEach channel's total influence
Normalize to sum 100%A comparable attribution split
Part 2 · up the ladder

Deep sequence models: LSTM + attention 6 min live

Gradient boosting throws away order. Deep sequence models keep it - and that is their whole pitch. Two papers define the space you will hear cited. DNAMTA (Deep Neural net with Attention for MTA) runs an LSTM over the touchpoint sequence and adds an attention mechanism whose weights become the per-touch credit; it also folds in user-context control variables - demographics and behaviour - to reduce the estimation bias of the media effects. DeepMTA uses a Phased-LSTM for conversion prediction (an extra time gate that copes with the irregular gaps between touches) and then an additive feature-attribution layer built on Shapley values for interpretability. Notice the pattern: deep model for prediction, Shapley for credit - the same division of labour as Part 1, just with a fancier predictor.

Attention weight = learned per-touch credit 0.08 0.31 0.19 0.14 0.28 Display P.social Email Organic P.search LSTM sequence memory - carries long-range dependency left to right → + user-context vars (reduce media-effect bias) → P(convert) + per-touch credit DNAMTA: LSTM + attention weights as credit. DeepMTA: Phased-LSTM (time gate) + a Shapley attribution layer. The LSTM captures order and long gaps that gradient boosting cannot - if, and only if, you have the conversion volume to train it.
🔍 Click to zoom - an LSTM reads the touch sequence; attention weights above each touch are the credit it assigns
LiveWhat attention actually buys you3 min

Attention is the part worth understanding, because it is where the credit comes from. An attention layer learns a weight for every touch in the sequence - how much the model should "look at" that touch when predicting the outcome. Those weights are interpretable by construction: a high attention weight on the paid-social touch means the model leaned on it to make the call. So the attribution falls straight out of the model instead of being bolted on afterward.

  • DNAMTA adds control variables (user demographics, prior behaviour) so the media effect is not confounded by "this was always a high-intent user anyway" - a genuine bias reduction, not just accuracy chasing.
  • DeepMTA's Phased-LSTM adds a learnable time gate, so a touch three days ago and a touch three minutes ago are handled differently - the irregular spacing of real journeys becomes a signal instead of noise.
  • Both lean on Shapley or attention for the credit read-out. The interpretability problem never went away; it just got a better predictor in front of it.
Self-studyCAMTA and the causal wrinkle2 min read

CAMTA is a causal attention variant that adds propensity-style de-biasing on top of the attention mechanism - an attempt to move the credit from "correlates with conversion" toward "caused conversion". It is the same instinct we chase properly with incrementality in B9. Keep it filed under "interesting, and still not a randomized experiment": no observational sequence model, however causal its framing, replaces a real holdout. It sharpens the correlational story; it does not turn it into proof.

Part 3 · the honest tradeoff

When deep learning is worth it - and when it overfits 4 min live

This is the part the papers under-sell and production teams learn the hard way. LSTMs are hungry. They need volume - tens of thousands of labelled conversions, not thousands - to learn sequence structure without memorizing noise. Most brands, Lumen included, do not have that. Overfitting a deep model on sparse conversion data is one of the top pitfalls in this whole field: the validation curve looks fine, the attribution looks precise, and it is precisely wrong, because the model fit patterns that will not repeat. The default is gradient boosting + SHAP. You reach for deep sequence models only when scale genuinely warrants it.

few conversions high volume + scale Default: LightGBM + SHAP robust on modest data, interactions for free, defensible credit At scale: LSTM + attention order + long gaps modelled, DNAMTA / DeepMTA territory danger zone: deep model, sparse data Match the model to the data you have, not the model on the conference slide.
🔍 Click to zoom - climb the ladder only as far as your conversion volume can hold you
LiveA rule of thumb you can defend2 min

When someone proposes an LSTM for attribution, ask three questions before you write any Keras:

QuestionIf the answer is "no"
Do we have tens of thousands of conversions?Stay on LightGBM + SHAP.
Does touch order plausibly change the outcome here?A bag-of-channels model loses nothing - stay on trees.
Can we hold out a validation window and watch for overfit?Do not deploy a deep model you cannot audit.
The builder's discipline Precision is not accuracy. A deep model that reports crisp per-touch credit on 3,000 conversions is giving you confident nonsense. The most senior move in the room is often "we don't have the data for that yet" - and then shipping the boosted model that actually generalizes.
Build-along 1 of 3

Featurize Lumen journeys + train LightGBM ★ 12 min · everyone

We collapse Lumen's touchpoint log into one row per journey - a channel-count matrix - and train a gradient-boosted classifier to predict whether that journey converted. Trees do not need the sequence; they need the features.

Python · train the conversion model import pandas as pd import lightgbm as lgb from sklearn.model_selection import train_test_split from sklearn.metrics import roc_auc_score # one row per customer: how many times each of the 9 channels touched them tp = pd.read_sql("SELECT customer_id, channel FROM touchpoints", con) X = (tp.assign(n=1) .pivot_table(index="customer_id", columns="channel", values="n", aggfunc="sum", fill_value=0)) conv = pd.read_sql("SELECT DISTINCT customer_id FROM conversions", con) y = X.index.to_series().isin(conv["customer_id"]).astype(int) X_tr, X_te, y_tr, y_te = train_test_split( X, y, test_size=0.25, stratify=y, random_state=42) model = lgb.LGBMClassifier( n_estimators=400, learning_rate=0.05, num_leaves=31, subsample=0.8, random_state=42) model.fit(X_tr, y_tr) proba = model.predict_proba(X_te)[:, 1] print("val AUC:", round(roc_auc_score(y_te, proba), 3)) # ~0.79 on Lumen

Featurize, do not sequence. The pivot gives one feature per channel - the count of touches. That is deliberately a bag-of-channels view; order is dropped and the tree still finds the interactions that matter.

Stratify the split. Conversions are the minority class. stratify=y keeps the same conversion rate in train and test so your AUC is honest.

Read AUC, not accuracy. With imbalanced conversions, accuracy is a liar - a model predicting "never converts" scores high. ROC AUC asks whether converters get higher scores than non-converters, which is what you actually care about.

Real world

Adding two cheap features to the count matrix - total touch count and journey length in days - usually lifts AUC a few points and gives SHAP something richer to attribute. Resist the urge to add fifty features on a few thousand conversions, though; that is the same overfitting trap as the LSTM, just wearing a tree costume.

Build-along 2 of 3

SHAP values → channel attribution ★ 11 min · everyone

Now the payoff: turn the trained model into a channel attribution. We run TreeExplainer, take the mean absolute SHAP value per channel, and normalize - a credit split you can lay next to your Markov and Shapley numbers from B4 and B5.

Python · SHAP as attribution import shap import numpy as np # SHAP = Shapley values (B5) computed over the trained model's features explainer = shap.TreeExplainer(model) sv = explainer.shap_values(X_te) # per-row, per-channel contributions # a channel's total influence = mean of its absolute SHAP across journeys mean_abs = np.abs(sv).mean(axis=0) credit = pd.Series(mean_abs, index=X.columns) credit = (credit / credit.sum() * 100).round(1) # normalize to 100% print(credit.sort_values(ascending=False)) # paid_social 27.9 # email 21.4 # paid_search 18.6 # display 12.1 # organic_search 9.8 # influencer 6.0 ...

TreeExplainer is exact and fast for trees. It computes Shapley values analytically over the ensemble - no Monte-Carlo sampling like the general case in B5. Same axioms, far cheaper.

Absolute, then mean, then normalize. Absolute value captures total influence regardless of direction; the mean aggregates across journeys; normalizing makes it a comparable 100% split.

Compare, do not just admire. Put this next to your B4 Markov split and B5 exact-Shapley split on the same Lumen data. Where they agree, you gain confidence. Where they diverge, you have found something worth investigating - usually a channel with strong interactions.

One SHAP gotcha to remember For a binary LightGBM model, some SHAP versions return contributions for the positive class directly and others return a two-element list. Always check the shape of what you got back before you take the mean - a silent axis mix-up will hand you a confident, wrong attribution.
Build-along 3 of 3

Read the tradeoff: GBM+SHAP vs an LSTM at Lumen's size ★ 8 min · everyone

Before anyone proposes a deep model, we count. Lumen's conversion volume decides the honest answer - and this tiny check is the most valuable code in the session.

Python · does the data justify a deep model? n_conversions = int(y.sum()) n_journeys = len(y) print(f"Lumen: {n_conversions:,} conversions across {n_journeys:,} journeys") # rough field heuristic: LSTMs need tens of thousands of positives THRESHOLD = 50_000 if n_conversions < THRESHOLD: print("-> stay on LightGBM + SHAP (a deep LSTM will overfit here)") else: print("-> a DNAMTA-style LSTM + attention may now earn its keep") # Lumen prints ~9,400 conversions -> well under threshold -> trees win

Lumen has roughly 9,400 conversions. That is a healthy tabular dataset and a starvation diet for an LSTM. The check returns "stay on trees" - and it is right.

The threshold is a guide, not a law. Order-sensitivity, sequence length, and how much your context features help all move the line. But being an order of magnitude under it, as Lumen is, settles the question.

Write the recommendation down. "LightGBM + SHAP is our production attribution model; revisit deep sequence models if monthly conversions pass ~50k." That one sentence saves a quarter of GPU-shaped disappointment.

Real world

A team ran the LSTM anyway because it demoed beautifully in a notebook. In production its per-touch credit swung wildly week to week - classic overfit variance on sparse conversions - while the boosted model's SHAP attribution stayed stable. They quietly rolled back to trees and kept the LSTM as a slide. Ship the model that generalizes.

Before Builder Session 7

This week ◐ 45 min total

Check yourself

Three questions before you go 🎯 ◐ 90 seconds

1 · What is SHAP, in one line?

SHAP is not a new idea. It is the same average-marginal-contribution Shapley math you coded by hand, read off a trained model's features - which is what makes it the production interpretability layer.

2 · How does DNAMTA produce per-touch credit?

DNAMTA runs an LSTM over the sequence and uses attention weights as the credit, plus user-context control variables to reduce media-effect bias. DeepMTA does the deep-prediction-then-Shapley variant.

3 · You have ~9,000 conversions and want per-touch attribution. Best move?

LSTMs need tens of thousands of conversions or they memorize noise and report confident, unstable credit. At Lumen's scale, gradient boosting + SHAP is both the safe and the correct default.

Source material

What this session covers

This session bridges applied ML interpretability and the deep-MTA literature into a builder-first workflow: the production GBM + SHAP path, and an honest read of the deep sequence models. Papers stay with their authors; we teach the method and the judgement.

LightGBM + SHAP for tabular attributionthe production path - Part 1, Build-alongs 1-2
DNAMTA - Deep Neural net with Attention for MTA (arxiv 1809.02230)LSTM + attention as per-touch credit - Part 2
DeepMTA - Phased-LSTM + Shapley attribution (arxiv 2004.00384)deep prediction, Shapley read-out - Part 2
CAMTA - causal attention variantde-biasing idea, named in self-study - Part 2
True causal lift (holdout / geo experiments)the real answer to confounding - Builder Session 9

Builder Session 6 cheat sheet · pin this

Production pathFeaturize journeys → LightGBM predicts conversion → SHAP reads per-channel credit. Robust, defensible, no GPU.
SHAP = ShapleyThe same B5 math, applied to a trained model's features. Mean absolute SHAP per channel, normalized to 100%.
DNAMTALSTM over the touch sequence + attention weights as per-touch credit; user-context vars reduce media-effect bias.
DeepMTAPhased-LSTM (time gate for irregular gaps) predicts, then an additive Shapley layer gives interpretable credit.
The overfit trapLSTMs need tens of thousands of conversions. On sparse data they report confident, unstable credit. Default to GBM+SHAP.
Match model to dataClimb the ladder only as far as your conversion volume holds. Lumen (~9.4k) → trees. Reach for LSTM only at scale.