--- title: XAI Vision Inspector emoji: πŸ‘οΈ colorFrom: blue colorTo: indigo sdk: streamlit sdk_version: "1.37.0" python_version: "3.10" app_file: app.py pinned: false --- # πŸ” XAI Vision Inspector An interactive Explainable AI dashboard for visualizing how CNNs make decisions β€” built entirely from scratch using PyTorch. Features **Eigen-IG**, a novel attribution method with published benchmark results across 500 ImageNet images and three architectures. ![Dashboard Preview](Dashboard.png) --- ## ✨ What's Inside ### Novel Method: Eigen-IG Eigen-IG extends Integrated Gradients with **per-channel trajectory SVD** and **eigenvalue-weighted integration**. Instead of uniformly averaging all gradient steps (many of which are noisy near the zero baseline), Eigen-IG scores each step by its alignment with dominant gradient patterns and upweights the most signal-rich steps via softmax. **Benchmark results (500 ImageNet images):** | Architecture | Deletion AUC ↓ | vs IG | Insertion AUC ↑ | vs IG | Significant? | |---|---|---|---|---|---| | ResNet-50 | **0.0278** | βˆ’17.4% | 0.0560 | βˆ’36% | βœ… p<0.0001 | | VGG-16 | 0.0455 | βˆ’5.6% | 0.0762 | βˆ’37% | ❌ p=0.12 | | EfficientNet-B0 | 0.0625 | βˆ’2.9% | **0.2385** | +35.2% | βœ… p<0.0001 | Eigen-IG consistently produces **94–97% sparse** attribution maps vs 80–93% for IG. See the companion paper for full analysis. Raw per-image results: [`results/`](results/) --- ### All XAI Methods β€” Implemented From Scratch | Method | Type | Resolution | Speed | Axioms | |--------|------|-----------|-------|--------| | **Eigen-IG** | SVD-weighted path integral | Full pixel | 🐒 ~1.3Γ— IG | Relaxed completeness | | **Integrated Gradients** | Path integral | Full pixel | 🐒 Slow | βœ… Completeness, Sensitivity | | **Grad-CAM** | Gradient-based | Low–Med | ⚑ Fast | None | | **Grad-CAM++** | Gradient-based | Low–Med | ⚑ Fast | None | | **Occlusion Sensitivity** | Perturbation | Configurable | 🐌 Very slow | Model-agnostic | | **SmoothGrad** | Noise-averaged | Full pixel | 🐒 Slow | None | ### Pretrained Models ResNet-50, ResNet-101, VGG-16, EfficientNet-B0, DenseNet-121, MobileNet-V3 ### Dashboard Highlights - πŸ”¬ **Single-image analysis** β€” side-by-side attribution map comparison with interactive Plotly heatmaps - πŸš€ **Batch benchmark mode** β€” run faithfulness metrics over a folder of images with auto CSV export - πŸŽ›οΈ Configurable method parameters (steps, patch size, noise level, SVD rank, temperature) - 🎨 8 colormaps with adjustable overlay opacity - πŸ“Š Attribution statistics, distribution histograms, and faithfulness metrics table - πŸ“– In-app method documentation with paper references and axiomatic analysis - ⚑ GPU acceleration (CUDA auto-detected) --- ## πŸš€ Quick Start ### 1. Clone & Install ```bash git clone cd xai-vision-inspector python -m venv .venv source .venv/bin/activate # Windows: .venv\Scripts\activate pip install -r requirements.txt ``` **For GPU acceleration (recommended, RTX 40/50 series):** ```bash pip install --pre torch torchvision --index-url https://download.pytorch.org/whl/nightly/cu128 ``` Verify CUDA: ```bash python -c "import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name(0))" ``` ### 2. Run the Dashboard ```bash streamlit run app.py ``` Open [http://localhost:8501](http://localhost:8501) in your browser. --- ## πŸ“ Project Structure ``` xai-vision-inspector/ β”‚ β”œβ”€β”€ app.py # Streamlit dashboard entry point β”œβ”€β”€ requirements.txt β”‚ β”œβ”€β”€ explainers/ # XAI methods β€” all from scratch β”‚ β”œβ”€β”€ eigen_integrated_gradients.py # Eigen-IG (novel method) β”‚ β”œβ”€β”€ grad_cam.py # Grad-CAM + Grad-CAM++ β”‚ β”œβ”€β”€ integrated_gradients.py # IG with convergence delta check β”‚ β”œβ”€β”€ occlusion_sensitivity.py # Batched sliding-window perturbation β”‚ └── smooth_grad.py # SmoothGrad standard/squared/variance β”‚ β”œβ”€β”€ evaluation/ # Faithfulness benchmark suite β”‚ └── faithfulness.py # Deletion AUC, Insertion AUC, Infidelity β”‚ β”œβ”€β”€ model_zoo/ # Model registry + layer navigation β”‚ └── model_loader.py β”‚ β”œβ”€β”€ visualization/ # Heatmap overlays, Plotly charts, stats β”‚ └── overlay.py β”‚ └── utils/ └── image_utils.py # Preprocessing, denormalization ``` --- ## πŸ”¬ Method Deep Dive ### Eigen-IG ```python from explainers import EigenIntegratedGradients explainer = EigenIntegratedGradients( model, n_steps=50, # integration steps n_components=10, # SVD rank k weight_temp=1.0, # softmax temperature Ο„ baseline_type="zeros" ) eigen_attrs, eigen_map = explainer(input_tensor, class_idx=None) # Check completeness relaxation delta = explainer.convergence_delta(input_tensor, eigen_attrs) print(f"Convergence delta: {delta:.4f}") # mean 0.031 Β± 0.019 on ResNet-50 ``` **Algorithm:** 1. Collect gradient trajectory `G ∈ R^(T Γ— C Γ— H Γ— W)` via batched backward pass 2. Per channel: form `G_c ∈ R^(T Γ— H*W)`, compute truncated SVD β†’ top-k spatial patterns `V_k` 3. Score each step: `s_t = β€–g_c^(t) Β· V_k^Tβ€–β‚‚` 4. Softmax weights: `w_t = exp(s_t/Ο„) / Ξ£ exp(s_t'/Ο„)` 5. Weighted average: `ĝ_c = Ξ£ w_t Β· g_c^(t)`, scaled by `(x - x')` ### Integrated Gradients ```python from explainers import IntegratedGradients explainer = IntegratedGradients(model, n_steps=100, baseline_type="zeros") ig_attrs, ig_map = explainer(input_tensor, class_idx=None) delta = explainer.convergence_delta(input_tensor, ig_attrs, class_idx=top1_idx) print(f"Convergence delta: {delta:.4f}") # should be near 0 ``` ### Grad-CAM ```python from explainers import GradCAM from model_zoo import load_model, get_layer_by_name model, config = load_model("ResNet-50") layer = get_layer_by_name(model, config.default_target_layer) explainer = GradCAM(model, layer) cam = explainer(input_tensor, class_idx=None) # (H, W) array in [0, 1] explainer.remove_hooks() ``` ### Occlusion Sensitivity ```python from explainers import OcclusionSensitivity explainer = OcclusionSensitivity(model, patch_size=32, stride=16) sensitivity_map, resolved_class = explainer(input_tensor, batch_size=32) ``` ### SmoothGrad ```python from explainers import SmoothGrad explainer = SmoothGrad(model, n_samples=50, noise_level=0.15, variant="squared") attrs, smooth_map = explainer(input_tensor, class_idx=None) ``` --- ## πŸ“Š Faithfulness Benchmark Run the full benchmark from the dashboard (Batch Benchmark tab) or programmatically: ```python from evaluation.faithfulness import run_full_faithfulness_eval results = run_full_faithfulness_eval( model=model, input_tensor=tensor, saliency_maps={ "Eigen-IG": eigen_map, "IG": ig_map, "Grad-CAM": cam, }, class_idx=top1_idx, steps=10, ) # results[method] = {Deletion_AUC, Insertion_AUC, Infidelity, Deletion_final_drop} ``` **Metric interpretation:** | Metric | Better | Measures | |---|---|---| | Deletion AUC | Lower | How causally decisive the attributed pixels are | | Insertion AUC | Higher | How sufficient the attributed pixels are | | Infidelity | Lower | How well attributions predict score changes under noise | ### Batch Benchmark (Dashboard) 1. Switch sidebar to **Batch Benchmark** 2. Enter folder path to your ImageNet validation images 3. Select methods and max image count 4. Click **β–Ά Run Analysis** The first 5 images run without timeout (**calibration phase**) to measure real per-image wall time. The remaining images run with a `max_observed + 0.5s` per-image timeout, with live ETA and skip counter. Results auto-export as CSV. **Approximate runtimes (RTX 5050, T=50, steps=5):** | Architecture | Per image | 500 images | |---|---|---| | ResNet-50 | ~2–3s | ~25–35 min | | VGG-16 | ~4–5s | ~45–60 min | | EfficientNet-B0 | ~2–3s | ~25–35 min | --- ## πŸŽ›οΈ Configuration Guide | Parameter | Default | Effect | |-----------|---------|--------| | IG / Eigen-IG steps | 50 | ↑ steps = more accurate, slower | | Eigen-IG n_components (k) | 10 | SVD rank β€” stable for k β‰₯ 5 | | Eigen-IG weight_temp (Ο„) | 1.0 | Lower = more aggressive step weighting | | Occlusion patch size | 32px | ↑ patch = faster but coarser | | Occlusion stride | 16px | ↓ stride = higher resolution, much slower | | SmoothGrad samples | 40 | ↑ samples = smoother map | | Noise level | 0.15 | Οƒ as fraction of input range | --- ## πŸ§ͺ Programmatic API Example ```python import torch from PIL import Image from model_zoo import load_model, get_layer_by_name from explainers import GradCAM, IntegratedGradients, EigenIntegratedGradients from utils import preprocess_image from visualization import overlay_heatmap, make_comparison_figure from evaluation.faithfulness import run_full_faithfulness_eval # Load model model, config = load_model("ResNet-50", device="cuda") # Preprocess image pil_img = Image.open("dog.jpg") tensor, display = preprocess_image(pil_img, device="cuda") # Run methods layer = get_layer_by_name(model, config.default_target_layer) cam_exp = GradCAM(model, layer) cam = cam_exp(tensor); cam_exp.remove_hooks() ig_exp = IntegratedGradients(model, n_steps=50) _, ig = ig_exp(tensor) eig_exp = EigenIntegratedGradients(model, n_steps=50, n_components=10) _, eig = eig_exp(tensor) # Faithfulness benchmark import torch.nn.functional as F with torch.no_grad(): probs = F.softmax(model(tensor), dim=1).squeeze() class_idx = int(probs.argmax()) results = run_full_faithfulness_eval( model, tensor, {"Grad-CAM": cam, "IG": ig, "Eigen-IG": eig}, class_idx=class_idx ) for method, metrics in results.items(): print(f"{method}: {metrics}") # Save comparison figure fig = make_comparison_figure( display, {"Grad-CAM": cam, "IG": ig, "Eigen-IG": eig}, colormap="jet", alpha=0.5, ) fig.savefig("comparison.png", bbox_inches="tight") ``` --- ## πŸ“š References - **Eigen-IG** (this work): *SVD-Weighted Path Integration for Sparser CNN Attribution Maps*, 2026 - **Integrated Gradients**: Sundararajan et al., 2017 β€” [arXiv:1703.01365](https://arxiv.org/abs/1703.01365) - **Grad-CAM**: Selvaraju et al., 2017 β€” [arXiv:1610.02391](https://arxiv.org/abs/1610.02391) - **Grad-CAM++**: Chattopadhay et al., 2018 β€” [arXiv:1710.11063](https://arxiv.org/abs/1710.11063) - **Occlusion Sensitivity**: Zeiler & Fergus, 2014 β€” [arXiv:1311.2901](https://arxiv.org/abs/1311.2901) - **SmoothGrad**: Smilkov et al., 2017 β€” [arXiv:1706.03825](https://arxiv.org/abs/1706.03825) - **RISE (Deletion/Insertion)**: Petsiuk et al., 2018 β€” [arXiv:1806.07421](https://arxiv.org/abs/1806.07421) - **Infidelity**: Yeh et al., 2019 β€” [arXiv:1901.09392](https://arxiv.org/abs/1901.09392) --- ## πŸ“ License MIT License