search_ads_recsys / 21 · explainability lesson 21 / 39

Explainable recommendation

A ranker that returns the right item but cannot say why is a black box your users distrust, your engineers cannot debug, and your regulators will not accept. The trick is to keep two questions apart — "is the model transparent?" and "can we attach an honest reason to this card?" — and to remember that an explanation is a claim about the model that most cheap methods fail to keep.

Why an explanation earns its latency

Explanations are not free — they cost compute, screen space, engineering time, and they can leak private signal (lesson 22 · Privacy). So before any method, name who the explanation is for, because the audience decides what "good" means. There are four, and they pull in different directions.

AudienceWhy they need itWhat "good" means
User — trust & persuasion "Because you watched X" lowers the friction of accepting an unfamiliar item; trust converts to clicks. Industrial A/B tests routinely report +5–8% CTR on cards that carry a reason, with measurable lifts in dwell and retention. One short, true, human sentence — a watched item, a followed creator, a topic tag.
Developer — debugging When the feed suddenly serves clickbait, an attribution tells you which feature drove it — a distribution shift, a leaked label, a runaway popularity prior. A ranked per-prediction feature breakdown, comparable across model versions.
Regulator — compliance Algorithm-transparency law (China's algorithmic-recommendation rules, the EU's DSA and GDPR "right to explanation") increasingly requires a stated logic plus an opt-out of personalization. A reproducible, auditable account of the decision logic — defensible, not necessarily user-facing.
Auditor — fairness To check whether a protected attribute or its proxy is driving exposure, you must attribute the score to features. Explainability is the instrument fairness work reads from (lesson 20 · Fairness). Global attributions across slices, stable enough to compare groups.

The interviewer who asks "why would you add explanations?" wants to hear that these are four jobs, not one. A method that is fine for a recommendation card ("people who liked this also liked…") can be malpractice in a fairness audit.

The one distinction that dissolves most confusion
"Explainable" is two properties wearing one word. Transparency is whether a human can follow the model's actual computation. Justification is whether you can show the user a plausible reason for this result. They are not the same: a transparent linear model can still be displayed with a misleading reason, and an opaque deep net can be paired with a faithful post-hoc attribution. Keep the two columns apart and ask, every time, which one you owe.

The master axis: intrinsic vs post-hoc

Every method sits on one side of a single axis. Either the model is transparent by construction (intrinsic, glass-box — the model is the explanation), or it is a black box you explain after the fact (post-hoc — an opaque model plus a separate explainer).

INTRINSIC · glass-box POST-HOC · explain a black box model IS the explanation linear weights · decision tree · attention · KG path opaque model + separate explainer SHAP · LIME · surrogate tree · counterfactual + faithful by construction (reason = computation) + cheap to read, no extra pass − capacity ceiling on the model class use: cold start, high-stakes, regulated + model stays state-of-the-art accurate + explainer swappable, model-agnostic − approximation: may be unfaithful − extra compute per explanation

The choice is a business decision, not an aesthetic one. Pick intrinsic when the decision is high-stakes or regulated, when you are in cold start (lesson 15 · Cold start) and a simple rule is all the data supports anyway, or when the audit requirement is hard. Pick post-hoc when you have already paid for a deep ranker (lesson 04 · Ranking models) and cannot give up its accuracy — you bolt an explainer on and accept that the explanation is an approximation.

Intrinsic methods — when the model is the reason

Linear models and decision trees

A logistic CTR model p = σ(w·x + b) is self-explaining: feature i's contribution to the logit is exactly wᵢ xᵢ, and sorting those gives a per-prediction breakdown that is literally the computation (lesson 04 · Ranking models traces why we left LR behind). A decision tree hands you a root-to-leaf rule — "watch-time > 30s AND topic = cooking → recommend." In a regulated slice or for a brand-new user with three data points, that honesty is worth more than a deep model's last point of AUC.

Attention weights as explanations — and the honest caveat

Behavior-sequence rankers like DIN/DIEN (lesson 13 · User-behavior sequences) attend over the user's history: each past item hⱼ gets a weight αⱼ = softmax( a(hⱼ, cand) ) measuring its relevance to the candidate. The natural story is "we recommend this because of the high-attention items," visualized as a heat-strip over the history. It is the most popular explanation in industry — and the most over-claimed.

