team-project-gui / src /explainability.py
nikos99n's picture
remove random state, matrix labels on specific rows
2607de5
Raw
History Blame Contribute Delete
3.31 kB
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import os
import lime
import lime.lime_tabular
from . import config
def plot_rf_feature_importance(model, feature_names):
"""
Plots and saves the Feature Importance for a Random Forest model.
"""
if not hasattr(model, 'feature_importances_'):
return None
importances = model.feature_importances_
indices = np.argsort(importances)[::-1]
sorted_names = [feature_names[i] for i in indices]
plt.figure(figsize=(14, 8))
plt.title("Random Forest: Feature Importance (Global XAI)")
plt.bar(range(len(importances)), importances[indices], align="center", color='teal')
plt.xticks(range(len(importances)), sorted_names, rotation=90)
plt.xlim([-1, len(importances)])
plt.ylabel("Relative Importance")
plt.tight_layout()
save_path = os.path.join(config.MODEL_DIR, 'rf_feature_importance.png')
plt.savefig(save_path)
plt.close()
print(f"Global XAI Plot saved to {save_path}")
def generate_lime_explanations(model, X_train, X_test, y_test, feature_names, class_names, model_name, num_samples=3):
"""
Generates LIME (Local Interpretable Model-agnostic Explanations) for specific test instances.
This works for ANY model (RF, SVM, etc.).
"""
print(f" Initializing LIME Explainer for {model_name}...")
# 1. Initialize Explainer
# LIME needs the training data to learn the distribution of features (mean, std, etc.)
explainer = lime.lime_tabular.LimeTabularExplainer(
training_data=np.array(X_train),
feature_names=feature_names,
class_names=class_names,
mode='classification',
verbose=False
)
# 2. Pick sample indices to explain
# We pick evenly spaced samples from the test set to get a variety
indices = np.linspace(0, len(X_test) - 1, num_samples, dtype=int)
output_dir = os.path.join(config.MODEL_DIR, 'lime_explanations')
os.makedirs(output_dir, exist_ok=True)
for i in indices:
# 3. Generate Explanation
# LIME perturbs this specific instance and sees how the model's prediction changes
exp = explainer.explain_instance(
data_row=X_test[i],
predict_fn=model.predict_proba,
num_features=10,
top_labels=1
)
# 4. Save Plot
# FIX: We must explicitly tell pyplot which label to plot.
# exp.local_exp keys are the class indices that were explained.
# Since we used top_labels=1, there is only one key.
available_labels = list(exp.local_exp.keys())
if not available_labels:
continue
explained_label_idx = available_labels[0]
# Get class name for title
pred_label_name = class_names[explained_label_idx]
true_label_name = class_names[y_test[i]]
# Pass the specific label we computed to avoid KeyError
fig = exp.as_pyplot_figure(label=explained_label_idx)
plt.title(f"LIME ({model_name}): Test Inst {i}\nTrue: {true_label_name} | Pred: {pred_label_name}")
plt.tight_layout()
save_path = os.path.join(output_dir, f'{model_name}_inst_{i}_lime.png')
plt.savefig(save_path)
plt.close()
print(f" LIME explanations saved to {output_dir}")