Understanding Model Decisions with Explainer

The Explainer class in the ruleopt library provides a tool for interpreting the decisions made by the trained models like RUGClassifier. The Explainer facilitates this by breaking down the predictions into understandable rules and metrics. This guide focuses on demonstrating how to use the Explainer alongside RUGClassifier to gain insights into the classification decisions on the Iris dataset.

Step 1: Import Libraries

First, import the necessary libraries:

from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_iris

from ruleopt import RUGClassifier
from ruleopt.rule_cost import Gini
from ruleopt.explainer import Explainer
from ruleopt.solver import HiGHSSolver

Step 2: Prepare the Data

Load the Iris dataset and split it into training and testing sets:

X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.05, random_state=42)

Step 3: Initialize and Train the Classifier

Set up the RUGClassifier with specific parameters and fit it to the training data:

# Define tree parameters, solver and rule_cost
tree_parameters = {"max_depth": 3, "class_weight": "balanced"}
solver = HiGHSSolver()
rule_cost = Gini()

# Initialize the RUGClassifier with specific parameters
rug = RUGClassifier(
    solver=solver,
    max_rmp_calls=20,
    rule_cost=rule_cost,
    **tree_parameters,
)

rug.fit(X_train, y_train)

Step 4: Utilize the Explainer

Initialize the Explainer with the fitted RUGClassifier:

exp = Explainer(rug)

In all methods, the info parameter can be set to True to collect additional output information. For more details, you can check the API reference.

Retrieve Rule Details

Retrieve and print details for all rules:

rule_details = exp.retrieve_rule_details(feature_names=["sepal length", "sepal width", "petal length", "petal width"], info=True)
+-------------------------------------------------------------+
| Rule 0  |  Class: 2  |  Weight: 1.0000                      |
+-------------------------------------------------------------+
| single | -inf < sepal length <= 6.35             | not null |
| single | 1.55 < petal width <= inf               | or null  |
| single | -inf < sepal width <= 3.10              | or null  |
+-------------------------------------------------------------+
+-------------------------------------------------------------+
| Rule 1  |  Class: 2  |  Weight: 0.6667                      |
+-------------------------------------------------------------+
| single | -inf < petal width <= 1.55              | not null |
| single | 4.90 < petal length <= inf              | or null  |
| single | -inf < sepal width <= 3.10              | or null  |
+-------------------------------------------------------------+

Rules are displayed in a table format. Each row shows the clause type (single for single-feature, oblique for multi-feature), the condition, and null-handling behavior. When use_oblique=True is used, oblique clauses appear as linear combinations:

+----------------------------------------------------------------------+
| Rule 0  |  Class: 1  |  Weight: 1.0000                               |
+----------------------------------------------------------------------+
| single  | -inf < petal length <= 5.05                      | or null |
| oblique | -0.53*sepal width + 1.00*petal width < 0.19                |
| oblique | 0.13*sepal length + -1.00*petal width < -0.08              |
+----------------------------------------------------------------------+

The returned dictionary also includes n_clauses and n_oblique_clauses fields for each rule.

Find Applicable Rules for Test Samples

Identify rules applicable to samples in the test set:

applicable_rules = exp.find_applicable_rules_for_samples(X_test, feature_names=["sepal length", "sepal width", "petal length", "petal width"], info=True)

By applying the classifier’s rules to the test samples, we identify which rules are activated for individual instances and observe their respective weights.

Rules for instance 0
+--------------------------------------------------+
| Rule 2  |  Class: 1  |  Weight: 0.6667           |
+--------------------------------------------------+
| single | -inf < petal width <= 1.75   | or null  |
| single | 5.40 < sepal length <= 6.95  | or null  |
+--------------------------------------------------+
+--------------------------------------------------+
| Rule 5  |  Class: 1  |  Weight: 0.4444           |
+--------------------------------------------------+
| single | -inf < petal length <= 4.95  | or null  |
| single | 0.80 < petal width <= 1.75   | or null  |
+--------------------------------------------------+

Rules for instance 1
+--------------------------------------------------+
| Rule 2  |  Class: 1  |  Weight: 0.6667           |
+--------------------------------------------------+
| single | -inf < petal width <= 1.75   | or null  |
| single | 5.40 < sepal length <= 6.95  | or null  |
+--------------------------------------------------+
+---------------------------------------------------+
| Rule 4  |  Class: 0  |  Weight: 0.4444            |
+---------------------------------------------------+
| single | -inf < petal width <= 0.80   | not null  |
+---------------------------------------------------+

Summarize Rule Metrics

Get a summary of rule complexity and count:

rule_metrics_summary = exp.summarize_rule_metrics(info=True)

This summary provides an overview by detailing the total number of rules generated, the average rule length, and other metrics.

Total number of rules: 7
Average rule length: 2.00
Total clauses: 12 single-feature, 8 oblique

The clause breakdown (single-feature vs oblique) is shown when the model contains oblique rules.

Evaluate Rule Coverage Metrics

Understand how well the rules cover the test dataset:

rule_coverage_metrics = exp.evaluate_rule_coverage_metrics(X_test, info=True)

This evaluation assesses the effectiveness and coverage of the rules across the test dataset, highlighting the number of instances not covered by any rule (they are predicted as belonging to the majority class), the average number of rules applicable per sample, and the average length of these rules.

Number of instances not covered by any rule: 0
Average number of rules per sample: 1.75
Average length of rules per sample: 1.75

Conclusion

This example shows how to classify the Iris dataset using RUGClassifier and analyzes the resulting rules using the Explainer. For more information on ruleopt and its features, visit the official documentation.