User history → attention over the candidate "ramen recipe video" cooking vlogα = 0.41 noodle ASMRα = 0.27 gym workoutα = 0.08 car reviewα = 0.06 street foodα = 0.18 Story shown to the user: "Because you watch a lot of cooking content." ← plausible, persuasive… …but α is a relevance weight inside one layer, not a proof that this item caused the score.
Attention ≠ causal importance
Attention weights are seductive but not guaranteed faithful. Three reasons: (1) the value vector a low-attention item carries can still dominate the output; (2) in multi-head, multi-layer nets the "explanation" is one slice of a deep computation, and you can often find a different attention distribution that yields the same prediction (Jain & Wallace 2019, "Attention is not Explanation"); (3) attention answers "what did the model look at," not "what would change the answer." Fine as a user-facing justification. Never present it in a fairness audit as proof of what drove the decision — verify with a counterfactual below.

Knowledge-graph path explanations

The most satisfying intrinsic explanations come from models that reason over a knowledge graph of entities and relations (users, items, attributes, creators, topics). The recommendation is a path, and the path is the explanation — fully transparent and naturally human-readable.

Youuser SpiritedAwaywatched Miyazakidirector MyNeighborrecommended watched directed_by directed Reason read straight off the path: "Because you watched Spirited Away, also directed by Miyazaki."

Path-based recommenders (RippleNet, KPRN, PGPR) score a candidate by the strength of the connecting paths and surface the strongest one as the reason — faithful and readable in one object. The cost: you must build and maintain the graph, paths explode combinatorially, and graph coverage becomes the new bottleneck. But for a cold-start item with no interaction history, a semantic path ("recommended because you follow a science creator") is often the only reason you can honestly give.

Where KG paths break — readability has a hop limit
A path is readable at 2–3 hops ("you watched Spirited Away → directed by Miyazaki → directed My Neighbor Totoro"). At 5+ hops ("you watched X → genre fantasy → also liked by user cluster 7 → cluster watches studio Y → studio Y made Z") it stops being an explanation and becomes a proof no human will read. Two practical rules: when several valid paths exist, surface the shortest one, not the highest-scoring one — readability beats marginal relevance for a user-facing reason; and when the only connecting paths are long, fall back to a simpler explanation type ("popular with people who share your interests") rather than show a 6-hop chain. KG paths shine precisely where interaction history is sparse and the graph link is short; they degrade exactly where a warm item already has a dozen tangled paths.

Post-hoc methods — explaining a black box

You kept the deep ranker for its accuracy; now attach an explainer. Every post-hoc method shares one trick: probe the model by perturbing inputs and watch the output move.

LIME — a local linear surrogate

To explain one prediction, LIME samples perturbed versions of that input, gets the black box's prediction on each, and fits a tiny linear model in the neighborhood, weighting samples by closeness to the original. The surrogate's coefficients are the local feature importances. Model-agnostic and intuitive, but only as stable as the sampling — re-run it and the top features can wobble.

SHAP — fair credit from game theory

SHAP assigns each feature its Shapley value: the feature's average marginal contribution to the prediction, taken over all orderings in which features could be "added." It is the unique attribution satisfying three sane axioms — efficiency (contributions sum to prediction minus baseline), symmetry, and a dummy/null property:

φᵢ = ΣS ⊆ F∖{i}   ( |S|! · (|F|−|S|−1)! / |F|! )   ·   [ f(S ∪ {i}) − f(S) ]     with   f(x) = φ₀ + Σᵢ φᵢ

Read it as: feature i's credit φᵢ is its marginal lift f(S∪{i}) − f(S), averaged over every coalition S of the other features. The exact sum is exponential in the feature count, so in practice you approximate — KernelSHAP (a weighted LIME-style regression) or, for tree ensembles, the exact-and-fast TreeSHAP in O(TLD²). The payoff: contributions that add up to the score, which is exactly what a developer breakdown and a fairness slice both want.

