|
import lime
|
|
import lime.lime_tabular
|
|
import numpy as np
|
|
import pandas as pd
|
|
|
|
class LimeExplainer:
|
|
def __init__(self, model, feature_names=None, class_names=None):
|
|
self.model = model
|
|
self.explainer = None
|
|
self.feature_names = feature_names
|
|
self.class_names = class_names or ['Denied', 'Approved']
|
|
|
|
def fit(self, X_background):
|
|
"""Fit the explainer with background data"""
|
|
|
|
if self.feature_names is None and hasattr(self.model, 'feature_names'):
|
|
self.feature_names = self.model.feature_names
|
|
|
|
|
|
def predict_fn(x):
|
|
if hasattr(self.model, 'predict_proba'):
|
|
return self.model.predict_proba(pd.DataFrame(x, columns=self.feature_names))
|
|
else:
|
|
return self.model.model.predict_proba(x)
|
|
|
|
|
|
self.explainer = lime.lime_tabular.LimeTabularExplainer(
|
|
X_background.values,
|
|
feature_names=self.feature_names,
|
|
class_names=self.class_names,
|
|
mode='classification'
|
|
)
|
|
|
|
def explain_instance(self, instance, num_features=5):
|
|
"""Generate LIME explanation for a single instance"""
|
|
if self.explainer is None:
|
|
raise ValueError("Explainer not fitted. Call fit() first.")
|
|
|
|
|
|
def predict_fn(x):
|
|
if hasattr(self.model, 'predict_proba'):
|
|
return self.model.predict_proba(pd.DataFrame(x, columns=self.feature_names))
|
|
else:
|
|
return self.model.model.predict_proba(x)
|
|
|
|
|
|
explanation = self.explainer.explain_instance(
|
|
instance.values[0],
|
|
predict_fn,
|
|
num_features=num_features
|
|
)
|
|
|
|
return explanation
|
|
|
|
def generate_explanation(self, instance, num_features=5):
|
|
"""Generate human-readable explanation"""
|
|
explanation = self.explain_instance(instance, num_features)
|
|
|
|
|
|
features_weights = explanation.as_list()
|
|
|
|
|
|
text_explanation = []
|
|
for feature, weight in features_weights:
|
|
direction = "increased" if weight > 0 else "decreased"
|
|
text_explanation.append(f"{feature} {direction} the likelihood")
|
|
|
|
return " and ".join(text_explanation)
|
|
|
|
def plot_explanation(self, instance, num_features=5):
|
|
"""Plot the explanation"""
|
|
explanation = self.explain_instance(instance, num_features)
|
|
return explanation.as_pyplot_figure() |