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:
/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 (orbase_valuefor the intercept)value— the input value the row carried for that feature, as a stringscore— that feature's additive contribution to this row's score
Which scale is the breakdown on?
| Model type | Breakdown scale | Sums to |
|---|---|---|
| Classification | Link scale (log-odds / logit) | score (convert with sigmoid to compare against proba) |
| Regression | Raw target units | score, 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)
If you only need decisions and not explanations, call
/predict with show_breakdown=false — responses are
substantially smaller.