Worked numbers — SHAP on one CTR prediction
A logistic ranker predicts p = 0.72 for a video. The baseline (average prediction over the dataset) is φ₀ = 0.50. SHAP attributes the 0.22 gap to the features that fired:
Featureφᵢ (contribution to p)
baseline φ₀0.50
watched cooking before+0.15
trending now+0.12
geo = USA+0.05
no recent history−0.10
Σ → prediction0.72
Two things this buys you that raw feature weights can't. (1) Efficiency: the parts sum exactly to the prediction (0.50 + 0.15 + 0.12 + 0.05 − 0.10 = 0.72), so "watched cooking explains +0.15 of the 0.72" is a literal, defensible statement. (2) Auditability: geo = USA contributes +0.05 — small, but a fairness auditor (lesson 20) can now see that a location proxy is moving the score and aggregate that φ across protected slices to check for disparate exposure. The widget below lets you toggle these features and watch the sum re-balance live.
MethodTypeGranularityFaithful?Cost / pitfallBest for
Linear / logistic weightsintrinsicglobal + localexactcaps model capacityregulated, cold start
Decision-tree pathintrinsiclocal ruleexactshallow trees underfitrule-like decisions
Attention heat-mapintrinsiclocalnot guaranteednot causal; can be gameduser-facing reasons
KG pathintrinsiclocalexact (path is the model)graph build & coveragecold start, semantic links
LIMEpost-hoclocalapproximateunstable across runsany black box, quick look
SHAPpost-hoclocal + aggregableapproximate, axiomaticexpensive; baseline mattersdebugging, fairness audit
Counterfactualpost-hoclocalcausal-by-constructionsearch cost; may be unrealistic"what would change this?"
Template / "because you…"presentationlocaldepends on the bound signalcan be a fake reasonthe actual UI card

Attributing the objective, not just the feature — multi-task rankers

Everything above attributes a score to features. But a production ranker is almost never one head — it is a multi-task model (lesson 12 · Multi-task learning) that predicts watch-time, like, comment, share, and a diversity term in parallel, then fuses them score = Σk wk · ŷk. The question a developer actually asks when the feed tilts is not "which feature fired?" but "which objective drove this card?" — and SHAP over input features cannot answer it, because two features can both feed the watch-time head and the diversity head at once. You need a second attribution axis: credit assigned over the heads / experts.

Two mechanisms, depending on how the model fuses objectives:

Worked numbers — decomposing one fused score
A PLE ranker fuses four heads. The raw head outputs and tuned fusion weights for one candidate:
Objective head khead output ŷkfusion weight wkcontribution wkŷkshare
watch-time0.801.00.8057%
like0.500.60.3021%
diversity term0.400.50.2014%
comment0.250.40.107%
Σ → fused score1.40100%
objective attribution (share of the 1.40 fused score) watch-time ████████████████████████████ 57% ← the real driver like ██████████ 21% diversity ███████ 14% comment ███ 7%
Read off the punchline: the card is 57% a watch-time decision, only 14% diversity. That is exactly the sentence a PM or a fairness auditor needs — and it explains a failure mode SHAP cannot. If the feed suddenly serves clickbait, feature-SHAP might say "thumbnail-saturation feature is up," but objective-attribution says the obvious thing: the watch-time head's share jumped from 40% to 70% because someone re-tuned wwatch. You debug the fusion, not the features. Pair it with a counterfactual on the weights — "drop wwatch by 10%, three comedy clips re-enter the top-K" — and you have a causal account of the objective mix.

Template and counterfactual explanations

Templates — the reason the user actually sees

Whatever runs under the hood, the card almost always renders through a template: "Because you watched X," "Popular with people you follow," "Trending in cooking." Templates are cheap, fast, and readable. The danger is the fake reason — filling the slot with a high-weight feature that wasn't the real driver, or a generic "popular near you" that has nothing to do with this prediction. A template is only as honest as the signal you bind into it, which is why you verify the binding with a counterfactual.

LLM-generated reasons — fluent, and a new way to lie

