Skip to main content
Version: v1.4.1

Explainability

xplainable models are glass-box: every prediction decomposes exactly into a base value plus one contribution per feature. Nothing is approximated after the fact — the breakdown is the computation the model performed.

Feel it before you read about it — drag the inputs and watch the breakdown re-sum to the score:

Live breakdown — churn modelreal model output
26.7%churn probability (proba)link score: -1.0089pred: RETAIN
base_value
+0.062
avg_order_value = 84
-0.073
discount_applied = 0.1
-0.167
plan = premium
-0.172
tenure_days = 381
-0.147
tenure_days_&_avg_order_value
+0.001
tenure_days_&_discount_applied
+0.085
tenure_days_&_plan
-0.294
avg_order_value_&_discount_applied
-0.059
avg_order_value_&_plan
-0.033
discount_applied_&_plan
-0.212
base + Σ contributions = -1.0090 → sigmoid → 26.7%
Responses are precomputed /v1/predict outputs from a real trained model (inputs snap to the demo grid). Interaction terms carry the _&_ separator — watch them shift as the rest of the row changes.

The additive contract​

For every scored row:

score = base_value + sum(feature scores)

This holds exactly, not approximately. The breakdown array returned by /predict lists base_value first, followed by one entry per feature:

"breakdown": [
{ "feature": "base_value", "value": null, "score": -0.20 },
{ "feature": "tenure_days", "value": "420", "score": 0.85 },
{ "feature": "plan", "value": "premium", "score": 0.17 }
]
  • feature — the feature name (or base_value for the intercept)
  • value — the input value the row carried for that feature, as a string
  • score — that feature's additive contribution to this row's score

Which scale is the breakdown on?​

Model typeBreakdown scaleSums to
ClassificationLink scale (log-odds / logit)score (convert with sigmoid to compare against proba)
RegressionRaw target unitsscore, which equals pred

For classification the calibrated probability proba is derived from the link-scale score, so a positive feature score always pushes the probability up and a negative one pushes it down — but the size of the probability change depends on where the row sits on the sigmoid.

Interaction terms (v2 models)​

v2 (XGM) models can include pairwise interaction terms. These appear in the breakdown as their own entries with an "_&_" separator in the name and a null value (there is no single input value to report):

{ "feature": "tenure_days_&_plan", "value": null, "score": 0.09 }

The additive contract still holds — interaction scores are part of the sum.

Why two similar rows can get different explanations​

When a model carries interactions, a feature's contribution for a given row is conditioned on the rest of that row. The same discount_applied = 0.1 can contribute +0.3 for a long-tenure customer and -0.1 for a new one, because the interaction terms fold the other feature values into its effective contribution. This is the mechanism behind personalized optimization results: each row gets its own contribution curves, so the optimal intervention is genuinely row-specific.

Model-level explainers​

Row breakdowns explain a single prediction. For a model-wide view, use the platform SDK.

explain_model()​

Digests the model profile into the base value and the top features by importance, each with an effect direction and a one-line summary:

explanation = client.workflow.explain_model(model_id)
{
"model_id": "...",
"version_id": "...",
"target": "churned",
"base_value": -0.2,
"n_features": 14,
"top_features": [
{
"name": "tenure_days",
"importance": 0.35,
"direction": "decreasing",
"summary": "Higher tenure_days lowers the prediction."
}
]
}

direction is one of increasing, decreasing, mixed, or flat for numeric features, and categorical for categorical ones.

get_model_profile()​

The full raw profile — per-feature score curves (numeric bins) and per-category scores — as rendered in the platform UI:

profile = client.models.get_model_profile(version_id)

get_feature_info()​

Feature types, health metrics, and distributions:

info = client.models.get_feature_info(version_id)
Trimming payloads

If you only need decisions and not explanations, call /predict with show_breakdown=false — responses are substantially smaller.