Skip to content

Explain an ml4 model

Time: about 10 minutes · You need: mli installed (Installation) and a binary classifier saved by ml4. You'll end with: the model's archetypes and rules in business column names, and reason codes for one new row scored by the deployed pipeline.

In short

  • ml4 builds the model, mli explains it. In the app, an experiment's Interpretation tab does all of this for you, for every task.
  • In Python, mli explains the best readable base tree on the ml4 board, which need not be the deployed model. pipeline.best_introspectable_leaf() picks it.
  • Ml4OriginRegistry rolls engineered features back to the columns they came from.
  • result.justifier(pipeline=...) scores a raw row with the deployed pipeline and explains it with the base tree.

1. Load the pipeline and pick the tree to explain

import parleml_ml4
import pandas as pd

pipeline = parleml_ml4.load_pipeline("out/bundle")          # a FittedPipeline saved by ml4
choice = pipeline.best_introspectable_leaf()         # None when nothing on the board is readable
estimator = choice.handle.raw_estimator              # e.g. a LightGBM booster
print(choice.model_name, choice.is_strongest)        # is_strongest: it is also the deployed model

best_introspectable_leaf() scans the board for the best-scoring base model mli can read, preferring LightGBM, XGBoost and CatBoost over glass-box models. It returns None when nothing qualifies, for example a run of foundation models only. Use monitor_predictions on the pipeline's scores then.

2. Move your rows into the tree's feature space

The tree was trained on ml4's engineered features, not on your raw columns. Transform the raw rows once, then build the tree's own matrix:

df = pd.read_parquet("transactions.parquet")
y = df.pop("is_fraud")                               # a 0/1 target

X = pipeline.prepare_member_introspection_frame(pipeline.transform(df))

prepare_member_introspection_frame applies the chosen tree's own fold-level feature engineering, such as target encodings, on top of the pipeline's global features.

The result is a plain, column-named DataFrame whose columns are the tree's feature names.

3. Profile it

from parleml_mli import ProfilingConfig, RulesConfig, TaskType, fit_profiler
from parleml_mli.adapters.ml4_encoding import Ml4OriginRegistry

result = fit_profiler(
    model=estimator,
    X=X,
    y=y,
    target_mask=(y == 1),
    task_type=TaskType.BINARY,
    positive_class=1,                                  # a raw booster records no classes
    encoding_registry=Ml4OriginRegistry(df.columns),   # engineered name -> source column
    config=ProfilingConfig(rules=RulesConfig()),
)
print(result.summary_df[["cluster_id", "n_target_cases", "lift"]])

This example uses a 0/1 target, which ml4 also codes as 0/1. With string labels or more than two classes, the tree predicts ml4's class codes: map y to those codes before you profile. The app's Interpretation tab does this for you.

To test the rules on rows the model never saw, pass held-out rows moved the same way as X_holdout / y_holdout. See Rule extraction.

4. Explain one new row

justifier = result.justifier(pipeline=pipeline)      # bind once, reuse for many rows
case = justifier.justify(df.iloc[[0]])               # a raw one-row DataFrame
print(case.prediction, case.matched_archetype)
for driver in case.shap_drivers:
    print(driver)

The deployed pipeline produces the prediction and the decision threshold. The drivers, archetype, rules and neighbours come from the tree you profiled. When that tree is the deployed model, the two are the same model.

Details: glass-box structure of the deployed models

parleml_mli.adapters.ml4_pipeline.explain_pipeline(pipeline) returns one LeafExplanation per model in the deployed pipeline: its kind, ensemble weight, feature names and native structure. Coefficients, EBM shape functions and RuleFit rules come back as a dict; an opaque tree or booster has native_explanation=None.

Next