The emerging pattern (lesson 26 · LLMs & GenAI) replaces the rigid template slot with an LLM that writes the sentence: instead of "Because you watched X," it produces "Recommended because you saved 3 similar cooking tutorials this week and the creator is an expert you interact with often." More natural, more persuasive, higher click-through on the reason — and a fresh failure mode on top of the old one. A template can only show a fake reason (a real feature that wasn't the driver). An LLM can show a hallucinated reason: a fluent, plausible sentence describing behaviour that never happened ("because you love Italian cuisine" when the user has never touched a cooking video). The fluency is exactly what makes it dangerous — a hallucinated reason reads more trustworthy than a clumsy true one.

Ground the generator, then gate it — the same counterfactual, now mandatory
An LLM explanation is unsafe unless it is constrained to grounded features and verified against the model. The production recipe is a two-stage clamp:
  1. Constrain the input. Do not free-generate. Feed the LLM only the actual top-k SHAP features (or the strongest KG path) for this prediction and instruct it to paraphrase those — never to invent context. The model's job is fluency, not retrieval.
  2. Verify the output. Parse the entities the LLM named back out of the sentence and check them against the grounded set. If the sentence claims "saved 3 cooking tutorials," confirm (a) the cooking signal is in the top SHAP features and (b) the counterfactual holds — remove it and the item drops out of the top-K. Any named entity not in the grounded set is a hallucination: block the card and fall back to a deterministic template.
This is the identical consistency gate you run for templates — an unfaithful explanation is worse than none, and a fluent unfaithful one is worse still — just applied to generated text. Cost control: don't call a frontier model per request. Distill it — the heavy model generates a bank of verified explanation phrasings offline; a light model (or a cache keyed by the top-feature signature) serves them at sub-millisecond latency. Generation and verification are nearline; the card stays fast.

Counterfactuals — the most causal explanation you can cheaply give

A counterfactual answers what users and auditors actually mean: what would have to be different for the recommendation to change? "If you hadn't watched any gaming videos this week, this item would drop out of your top 10." You find it by searching for the smallest input change that flips the outcome:

x* = arg minx'   d(x, x')    subject to    rank(item | x') > K    (item leaves the top-K)

Because it intervenes on the input and observes the output, a counterfactual is causal-by-construction — it sidesteps the "attention isn't causal" trap entirely. It is also the cleanest consistency check: if a card claims "because you watched cooking videos," remove the cooking videos and confirm the item really falls. If it doesn't, your stated reason was a lie. The cost is search (you may probe the model many times) and realism (the minimal change must be one the user could plausibly have made).

A practical layered recipe
Production rarely picks one method. A common stack: keep the deep ranker for accuracy; compute SHAP / attention offline or nearline for developer dashboards and fairness slices; render a template to the user, its slot filled by the top SHAP feature or strongest KG path; and run a counterfactual spot-check in CI to verify the user-facing reasons aren't fake. The latency budget forbids per-request SHAP, so the heavy attribution is asynchronous (lesson 17 · Realtime streaming) while the card itself stays sub-millisecond.

Evaluating explanations — because an unfaithful one is worse than none

Here is the dimension most candidates miss entirely. You can pick the right method, bind a beautiful template, and still ship a disaster — because you never measured whether the explanation works. The engineering line is sharp: an explanation is a claim about the model, and an unfaithful or useless claim is not neutral, it is negative. A wrong "because you watched X" actively erodes the trust the feature was supposed to build, teaches users to ignore reasons, and — if it leaks into a fairness narrative — hands a regulator a false account. So explanations get the same treatment as any other shipped surface: a metric suite, thresholds, and an A/B with causal attribution. The book devotes a whole section to this; the lesson would be incomplete without it.

Measure on three layers, because the four audiences (top of this lesson) need different evidence.

LayerMetricWhat it asksTarget / threshold
SystemExplanation coverage% of served items that get any reasonhigh & rising; a long tail with no honest reason is a coverage gap, not a free pass to fabricate
generation latencydoes the reason fit the budget?card sub-ms; heavy attribution nearline (lesson 17)
Model / faithfulnessattribution stabilitydo the SHAP top-features agree with themselves across time windows? (Spearman ρ of the rankings)ρ > 0.7 — below this the explainer is unstable; same input, different "reason" tomorrow
fidelity–stabilitydoes the explanation track the model under small input perturbation?> 0.7 — an explanation that swings while the prediction holds is noise
decision-path coveragedo the top-3 explained paths overlap the user's actual behaviour log?≥ 85% overlap — the reason should describe what really happened
User / businessESS — explanation satisfaction"did this reason help you understand the rec?" (5-pt Likert survey)tracked over time; watch the low-activity cohort, where trust matters most
adoption & ECRdo users engage the reason, and does it lift conversion?adoption = CTR on explained items; ECR = the conversion lift from adding the reason at all
The non-negotiable guardrail — accuracy-neutrality (±1%)
Before any of the satisfaction metrics matter, one hard gate: the explanation module must not move ranking quality by more than ±1% AUC / NDCG. The logic is brutal and correct — explanations are a feature on top of the ranker, not a trade against it. If wiring in attention-logging, a glass-box constraint, or an LLM-reason path costs you 2% NDCG, you have made the product worse to make it more legible, and no satisfaction lift repays a ranking regression at that scale. Treat ±1% as a release blocker, the same way you would a latency budget. (This is also why the "accuracy vs explainability tradeoff" below is a real constraint, not a philosophy: it has a number, and the number is ±1%.)
Worked example — did the explanation actually help, or did the season?
You ship "because you watched X" reasons to a 5% experiment bucket, hold out a 5% control. After 14 days:
beforeafterΔ (this group)
treatment — CTR on explained items4.0%4.6%+0.6 pp
control — CTR, no reason shown4.0%4.2%+0.2 pp
The naive read — "explanations gave +0.6pp" — is wrong, because the whole platform drifted up +0.2pp (a seasonal bump, a UI change, anything). The honest estimate is difference-in-differences: subtract the control's trend from the treatment's to strip the common shock.

ECRcausal = ΔCTRtreat − ΔCTRcontrol = (+0.6 pp) − (+0.2 pp) = +0.4 pp

+0.6pp treatment (reason shown) │ +0.4pp ──┤ ← DiD: the explanation's true causal lift │ +0.2pp control (no reason) — the shock you must subtract
So the explanation's real causal lift is +0.4pp relative CTR, not +0.6. DiD is what lets you say "the reason caused this," not "this happened while the reason was up." Run the same DiD on ESS to attribute the satisfaction lift, and on negative-feedback rate to confirm you did not just train users to tap a novel button. And before you celebrate any of it: confirm the ±1% AUC gate held — a CTR lift bought with a ranking regression is a loss you booked as a win.

Now the obvious next question — when the experiment shows watch-time down but share-rate up (a real and common outcome), the verdict is not a single number. You build a weighted decision score across the objectives and decide explicitly, exactly the multi-objective reasoning of lesson 08 · Evaluation; the explanation framework feeds that decision, it does not bypass it.

The accuracy–explainability tradeoff (and why it's overstated)

The folk theorem: simple models are explainable but weak, powerful models are accurate but opaque, so you must trade one for the other. There is a kernel of truth — a 3-deep tree genuinely cannot match a DCN-v2 cross stack on a billion-event dataset. But the tradeoff is routinely overstated, for three reasons:

Interactive · feature-attribution composer (toy SHAP)

Toggle which signals fired for one prediction. The widget shows a SHAP-style additive breakdown — each feature's φᵢ contribution to the logit, summing to the score — picks the template slot from the top contributor, and runs a counterfactual consistency check on the reason it would show. The numbers are toy; the failure modes are the point.

Attribute one recommendation
All toggles default to "on." Turn signals off and watch the attribution re-sum and the user-facing reason change. Watch what happens when the template's bound feature is no longer the true driver — that's the fake-reason trap.
top contributor (SHAP)
predicted logit (φ₀ + Σφᵢ)
counterfactual check
verdict
Reading

Interview prompts you should be ready for

  1. "What's the difference between transparency and justification?" (Transparency = a human can follow the model's actual computation; justification = you can show the user a plausible reason for this result. A glass-box model can be shown with a misleading reason; a black box can carry a faithful post-hoc attribution. Different audiences demand different ones.)
  2. "Intrinsic vs post-hoc — when do you pick each?" (Intrinsic when high-stakes / regulated / cold-start where a simple rule is all the data supports; post-hoc when you've already paid for a deep ranker and can't surrender its accuracy. Intrinsic is faithful-by-construction but caps capacity; post-hoc keeps accuracy but only approximates.)
  3. "Can I just show attention weights as the explanation?" (For a user-facing card, yes — plausible and persuasive. For an audit, no: attention is a relevance weight in one layer, not causal; low-attention items' value vectors can dominate, and different attention distributions can yield the same prediction. Verify with a counterfactual instead.)
  4. "Explain SHAP and why it beats raw feature weights." (Shapley value = a feature's average marginal contribution over all coalitions; the unique attribution with efficiency, symmetry, dummy. Contributions add up to the score — aggregable across a fairness slice and stable in a way LIME isn't. Cost: exponential exact, so KernelSHAP / TreeSHAP approximations.)
  5. "How do you make sure a 'Because you watched X' card isn't lying?" (Bind the template slot to the top attribution, then run a counterfactual check: remove X from the input and confirm the item actually drops out of the top-K. If it doesn't, the reason was fake — block it. This is the consistency guard you'd run in CI.)
  6. "How does explainability connect to fairness auditing?" (You can't audit what you can't attribute. Global SHAP across protected slices reveals whether an attribute or its proxy — coarse geo, device — is driving exposure. See lesson 20 · Fairness; explainability is the instrument that work reads from.)
  7. "Doesn't adding explanations cost accuracy?" (Usually overstated. Post-hoc keeps the accurate model; on structured data a GBDT with TreeSHAP is glass-box within a fraction of a percent of a deep net; and explanations buy accuracy back by surfacing leaked labels and train–serve skew faster. Ask which column you're in, not how much accuracy it costs. The one hard number: a ±1% AUC/NDCG neutrality gate the module may not breach.)
  8. "How would you measure whether your explanations actually work?" (Three layers: system — coverage + generation latency; faithfulness — attribution stability > 0.7 (Spearman of SHAP rankings across time windows), fidelity to the model > 0.7, decision-path coverage ≥ 85% vs the real behaviour log; user/business — ESS (5-pt Likert), adoption (CTR on explained items), ECR (conversion lift). Gate on ±1% AUC neutrality first, then attribute the lift causally with difference-in-differences in the A/B — subtract the control's trend so you don't credit the explanation for a platform-wide shock. The senior point: an unfaithful explanation is worse than none, so faithfulness is a release blocker, not a nice-to-have.)
  9. "In a multi-task ranker, how do you explain which objective drove a card — not which feature?" (Feature-SHAP can't answer it; you need attribution over the heads. LRP back-propagates the fused score through the fusion weights onto each task head — "57% watch-time, 14% diversity"; or read per-expert gate activations in PLE/MMoE to see when the model favoured retention over clicks. This is the lesson-12 intersection: when the feed tilts to clickbait, objective-attribution shows the watch-time head's share jumped, so you debug the fusion weights — confirm with a counterfactual on w_k — rather than chasing features.)
  10. "Would you let an LLM write the recommendation reason?" (Yes, for fluency — but it adds hallucination on top of the fake-reason trap: a fluent sentence describing behaviour that never happened, which reads more trustworthy than a clumsy true one. Clamp it both ends: constrain the input to the actual top-SHAP features / strongest KG path (paraphrase, don't retrieve), then parse named entities back out and verify against the grounded set + a counterfactual; any ungrounded entity → block and fall back to a deterministic template. Distill offline, serve light, so it's nearline not per-request.)
Takeaway
Keep two words apart: transparency (follow the computation) and justification (show a reason). The master axis is intrinsic vs post-hoc — be the model, or explain it after the fact. Attention and templates are persuasive but not necessarily faithful (fine for users, dangerous in an audit); SHAP gives axiomatic, additive, aggregable attributions for debugging and fairness slices; KG paths are faithful-and-readable and often the only honest reason for a cold-start item; counterfactuals are your causal sanity check against fake reasons. The accuracy tradeoff is real but small on structured data — and post-hoc dissolves most of it.