exai67 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of exai67 might be problematic. Click here for more details.

exai67-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 EXAI Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
exai67-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,143 @@
1
+ Metadata-Version: 2.1
2
+ Name: exai67
3
+ Version: 0.1.0
4
+ Summary: Lightweight, dependency-minimal Explainable AI (XAI) algorithms and laboratory experiments implemented from scratch.
5
+ Author: EXAI Team
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/exai-toolkit/exai-toolkit
8
+ Project-URL: Repository, https://github.com/exai-toolkit/exai-toolkit
9
+ Keywords: xai,explainable-ai,interpretability,shapley,lime,gradcam,lrp,ice,pdp,xrai,machine-learning
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Education
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.8
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
23
+ Requires-Python: >=3.8
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: numpy>=1.20.0
27
+ Requires-Dist: pandas>=1.3.0
28
+ Requires-Dist: scikit-learn>=1.0.0
29
+
30
+ # EXAI (`exai67`)
31
+
32
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
33
+ [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
34
+
35
+ A lightweight, dependency-minimal Python library containing complete implementations of **10 foundational Explainable AI (XAI)** laboratory experiments from scratch. Calling any function prints the full Python code for that experiment directly to the screen.
36
+
37
+ ---
38
+
39
+ ## Features
40
+
41
+ - **Instant Code Display**: Calling any function or experiment displays the complete, working Python source code.
42
+ - **Pure / Minimal Dependencies**: Built with standard Python, `numpy`, `pandas`, and `scikit-learn`. Zero deep learning frameworks.
43
+ - **Exact Formulations**: Follows published literature (Greenwell et al., Ribeiro et al. LIME, Selvaraju et al. Grad-CAM, Bach et al. LRP).
44
+ - **Interactive CLI**: Display experiment codes directly from terminal with `exai run <1-10>` or `exai list`.
45
+
46
+ ---
47
+
48
+ ## Included XAI Algorithms
49
+
50
+ | Experiment | Module / Function | Description |
51
+ | :--- | :--- | :--- |
52
+ | **Index** | `exai.index()` | Index of all 10 experiments with descriptions and commands |
53
+ | **Exp 1** | `exai.exp1()`, `exai.compute_linear_regression_metrics()` | Linear ($R^2$, Adj $R^2$, $t$-stat, 95% CI) & Logistic Regression (Odds Ratio, % change) |
54
+ | **Exp 2** | `exai.exp2()`, `exai.extract_if_then_rules()` | Decision Tree IF-THEN rules, MDI vs Permutation Importance; KNN local neighbor explanations |
55
+ | **Exp 3** | `exai.exp3()`, `exai.approximate_shapley_sampling()` | Approximate Shapley Values via permutation sampling from scratch |
56
+ | **Exp 4** | `exai.exp4()`, `exai.compute_pdp()` | Partial Dependence Plot (PDP) Feature Importance (Greenwell et al., 2018) |
57
+ | **Exp 5** | `exai.exp5()`, `exai.compute_ice_scratch()` | Individual Conditional Expectation (ICE) curves and PDP average computation |
58
+ | **Exp 6** | `exai.exp6()`, `exai.compute_xrai_fixed_regions()` | XRAI Region-based image attribution and visual intensity scoring |
59
+ | **Exp 7** | `exai.exp7()`, `exai.compute_gradcam()` | Grad-CAM (Gradient-Weighted Class Activation Mapping) with ReLU heatmap generation |
60
+ | **Exp 8** | `exai.exp8()`, `exai.fit_lime_surrogate()` | LIME from scratch via Weighted Least Squares (WLS) and exponential kernels |
61
+ | **Exp 9** | `exai.exp9()`, `exai.compute_gradient_x_input()` | CBOW Word Embeddings from scratch & Gradient × Input sentiment attribution |
62
+ | **Exp 10** | `exai.exp10()`, `exai.layerwise_relevance_propagation()` | Layer-Wise Relevance Propagation (LRP) for multi-layer neural networks |
63
+
64
+ ---
65
+
66
+ ## Installation
67
+
68
+ ### From PyPI
69
+ ```bash
70
+ pip install exai67
71
+ ```
72
+
73
+ ### Local Development Install
74
+ ```bash
75
+ pip install -e .
76
+ ```
77
+
78
+ ---
79
+
80
+ ## Python API Usage
81
+
82
+ Calling any function displays the clean, complete, ready-to-run Python code for that experiment:
83
+
84
+ ```python
85
+ import exai
86
+
87
+ # 1. Print code by experiment number
88
+ exai.get_code(1) # Displays Experiment 1 code
89
+ exai.get_code(8) # Displays Experiment 8 code
90
+
91
+ # 2. Print code using shortcut functions
92
+ exai.exp1() # Linear & Logistic Regression
93
+ exai.exp2() # Decision Trees & KNN
94
+ exai.exp3() # Shapley Values
95
+ exai.exp4() # PDP Feature Importance
96
+ exai.exp5() # ICE Plot
97
+ exai.exp6() # XRAI Attribution
98
+ exai.exp7() # Grad-CAM
99
+ exai.exp8() # LIME
100
+ exai.exp9() # CBOW & Gradient x Input
101
+ exai.exp10() # LRP
102
+
103
+ # 3. Print code using algorithm names
104
+ exai.compute_gradcam()
105
+ exai.fit_lime_surrogate()
106
+ exai.layerwise_relevance_propagation()
107
+ ```
108
+
109
+ ---
110
+
111
+ ## Command-Line Interface (CLI)
112
+
113
+ ```bash
114
+ # List all 10 available experiments
115
+ exai list
116
+
117
+ # Display code for a specific experiment
118
+ exai run 8
119
+
120
+ # Display code for all experiments
121
+ exai run all
122
+ ```
123
+
124
+ ---
125
+
126
+ ## Publishing to PyPI
127
+
128
+ ```bash
129
+ # 1. Install build tools
130
+ pip install --upgrade build twine
131
+
132
+ # 2. Build the package
133
+ python3 setup.py sdist
134
+
135
+ # 3. Upload to PyPI
136
+ twine upload dist/*
137
+ ```
138
+
139
+ ---
140
+
141
+ ## License
142
+
143
+ This project is licensed under the [MIT License](LICENSE).
exai67-0.1.0/README.md ADDED
@@ -0,0 +1,114 @@
1
+ # EXAI (`exai67`)
2
+
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
+ [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
5
+
6
+ A lightweight, dependency-minimal Python library containing complete implementations of **10 foundational Explainable AI (XAI)** laboratory experiments from scratch. Calling any function prints the full Python code for that experiment directly to the screen.
7
+
8
+ ---
9
+
10
+ ## Features
11
+
12
+ - **Instant Code Display**: Calling any function or experiment displays the complete, working Python source code.
13
+ - **Pure / Minimal Dependencies**: Built with standard Python, `numpy`, `pandas`, and `scikit-learn`. Zero deep learning frameworks.
14
+ - **Exact Formulations**: Follows published literature (Greenwell et al., Ribeiro et al. LIME, Selvaraju et al. Grad-CAM, Bach et al. LRP).
15
+ - **Interactive CLI**: Display experiment codes directly from terminal with `exai run <1-10>` or `exai list`.
16
+
17
+ ---
18
+
19
+ ## Included XAI Algorithms
20
+
21
+ | Experiment | Module / Function | Description |
22
+ | :--- | :--- | :--- |
23
+ | **Index** | `exai.index()` | Index of all 10 experiments with descriptions and commands |
24
+ | **Exp 1** | `exai.exp1()`, `exai.compute_linear_regression_metrics()` | Linear ($R^2$, Adj $R^2$, $t$-stat, 95% CI) & Logistic Regression (Odds Ratio, % change) |
25
+ | **Exp 2** | `exai.exp2()`, `exai.extract_if_then_rules()` | Decision Tree IF-THEN rules, MDI vs Permutation Importance; KNN local neighbor explanations |
26
+ | **Exp 3** | `exai.exp3()`, `exai.approximate_shapley_sampling()` | Approximate Shapley Values via permutation sampling from scratch |
27
+ | **Exp 4** | `exai.exp4()`, `exai.compute_pdp()` | Partial Dependence Plot (PDP) Feature Importance (Greenwell et al., 2018) |
28
+ | **Exp 5** | `exai.exp5()`, `exai.compute_ice_scratch()` | Individual Conditional Expectation (ICE) curves and PDP average computation |
29
+ | **Exp 6** | `exai.exp6()`, `exai.compute_xrai_fixed_regions()` | XRAI Region-based image attribution and visual intensity scoring |
30
+ | **Exp 7** | `exai.exp7()`, `exai.compute_gradcam()` | Grad-CAM (Gradient-Weighted Class Activation Mapping) with ReLU heatmap generation |
31
+ | **Exp 8** | `exai.exp8()`, `exai.fit_lime_surrogate()` | LIME from scratch via Weighted Least Squares (WLS) and exponential kernels |
32
+ | **Exp 9** | `exai.exp9()`, `exai.compute_gradient_x_input()` | CBOW Word Embeddings from scratch & Gradient × Input sentiment attribution |
33
+ | **Exp 10** | `exai.exp10()`, `exai.layerwise_relevance_propagation()` | Layer-Wise Relevance Propagation (LRP) for multi-layer neural networks |
34
+
35
+ ---
36
+
37
+ ## Installation
38
+
39
+ ### From PyPI
40
+ ```bash
41
+ pip install exai67
42
+ ```
43
+
44
+ ### Local Development Install
45
+ ```bash
46
+ pip install -e .
47
+ ```
48
+
49
+ ---
50
+
51
+ ## Python API Usage
52
+
53
+ Calling any function displays the clean, complete, ready-to-run Python code for that experiment:
54
+
55
+ ```python
56
+ import exai
57
+
58
+ # 1. Print code by experiment number
59
+ exai.get_code(1) # Displays Experiment 1 code
60
+ exai.get_code(8) # Displays Experiment 8 code
61
+
62
+ # 2. Print code using shortcut functions
63
+ exai.exp1() # Linear & Logistic Regression
64
+ exai.exp2() # Decision Trees & KNN
65
+ exai.exp3() # Shapley Values
66
+ exai.exp4() # PDP Feature Importance
67
+ exai.exp5() # ICE Plot
68
+ exai.exp6() # XRAI Attribution
69
+ exai.exp7() # Grad-CAM
70
+ exai.exp8() # LIME
71
+ exai.exp9() # CBOW & Gradient x Input
72
+ exai.exp10() # LRP
73
+
74
+ # 3. Print code using algorithm names
75
+ exai.compute_gradcam()
76
+ exai.fit_lime_surrogate()
77
+ exai.layerwise_relevance_propagation()
78
+ ```
79
+
80
+ ---
81
+
82
+ ## Command-Line Interface (CLI)
83
+
84
+ ```bash
85
+ # List all 10 available experiments
86
+ exai list
87
+
88
+ # Display code for a specific experiment
89
+ exai run 8
90
+
91
+ # Display code for all experiments
92
+ exai run all
93
+ ```
94
+
95
+ ---
96
+
97
+ ## Publishing to PyPI
98
+
99
+ ```bash
100
+ # 1. Install build tools
101
+ pip install --upgrade build twine
102
+
103
+ # 2. Build the package
104
+ python3 setup.py sdist
105
+
106
+ # 3. Upload to PyPI
107
+ twine upload dist/*
108
+ ```
109
+
110
+ ---
111
+
112
+ ## License
113
+
114
+ This project is licensed under the [MIT License](LICENSE).
@@ -0,0 +1,57 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "exai67"
7
+ version = "0.1.0"
8
+ description = "Lightweight, dependency-minimal Explainable AI (XAI) algorithms and laboratory experiments implemented from scratch."
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = { text = "MIT" }
12
+ authors = [
13
+ { name = "EXAI Team" }
14
+ ]
15
+ keywords = [
16
+ "xai",
17
+ "explainable-ai",
18
+ "interpretability",
19
+ "shapley",
20
+ "lime",
21
+ "gradcam",
22
+ "lrp",
23
+ "ice",
24
+ "pdp",
25
+ "xrai",
26
+ "machine-learning"
27
+ ]
28
+ classifiers = [
29
+ "Development Status :: 4 - Beta",
30
+ "Intended Audience :: Developers",
31
+ "Intended Audience :: Education",
32
+ "Intended Audience :: Science/Research",
33
+ "License :: OSI Approved :: MIT License",
34
+ "Programming Language :: Python :: 3",
35
+ "Programming Language :: Python :: 3.8",
36
+ "Programming Language :: Python :: 3.9",
37
+ "Programming Language :: Python :: 3.10",
38
+ "Programming Language :: Python :: 3.11",
39
+ "Programming Language :: Python :: 3.12",
40
+ "Programming Language :: Python :: 3.13",
41
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
42
+ ]
43
+ dependencies = [
44
+ "numpy>=1.20.0",
45
+ "pandas>=1.3.0",
46
+ "scikit-learn>=1.0.0"
47
+ ]
48
+
49
+ [project.scripts]
50
+ exai = "exai.cli:main"
51
+
52
+ [project.urls]
53
+ Homepage = "https://github.com/exai-toolkit/exai-toolkit"
54
+ Repository = "https://github.com/exai-toolkit/exai-toolkit"
55
+
56
+ [tool.setuptools.packages.find]
57
+ where = ["src"]
exai67-0.1.0/setup.cfg ADDED
@@ -0,0 +1,8 @@
1
+ [sdist]
2
+ owner = root
3
+ group = wheel
4
+
5
+ [egg_info]
6
+ tag_build =
7
+ tag_date = 0
8
+
exai67-0.1.0/setup.py ADDED
@@ -0,0 +1,29 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ name="exai67",
5
+ version="0.1.0",
6
+ description="Lightweight Explainable AI (XAI) algorithms from scratch without deep learning libraries.",
7
+ long_description=open("README.md").read(),
8
+ long_description_content_type="text/markdown",
9
+ author="EXAI Team",
10
+ license="MIT",
11
+ package_dir={"": "src"},
12
+ packages=find_packages(where="src"),
13
+ python_requires=">=3.8",
14
+ install_requires=[
15
+ "numpy>=1.20.0",
16
+ "pandas>=1.3.0",
17
+ "scikit-learn>=1.0.0"
18
+ ],
19
+ entry_points={
20
+ "console_scripts": [
21
+ "exai=exai.cli:main",
22
+ ],
23
+ },
24
+ classifiers=[
25
+ "License :: OSI Approved :: MIT License",
26
+ "Programming Language :: Python :: 3",
27
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
28
+ ],
29
+ )
@@ -0,0 +1,195 @@
1
+ """
2
+ EXAI: Explainable AI Toolkit
3
+ ============================
4
+ A Python library providing complete implementations of 10 foundational Explainable AI (XAI)
5
+ laboratory experiments. Calling any function displays the complete Python code for that experiment.
6
+
7
+ Usage:
8
+ import exai
9
+ exai.exp1() # Prints code for Experiment 1
10
+ exai.experiment_8() # Prints code for Experiment 8 (LIME)
11
+ exai.get_code(7) # Prints code for Experiment 7 (Grad-CAM)
12
+ """
13
+
14
+ __version__ = "0.1.0"
15
+
16
+ from exai.linear_logistic import (
17
+ experiment_1,
18
+ run_experiment_1,
19
+ exp1,
20
+ compute_linear_regression_metrics,
21
+ interpret_logistic_regression,
22
+ CODE_EXPERIMENT_1,
23
+ )
24
+
25
+ from exai.tree_knn import (
26
+ experiment_2,
27
+ run_experiment_2,
28
+ exp2,
29
+ extract_if_then_rules,
30
+ compute_permutation_importance,
31
+ explain_knn_classification,
32
+ explain_knn_regression,
33
+ CODE_EXPERIMENT_2,
34
+ )
35
+
36
+ from exai.shapley import (
37
+ experiment_3,
38
+ run_experiment_3,
39
+ exp3,
40
+ linear_model,
41
+ compute_single_permutation_marginal_contribution,
42
+ approximate_shapley_sampling,
43
+ CODE_EXPERIMENT_3,
44
+ )
45
+
46
+ from exai.pdp import (
47
+ experiment_4,
48
+ run_experiment_4,
49
+ exp4,
50
+ compute_pdp,
51
+ compute_numerical_pdp_importance,
52
+ compute_categorical_pdp_importance,
53
+ CODE_EXPERIMENT_4,
54
+ )
55
+
56
+ from exai.ice import (
57
+ experiment_5,
58
+ run_experiment_5,
59
+ exp5,
60
+ compute_ice_scratch,
61
+ ascii_ice_plot,
62
+ CODE_EXPERIMENT_5,
63
+ )
64
+
65
+ from exai.xrai import (
66
+ experiment_6,
67
+ run_experiment_6,
68
+ exp6,
69
+ get_visual_intensity_bar,
70
+ compute_xrai_fixed_regions,
71
+ CODE_EXPERIMENT_6,
72
+ )
73
+
74
+ from exai.gradcam import (
75
+ experiment_7,
76
+ run_experiment_7,
77
+ exp7,
78
+ compute_gradcam,
79
+ print_ascii_heatmap,
80
+ CODE_EXPERIMENT_7,
81
+ )
82
+
83
+ from exai.lime import (
84
+ experiment_8,
85
+ run_experiment_8,
86
+ exp8,
87
+ compute_euclidean_distance,
88
+ exponential_kernel,
89
+ fit_lime_surrogate,
90
+ CODE_EXPERIMENT_8,
91
+ )
92
+
93
+ from exai.cbow_gradient import (
94
+ experiment_9,
95
+ run_experiment_9,
96
+ exp9,
97
+ tokenize,
98
+ build_vocabulary,
99
+ create_cbow_dataset,
100
+ softmax,
101
+ cosine_similarity,
102
+ SimpleCBOW,
103
+ sigmoid,
104
+ predict_sentiment,
105
+ compute_gradient_x_input,
106
+ CODE_EXPERIMENT_9,
107
+ )
108
+
109
+ from exai.lrp import (
110
+ experiment_10,
111
+ run_experiment_10,
112
+ exp10,
113
+ relu,
114
+ forward_propagation,
115
+ layerwise_relevance_propagation,
116
+ CODE_EXPERIMENT_10,
117
+ )
118
+
119
+ from exai.index import (
120
+ index,
121
+ show_index,
122
+ list_all,
123
+ INDEX_TEXT,
124
+ )
125
+
126
+ EXPERIMENTS = {
127
+ 1: CODE_EXPERIMENT_1,
128
+ 2: CODE_EXPERIMENT_2,
129
+ 3: CODE_EXPERIMENT_3,
130
+ 4: CODE_EXPERIMENT_4,
131
+ 5: CODE_EXPERIMENT_5,
132
+ 6: CODE_EXPERIMENT_6,
133
+ 7: CODE_EXPERIMENT_7,
134
+ 8: CODE_EXPERIMENT_8,
135
+ 9: CODE_EXPERIMENT_9,
136
+ 10: CODE_EXPERIMENT_10,
137
+ }
138
+
139
+
140
+ def get_code(experiment_number):
141
+ """
142
+ Displays the Python code for a given experiment number (1 to 10).
143
+ """
144
+ try:
145
+ num = int(experiment_number)
146
+ if num in EXPERIMENTS:
147
+ print(EXPERIMENTS[num])
148
+ else:
149
+ print(f"Error: Experiment {num} not found. Please choose between 1 and 10.")
150
+ except (ValueError, TypeError):
151
+ print(f"Error: Invalid experiment number '{experiment_number}'. Use an integer 1 to 10.")
152
+
153
+
154
+ __all__ = [
155
+ "__version__",
156
+ "get_code",
157
+ "index",
158
+ "show_index",
159
+ "list_all",
160
+ "INDEX_TEXT",
161
+ # Exp 1
162
+ "experiment_1", "run_experiment_1", "exp1",
163
+ "compute_linear_regression_metrics", "interpret_logistic_regression",
164
+ # Exp 2
165
+ "experiment_2", "run_experiment_2", "exp2",
166
+ "extract_if_then_rules", "compute_permutation_importance",
167
+ "explain_knn_classification", "explain_knn_regression",
168
+ # Exp 3
169
+ "experiment_3", "run_experiment_3", "exp3",
170
+ "linear_model", "compute_single_permutation_marginal_contribution",
171
+ "approximate_shapley_sampling",
172
+ # Exp 4
173
+ "experiment_4", "run_experiment_4", "exp4",
174
+ "compute_pdp", "compute_numerical_pdp_importance", "compute_categorical_pdp_importance",
175
+ # Exp 5
176
+ "experiment_5", "run_experiment_5", "exp5",
177
+ "compute_ice_scratch", "ascii_ice_plot",
178
+ # Exp 6
179
+ "experiment_6", "run_experiment_6", "exp6",
180
+ "get_visual_intensity_bar", "compute_xrai_fixed_regions",
181
+ # Exp 7
182
+ "experiment_7", "run_experiment_7", "exp7",
183
+ "compute_gradcam", "print_ascii_heatmap",
184
+ # Exp 8
185
+ "experiment_8", "run_experiment_8", "exp8",
186
+ "compute_euclidean_distance", "exponential_kernel", "fit_lime_surrogate",
187
+ # Exp 9
188
+ "experiment_9", "run_experiment_9", "exp9",
189
+ "tokenize", "build_vocabulary", "create_cbow_dataset", "softmax",
190
+ "cosine_similarity", "SimpleCBOW", "sigmoid", "predict_sentiment",
191
+ "compute_gradient_x_input",
192
+ # Exp 10
193
+ "experiment_10", "run_experiment_10", "exp10",
194
+ "relu", "forward_propagation", "layerwise_relevance_propagation",
195
+ ]
@@ -0,0 +1,37 @@
1
+ """Experiment 9: CBOW & Gradient x Input Explainability"""
2
+
3
+ CODE_EXPERIMENT_9 = '"""\nCBOW Word Embeddings & Gradient × Input Attribution Module\n==========================================================\nPart A: Continuous Bag of Words (CBOW) embeddings trained from scratch.\nPart B: Gradient × Input attribution for sentiment neural networks.\n"""\n\nimport math\nimport numpy as np\nimport pandas as pd\n\n\ndef tokenize(text):\n """Simple whitespace and lowercasing unigram tokenizer."""\n return text.lower().strip().split()\n\n\ndef build_vocabulary(corpus):\n """Builds a sorted unique vocabulary and index maps."""\n words = []\n for sentence in corpus:\n words.extend(tokenize(sentence))\n vocab = sorted(list(set(words)))\n word2idx = {w: i for i, w in enumerate(vocab)}\n idx2word = {i: w for i, w in enumerate(vocab)}\n return vocab, word2idx, idx2word\n\n\ndef create_cbow_dataset(corpus, word2idx, window_size=1):\n """Creates (context, target) pairs for CBOW."""\n data = []\n for sentence in corpus:\n tokens = tokenize(sentence)\n for t in range(len(tokens)):\n context_indices = []\n for delta in range(-window_size, window_size + 1):\n if delta != 0 and 0 <= t + delta < len(tokens):\n context_indices.append(word2idx[tokens[t + delta]])\n if context_indices:\n data.append((context_indices, word2idx[tokens[t]]))\n return data\n\n\ndef softmax(x):\n """Numerically stable softmax."""\n e_x = np.exp(x - np.max(x))\n return e_x / np.sum(e_x)\n\n\ndef cosine_similarity(v1, v2):\n """Computes cosine similarity between two vectors from scratch."""\n dot = np.dot(v1, v2)\n norm1 = np.linalg.norm(v1)\n norm2 = np.linalg.norm(v2)\n if norm1 == 0 or norm2 == 0:\n return 0.0\n return dot / (norm1 * norm2)\n\n\nclass SimpleCBOW:\n """CBOW model implemented in pure NumPy without DL libraries."""\n def __init__(self, vocab_size, embedding_dim=2, learning_rate=0.05, seed=42):\n np.random.seed(seed)\n self.vocab_size = vocab_size\n self.embedding_dim = embedding_dim\n self.lr = learning_rate\n self.W_in = np.random.uniform(-0.5, 0.5, (vocab_size, embedding_dim))\n self.W_out = np.random.uniform(-0.5, 0.5, (embedding_dim, vocab_size))\n \n def train(self, cbow_data, epochs=300):\n losses = []\n for _ in range(epochs):\n total_loss = 0.0\n for context_indices, target_idx in cbow_data:\n h = np.mean(self.W_in[context_indices], axis=0)\n u = np.dot(h, self.W_out)\n y_hat = softmax(u)\n loss = -np.log(y_hat[target_idx] + 1e-12)\n total_loss += loss\n \n e = y_hat.copy()\n e[target_idx] -= 1.0\n grad_W_out = np.outer(h, e)\n grad_h = np.dot(self.W_out, e)\n \n self.W_out -= self.lr * grad_W_out\n for idx in context_indices:\n self.W_in[idx] -= (self.lr / len(context_indices)) * grad_h\n losses.append(total_loss)\n return losses\n\n\ndef sigmoid(z):\n """Sigmoid activation function."""\n return 1.0 / (1.0 + math.exp(-z))\n\n\ndef predict_sentiment(x, weights, bias=0.0):\n """Single-neuron sentiment prediction."""\n z = sum(x[i] * weights[i] for i in range(len(x))) + bias\n return z, sigmoid(z)\n\n\ndef compute_gradient_x_input(x, weights, bias=0.0):\n """Computes Gradient × Input attribution: A_i = x_i * [d(y_hat)/d(x_i)]."""\n z, y_hat = predict_sentiment(x, weights, bias)\n sigmoid_gradient = y_hat * (1.0 - y_hat)\n gradients = [sigmoid_gradient * w for w in weights]\n attributions = [x[i] * gradients[i] for i in range(len(x))]\n return z, y_hat, sigmoid_gradient, gradients, attributions\n\n\ndef run_experiment_9():\n print("=" * 80)\n print("EXPERIMENT 9: CBOW EMBEDDINGS & GRADIENT × INPUT EXPLAINABILITY")\n print("=" * 80)\n vocab = ["the", "movie", "was", "good", "bad"]\n weights = [0.1, 0.2, 0.1, 2.0, -2.0]\n x = [1, 1, 1, 1, 0]\n z, y_hat, sig_grad, grads, attrs = compute_gradient_x_input(x, weights)\n print(f"Logit: {z:.2f}, Pred: {y_hat:.4f}")\n df = pd.DataFrame({\'Token\': vocab, \'Weight\': weights, \'Attribution\': attrs})\n print(df.to_string(index=False))\n'
4
+
5
+ def experiment_9(*args, **kwargs):
6
+ """Displays the complete Python code for Experiment 9."""
7
+ print(CODE_EXPERIMENT_9)
8
+
9
+ def tokenize(*args, **kwargs):
10
+ print(CODE_EXPERIMENT_9)
11
+
12
+ def build_vocabulary(*args, **kwargs):
13
+ print(CODE_EXPERIMENT_9)
14
+
15
+ def create_cbow_dataset(*args, **kwargs):
16
+ print(CODE_EXPERIMENT_9)
17
+
18
+ def softmax(*args, **kwargs):
19
+ print(CODE_EXPERIMENT_9)
20
+
21
+ def cosine_similarity(*args, **kwargs):
22
+ print(CODE_EXPERIMENT_9)
23
+
24
+ def SimpleCBOW(*args, **kwargs):
25
+ print(CODE_EXPERIMENT_9)
26
+
27
+ def sigmoid(*args, **kwargs):
28
+ print(CODE_EXPERIMENT_9)
29
+
30
+ def predict_sentiment(*args, **kwargs):
31
+ print(CODE_EXPERIMENT_9)
32
+
33
+ def compute_gradient_x_input(*args, **kwargs):
34
+ print(CODE_EXPERIMENT_9)
35
+
36
+ run_experiment_9 = experiment_9
37
+ exp9 = experiment_9
@@ -0,0 +1,94 @@
1
+ """
2
+ EXAI Command-Line Interface (CLI)
3
+ =================================
4
+ Allows running any of the 10 XAI laboratory experiments directly from terminal:
5
+ exai list
6
+ exai run 1
7
+ exai run all
8
+ """
9
+
10
+ import sys
11
+ import argparse
12
+ from exai.linear_logistic import run_experiment_1
13
+ from exai.tree_knn import run_experiment_2
14
+ from exai.shapley import run_experiment_3
15
+ from exai.pdp import run_experiment_4
16
+ from exai.ice import run_experiment_5
17
+ from exai.xrai import run_experiment_6
18
+ from exai.gradcam import run_experiment_7
19
+ from exai.lime import run_experiment_8
20
+ from exai.cbow_gradient import run_experiment_9
21
+ from exai.lrp import run_experiment_10
22
+
23
+ EXPERIMENTS = {
24
+ 1: ("Linear & Logistic Regression Explainability", run_experiment_1),
25
+ 2: ("Explainability of Decision Trees & KNN", run_experiment_2),
26
+ 3: ("Approximate Shapley Value for Explainable AI", run_experiment_3),
27
+ 4: ("PDP-Based Feature Importance", run_experiment_4),
28
+ 5: ("ICE (Individual Conditional Expectation) from Scratch", run_experiment_5),
29
+ 6: ("XRAI: Region-Based Image Attribution", run_experiment_6),
30
+ 7: ("Grad-CAM (Gradient-Weighted Class Activation Mapping)", run_experiment_7),
31
+ 8: ("LIME from Scratch using Weighted Least Squares", run_experiment_8),
32
+ 9: ("CBOW Word Embeddings & Gradient × Input Explainability", run_experiment_9),
33
+ 10: ("Layer-Wise Relevance Propagation (LRP) from Scratch", run_experiment_10),
34
+ }
35
+
36
+
37
+ def list_experiments():
38
+ print("=" * 80)
39
+ print("EXAI Toolkit: 10 Core Explainable AI (XAI) Laboratory Experiments")
40
+ print("=" * 80)
41
+ for num, (title, _) in EXPERIMENTS.items():
42
+ print(f" [{num:2d}] {title}")
43
+ print("=" * 80)
44
+ print("Run a specific experiment: exai run <1-10>")
45
+ print("Run all experiments : exai run all")
46
+
47
+
48
+ def main(argv=None):
49
+ if argv is None:
50
+ argv = sys.argv[1:]
51
+
52
+ parser = argparse.ArgumentParser(
53
+ prog="exai",
54
+ description="EXAI Toolkit: Core Explainable AI Algorithms & Experiments from Scratch"
55
+ )
56
+ subparsers = parser.add_subparsers(dest="command")
57
+
58
+ # Subcommand: list
59
+ subparsers.add_parser("list", help="List all available XAI experiments")
60
+
61
+ # Subcommand: run
62
+ run_parser = subparsers.add_parser("run", help="Run one or all experiments")
63
+ run_parser.add_argument("target", help="Experiment number (1-10) or 'all'")
64
+
65
+ args = parser.parse_args(argv)
66
+
67
+ if args.command == "list" or (args.command is None and len(argv) == 0):
68
+ list_experiments()
69
+ elif args.command == "run":
70
+ target = args.target.strip().lower()
71
+ if target == "all":
72
+ print("Running all 10 XAI experiments...")
73
+ for num in sorted(EXPERIMENTS.keys()):
74
+ title, fn = EXPERIMENTS[num]
75
+ fn()
76
+ print("\n")
77
+ else:
78
+ try:
79
+ exp_num = int(target)
80
+ if exp_num in EXPERIMENTS:
81
+ title, fn = EXPERIMENTS[exp_num]
82
+ fn()
83
+ else:
84
+ print(f"Error: Experiment {exp_num} not found. Choose between 1 and 10.")
85
+ sys.exit(1)
86
+ except ValueError:
87
+ print(f"Invalid experiment target: '{args.target}'. Use an integer 1-10 or 'all'.")
88
+ sys.exit(1)
89
+ else:
90
+ parser.print_help()
91
+
92
+
93
+ if __name__ == "__main__":
94
+ main()
@@ -0,0 +1,16 @@
1
+ """Experiment 7: Grad-CAM"""
2
+
3
+ CODE_EXPERIMENT_7 = '"""\nGrad-CAM: Gradient-Weighted Class Activation Mapping Module\n===========================================================\nGenerates class-discriminative localization heatmaps from convolutional feature maps.\n"""\n\nimport numpy as np\n\n\ndef compute_gradcam(feature_maps, gradients):\n """\n Computes Grad-CAM heatmap from scratch given feature maps and gradients.\n \n Parameters:\n - feature_maps: numpy array of shape (K, H, W)\n - gradients: numpy array of shape (K, H, W) representing d(y^c)/d(A)\n \n Returns:\n - alpha_weights: 1D array of shape (K,) with importance weights\n - weighted_sum: 2D array of shape (H, W) before ReLU\n - gradcam_heatmap: 2D array of shape (H, W) after ReLU\n - normalized_heatmap: 2D array of shape (H, W) scaled to [0, 1]\n """\n K, H, W = feature_maps.shape\n Z = H * W\n alpha_weights = np.sum(gradients, axis=(1, 2)) / Z\n \n weighted_sum = np.zeros((H, W), dtype=float)\n for k in range(K):\n weighted_sum += alpha_weights[k] * feature_maps[k]\n \n gradcam_heatmap = np.maximum(0.0, weighted_sum)\n max_val = np.max(gradcam_heatmap)\n min_val = np.min(gradcam_heatmap)\n if max_val > min_val:\n normalized_heatmap = (gradcam_heatmap - min_val) / (max_val - min_val)\n else:\n normalized_heatmap = np.zeros_like(gradcam_heatmap)\n \n return alpha_weights, weighted_sum, gradcam_heatmap, normalized_heatmap\n\n\ndef print_ascii_heatmap(heatmap, title="Grad-CAM Heatmap"):\n """Renders a 2D heatmap in ASCII blocks for visual inspection."""\n print(f"\\n{title}:")\n H, W = heatmap.shape\n chars = [\' \', \'░░\', \'▒▒\', \'▓▓\', \'██\']\n for r in range(H):\n row_str = ""\n for c in range(W):\n val = heatmap[r, c]\n idx = min(int(val * len(chars)), len(chars) - 1)\n row_str += chars[idx] + " "\n num_str = " ".join(f"{heatmap[r, c]:.2f}" for c in range(W))\n print(f" [{row_str.strip()}] | Values: [ {num_str} ]")\n\n\ndef run_experiment_7():\n print("=" * 80)\n print("EXPERIMENT 7: GRAD-CAM (GRADIENT-WEIGHTED CLASS ACTIVATION MAPPING)")\n print("=" * 80)\n K, H, W = 2, 4, 4\n feature_maps = np.zeros((K, H, W))\n feature_maps[0, :, :2] = 1.0 # Left object\n feature_maps[1, :, 2:] = 1.0 # Right object\n \n # Target Class 1 (Left object)\n grads_left = np.zeros((K, H, W))\n grads_left[0] = 1.0\n _, _, _, norm_hm = compute_gradcam(feature_maps, grads_left)\n print_ascii_heatmap(norm_hm, "Heatmap for Left Object Target")\n'
4
+
5
+ def experiment_7(*args, **kwargs):
6
+ """Displays the complete Python code for Experiment 7."""
7
+ print(CODE_EXPERIMENT_7)
8
+
9
+ def compute_gradcam(*args, **kwargs):
10
+ print(CODE_EXPERIMENT_7)
11
+
12
+ def print_ascii_heatmap(*args, **kwargs):
13
+ print(CODE_EXPERIMENT_7)
14
+
15
+ run_experiment_7 = experiment_7
16
+ exp7 = experiment_7
@@ -0,0 +1,16 @@
1
+ """Experiment 5: Individual Conditional Expectation (ICE) Plot"""
2
+
3
+ CODE_EXPERIMENT_5 = '"""\nIndividual Conditional Expectation (ICE) Module\n===============================================\nImplements ICE curves and ASCII curve visualization from scratch.\n"""\n\nimport numpy as np\nimport pandas as pd\n\n\ndef compute_ice_scratch(dataset, target_feature_idx, grid_values, predict_fn):\n """\n Computes Individual Conditional Expectation (ICE) matrix and PDP average.\n Returns:\n - ice_matrix: shape (len(grid_values), len(dataset))\n - pdp_curve: shape (len(grid_values),)\n """\n n_samples = len(dataset)\n n_grid = len(grid_values)\n ice_matrix = np.zeros((n_grid, n_samples))\n \n for row_idx, val in enumerate(grid_values):\n for col_idx, sample in enumerate(dataset):\n modified_sample = list(sample)\n modified_sample[target_feature_idx] = val\n pred = predict_fn(*modified_sample)\n ice_matrix[row_idx, col_idx] = pred\n \n pdp_curve = np.mean(ice_matrix, axis=1)\n return ice_matrix, pdp_curve\n\n\ndef ascii_ice_plot(grid_values, ice_matrix, pdp_curve, feature_name="X1"):\n """Draws an ASCII representation of ICE curves and PDP average."""\n print(f"\\n--- VISUALIZATION: ICE Plot for {feature_name} ---")\n print(f"{\'Feature \' + feature_name:<10} | {\'Sample 1\':<10} | {\'Sample 2\':<10} | {\'Sample 3\':<10} | {\'PDP (Average)\':<14} | Curve Visualization")\n print("-" * 80)\n for i, g in enumerate(grid_values):\n s1 = ice_matrix[i, 0]\n s2 = ice_matrix[i, 1]\n s3 = ice_matrix[i, 2]\n pdp = pdp_curve[i]\n bar = "█" * max(1, int((pdp - 20) * 1.5))\n print(f"{g:<10.1f} | {s1:<10.1f} | {s2:<10.1f} | {s3:<10.1f} | {pdp:<14.1f} | {bar}")\n\n\ndef run_experiment_5():\n print("=" * 80)\n print("EXPERIMENT 5: IMPLEMENTING AN ICE PLOT FROM SCRATCH")\n print("=" * 80)\n dataset = [[5, 2], [6, 3], [7, 4]]\n grid = [5, 6, 7]\n def model(x1, x2): return 3.5 + 3.0 * x1 + 4.0 * x2\n ice_mat, pdp = compute_ice_scratch(dataset, 0, grid, model)\n ascii_ice_plot(grid, ice_mat, pdp)\n'
4
+
5
+ def experiment_5(*args, **kwargs):
6
+ """Displays the complete Python code for Experiment 5."""
7
+ print(CODE_EXPERIMENT_5)
8
+
9
+ def compute_ice_scratch(*args, **kwargs):
10
+ print(CODE_EXPERIMENT_5)
11
+
12
+ def ascii_ice_plot(*args, **kwargs):
13
+ print(CODE_EXPERIMENT_5)
14
+
15
+ run_experiment_5 = experiment_5
16
+ exp5 = experiment_5
@@ -0,0 +1,68 @@
1
+ """
2
+ EXAI Toolkit: Index of Experiments
3
+ ==================================
4
+ Calling index() or running this file displays the index of all 10 XAI experiments.
5
+ """
6
+
7
+ INDEX_TEXT = r"""================================================================================
8
+ EXAI Toolkit: Index of All 10 Laboratory Experiments
9
+ ================================================================================
10
+
11
+ [ 1] Linear & Logistic Regression Explainability
12
+ Topics : R², Adjusted R², t-statistic, 95% CI, Odds Ratio, Feature Ranking
13
+ Function : exai.exp1() or exai.get_code(1) or python experiment_1.py
14
+
15
+ [ 2] Explainability of Decision Trees & K-Nearest Neighbors (KNN)
16
+ Topics : Tree Rules (IF-THEN), Impurity (MDI), Permutation Importance, KNN
17
+ Function : exai.exp2() or exai.get_code(2) or python experiment_2.py
18
+
19
+ [ 3] Approximate Shapley Value for Explainable AI
20
+ Topics : Permutation Sampling from Scratch, Marginal Contributions, Efficiency
21
+ Function : exai.exp3() or exai.get_code(3) or python experiment_3.py
22
+
23
+ [ 4] Partial Dependence Plot (PDP) Feature Importance
24
+ Topics : Greenwell et al. Formulation, Sample Std Dev, Range/4 Categorical
25
+ Function : exai.exp4() or exai.get_code(4) or python experiment_4.py
26
+
27
+ [ 5] Individual Conditional Expectation (ICE) Plot from Scratch
28
+ Topics : Individual Prediction Curves, PDP Average Relationship, Slopes
29
+ Function : exai.exp5() or exai.get_code(5) or python experiment_5.py
30
+
31
+ [ 6] XRAI — Region-Based Image Attribution
32
+ Topics : Fixed Region Quadrants, Pixel Attribution Aggregation, Ranking
33
+ Function : exai.exp6() or exai.get_code(6) or python experiment_6.py
34
+
35
+ [ 7] Grad-CAM (Gradient-Weighted Class Activation Mapping)
36
+ Topics : Convolutional Feature Maps, Gradient Global Pooling, ReLU Heatmap
37
+ Function : exai.exp7() or exai.get_code(7) or python experiment_7.py
38
+
39
+ [ 8] LIME from Scratch using Weighted Least Squares (WLS)
40
+ Topics : Local Perturbations, Exponential Kernel Weights, Normal Equations
41
+ Function : exai.exp8() or exai.get_code(8) or python experiment_8.py
42
+
43
+ [ 9] CBOW Word Embeddings & Gradient × Input Text Attribution
44
+ Topics : CBOW from Scratch, Softmax, Cosine Similarity, Sigmoid Attribution
45
+ Function : exai.exp9() or exai.get_code(9) or python experiment_9.py
46
+
47
+ [10] Layer-Wise Relevance Propagation (LRP) from Scratch
48
+ Topics : Two-Layer ANN (ReLU, Sigmoid), LRP-epsilon Rule, Relevance Conservation
49
+ Function : exai.exp10() or exai.get_code(10) or python experiment_10.py
50
+
51
+ ================================================================================
52
+ Quick Usage:
53
+ import exai
54
+ exai.index() # Displays this index
55
+ exai.get_code(8) # Displays Experiment 8 code
56
+ exai.exp8() # Displays Experiment 8 code
57
+ ================================================================================
58
+ """
59
+
60
+ def index(*args, **kwargs):
61
+ """Displays the index of all 10 XAI experiments."""
62
+ print(INDEX_TEXT)
63
+
64
+ show_index = index
65
+ list_all = index
66
+
67
+ if __name__ == "__main__":
68
+ index()
@@ -0,0 +1,19 @@
1
+ """Experiment 8: LIME from Scratch using Weighted Least Squares"""
2
+
3
+ CODE_EXPERIMENT_8 = '"""\nLocal Interpretable Model-Agnostic Explanations (LIME) Module\n=============================================================\nImplements LIME from scratch using Weighted Least Squares (WLS) and exponential kernel.\n"""\n\nimport numpy as np\nimport pandas as pd\n\n\ndef compute_euclidean_distance(x, z):\n """Euclidean distance between original instance x and perturbation z."""\n return np.linalg.norm(np.array(x) - np.array(z))\n\n\ndef exponential_kernel(distance, sigma=1.0):\n """Exponential similarity kernel: w = exp(-d^2 / sigma^2)."""\n return np.exp(- (distance ** 2) / (sigma ** 2))\n\n\ndef fit_lime_surrogate(perturbations, predictions, original_instance, sigma=1.0, feature_names=None):\n """\n Fits a local linear surrogate model via Weighted Least Squares (WLS).\n Solves: beta = (X^T * W * X)^(-1) * X^T * W * y\n """\n N = len(perturbations)\n p = len(original_instance)\n if feature_names is None:\n feature_names = [f"Feature_{j+1}" for j in range(p)]\n \n distances = np.array([compute_euclidean_distance(original_instance, z) for z in perturbations])\n weights = np.array([exponential_kernel(d, sigma=sigma) for d in distances])\n \n X = np.hstack([np.ones((N, 1)), np.array(perturbations)])\n W = np.diag(weights)\n y = np.array(predictions).reshape(-1, 1)\n \n XTW = X.T @ W\n XTWX = XTW @ X\n XTWy = XTW @ y\n \n beta = np.linalg.solve(XTWX, XTWy).flatten()\n intercept = beta[0]\n coefficients = beta[1:]\n \n coef_df = pd.DataFrame({\n \'Feature\': feature_names,\n \'Coefficient (Local Importance)\': coefficients,\n \'Abs Value\': np.abs(coefficients),\n \'Sign\': [\'Positive (+)\' if c > 0 else \'Negative (-)\' for c in coefficients]\n })\n coef_df[\'Local Rank\'] = coef_df[\'Abs Value\'].rank(ascending=False, method=\'min\').astype(int)\n coef_df = coef_df.sort_values(by=\'Local Rank\').reset_index(drop=True)\n \n perturbations_df = pd.DataFrame(perturbations, columns=feature_names)\n perturbations_df[\'f(z) Prediction\'] = predictions\n perturbations_df[\'Distance\'] = distances\n perturbations_df[\'Similarity Weight\'] = weights\n \n return {\n \'intercept\': intercept,\n \'coefficients\': coefficients,\n \'beta\': beta,\n \'XTWX\': XTWX,\n \'XTWy\': XTWy,\n \'explanation_df\': coef_df,\n \'perturbations_df\': perturbations_df\n }\n\n\ndef run_experiment_8():\n print("=" * 80)\n print("EXPERIMENT 8: LIME FROM SCRATCH USING WEIGHTED LEAST SQUARES")\n print("=" * 80)\n x_orig = [1, 1]\n perturbations = [[1, 1], [1, 0], [0, 1], [0, 0]]\n predictions = [0.90, 0.70, 0.60, 0.20]\n names = [\'Blood Pressure\', \'Cholesterol\']\n res = fit_lime_surrogate(perturbations, predictions, x_orig, sigma=1.0, feature_names=names)\n print(res[\'explanation_df\'].to_string(index=False))\n'
4
+
5
+ def experiment_8(*args, **kwargs):
6
+ """Displays the complete Python code for Experiment 8."""
7
+ print(CODE_EXPERIMENT_8)
8
+
9
+ def compute_euclidean_distance(*args, **kwargs):
10
+ print(CODE_EXPERIMENT_8)
11
+
12
+ def exponential_kernel(*args, **kwargs):
13
+ print(CODE_EXPERIMENT_8)
14
+
15
+ def fit_lime_surrogate(*args, **kwargs):
16
+ print(CODE_EXPERIMENT_8)
17
+
18
+ run_experiment_8 = experiment_8
19
+ exp8 = experiment_8
@@ -0,0 +1,16 @@
1
+ """Experiment 1: Linear and Logistic Regression Explainability"""
2
+
3
+ CODE_EXPERIMENT_1 = '"""\nLinear and Logistic Regression Explainability Module\n====================================================\nImplements:\n- Linear Regression evaluation: R², Adjusted R², t-statistic, 95% Confidence Intervals\n- Logistic Regression coefficient interpretation: Odds Ratio, % change in odds, ranking\n"""\n\nimport math\nimport numpy as np\nimport pandas as pd\n\n\ndef compute_linear_regression_metrics(y_true, y_pred, coefficients, standard_errors, feature_names, intercept=0.0):\n """\n Computes Linear Regression evaluation metrics:\n - SSE, SST, R², Adjusted R²\n - t-statistic for each coefficient (t = coef / SE)\n - 95% Confidence Interval for each coefficient (coef +/- 1.96 * SE)\n - Feature importance ranking based on absolute t-statistic\n """\n y_true = np.array(y_true, dtype=float)\n y_pred = np.array(y_pred, dtype=float)\n coefficients = np.array(coefficients, dtype=float)\n standard_errors = np.array(standard_errors, dtype=float)\n \n n = len(y_true)\n p = len(coefficients)\n \n sse = np.sum((y_true - y_pred) ** 2)\n y_mean = np.mean(y_true)\n sst = np.sum((y_true - y_mean) ** 2)\n r_squared = 1.0 - (sse / sst)\n \n if n - p - 1 > 0:\n adj_r_squared = 1.0 - ((1.0 - r_squared) * (n - 1) / (n - p - 1))\n else:\n adj_r_squared = float(\'nan\')\n \n t_statistics = coefficients / standard_errors\n z_critical = 1.96\n ci_lower = coefficients - (z_critical * standard_errors)\n ci_upper = coefficients + (z_critical * standard_errors)\n \n results_df = pd.DataFrame({\n \'Feature\': feature_names,\n \'Coefficient\': coefficients,\n \'Std Error\': standard_errors,\n \'t-Statistic\': t_statistics,\n \'95% CI Lower\': ci_lower,\n \'95% CI Upper\': ci_upper,\n \'Significant (95%)\': [not (low <= 0 <= high) for low, high in zip(ci_lower, ci_upper)],\n \'Absolute t-stat\': np.abs(t_statistics)\n })\n \n results_df[\'Importance Rank\'] = results_df[\'Absolute t-stat\'].rank(ascending=False, method=\'min\').astype(int)\n results_df = results_df.sort_values(by=\'Importance Rank\').reset_index(drop=True)\n \n return {\n \'n_observations\': n,\n \'n_predictors\': p,\n \'SSE\': sse,\n \'SST\': sst,\n \'R_squared\': r_squared,\n \'Adjusted_R_squared\': adj_r_squared,\n \'coefficients_table\': results_df\n }\n\n\ndef interpret_logistic_regression(features, coefficients, intercept=0.0):\n """\n Interprets Logistic Regression coefficients:\n - Sign: positive increases odds of positive class, negative decreases odds\n - Odds Ratio: exp(beta)\n - Percentage change in odds: (OR - 1) * 100%\n - Ranking based on coefficient magnitude\n """\n rows = []\n for feat, beta in zip(features, coefficients):\n odds_ratio = math.exp(beta)\n pct_change = (odds_ratio - 1.0) * 100.0\n direction = "Positive" if beta > 0 else ("Negative" if beta < 0 else "Neutral")\n \n if beta > 0:\n interpretation = (f"Every 1-unit increase in \'{feat}\' multiplies odds by {odds_ratio:.2f} "\n f"(increases odds by {pct_change:.1f}%).")\n elif beta < 0:\n interpretation = (f"Every 1-unit increase in \'{feat}\' multiplies odds by {odds_ratio:.2f} "\n f"(decreases odds by {abs(pct_change):.1f}%).")\n else:\n interpretation = f"\'{feat}\' has no effect on the odds (OR = 1.0)."\n \n rows.append({\n \'Feature\': feat,\n \'Coefficient (Beta)\': beta,\n \'Abs(Beta)\': abs(beta),\n \'Direction\': direction,\n \'Odds Ratio\': odds_ratio,\n \'Pct Change in Odds (%)\': pct_change,\n \'Interpretation\': interpretation\n })\n \n df = pd.DataFrame(rows)\n df[\'Influence Rank\'] = df[\'Abs(Beta)\'].rank(ascending=False, method=\'min\').astype(int)\n df = df.sort_values(by=\'Influence Rank\').reset_index(drop=True)\n return df\n\n\ndef run_experiment_1():\n print("=" * 80)\n print("EXPERIMENT 1: LINEAR & LOGISTIC REGRESSION EXPLAINABILITY")\n print("=" * 80)\n feature_names = [\'Size (100 sqft)\', \'Bedrooms\', \'Age (years)\']\n coefficients = [18.5, 7.2, -1.8]\n standard_errors = [2.1, 4.0, 1.2]\n y_true = [250, 310, 190, 400, 220, 280, 350, 175, 420, 300]\n y_pred = [245, 305, 198, 390, 228, 272, 342, 180, 412, 308]\n \n lin_reg = compute_linear_regression_metrics(y_true, y_pred, coefficients, standard_errors, feature_names)\n print(f"R²: {lin_reg[\'R_squared\']:.4f}, Adj R²: {lin_reg[\'Adjusted_R_squared\']:.4f}")\n print(lin_reg[\'coefficients_table\'].to_string(index=False))\n \n print("\\nLogistic Regression Interpretation:")\n log_features = [\'Age\', \'Income\', \'Loan Amount\', \'Credit Score\']\n log_coefficients = [0.06, 0.85, -0.40, 1.25]\n log_df = interpret_logistic_regression(log_features, log_coefficients)\n print(log_df[[\'Feature\', \'Coefficient (Beta)\', \'Odds Ratio\', \'Pct Change in Odds (%)\', \'Influence Rank\']].to_string(index=False))\n'
4
+
5
+ def experiment_1(*args, **kwargs):
6
+ """Displays the complete Python code for Experiment 1."""
7
+ print(CODE_EXPERIMENT_1)
8
+
9
+ def compute_linear_regression_metrics(*args, **kwargs):
10
+ print(CODE_EXPERIMENT_1)
11
+
12
+ def interpret_logistic_regression(*args, **kwargs):
13
+ print(CODE_EXPERIMENT_1)
14
+
15
+ run_experiment_1 = experiment_1
16
+ exp1 = experiment_1
@@ -0,0 +1,22 @@
1
+ """Experiment 10: Layer-Wise Relevance Propagation (LRP)"""
2
+
3
+ CODE_EXPERIMENT_10 = '"""\nLayer-Wise Relevance Propagation (LRP) Module\n=============================================\nImplements LRP for a two-layer ANN with ReLU and sigmoid from scratch.\n"""\n\nimport math\nimport pandas as pd\n\n\ndef relu(a):\n """ReLU activation function: max(0, a)."""\n return max(0.0, float(a))\n\n\ndef sigmoid(s):\n """Sigmoid activation function: 1 / (1 + exp(-s))."""\n return 1.0 / (1.0 + math.exp(-s))\n\n\ndef forward_propagation(x1, x2, w11=1, w21=0, w12=0, w22=2, v1=1, v2=1, bias_h1=0, bias_h2=0, bias_o=0):\n """Forward Propagation through 2-layer ANN."""\n z1 = x1 * w11 + x2 * w21 + bias_h1\n h1 = relu(z1)\n \n z2 = x1 * w12 + x2 * w22 + bias_h2\n h2 = relu(z2)\n \n s = h1 * v1 + h2 * v2 + bias_o\n prob = sigmoid(s)\n \n return {\n \'x1\': x1, \'x2\': x2,\n \'z1\': z1, \'h1\': h1,\n \'z2\': z2, \'h2\': h2,\n \'s\': s, \'prob\': prob\n }\n\n\ndef layerwise_relevance_propagation(fwd, w11=1, w21=0, w12=0, w22=2, v1=1, v2=1):\n """LRP Backward propagation from output logit down to input features."""\n x1, x2 = fwd[\'x1\'], fwd[\'x2\']\n h1, h2 = fwd[\'h1\'], fwd[\'h2\']\n s = fwd[\'s\']\n R_output = s\n \n denom_output = h1 * v1 + h2 * v2\n if denom_output != 0:\n R_h1 = (h1 * v1 / denom_output) * R_output\n R_h2 = (h2 * v2 / denom_output) * R_output\n else:\n R_h1, R_h2 = 0.0, 0.0\n \n denom_h1 = x1 * w11 + x2 * w21\n if denom_h1 != 0 and R_h1 != 0:\n R_x1_from_h1 = (x1 * w11 / denom_h1) * R_h1\n R_x2_from_h1 = (x2 * w21 / denom_h1) * R_h1\n else:\n R_x1_from_h1, R_x2_from_h1 = 0.0, 0.0\n \n denom_h2 = x1 * w12 + x2 * w22\n if denom_h2 != 0 and R_h2 != 0:\n R_x1_from_h2 = (x1 * w12 / denom_h2) * R_h2\n R_x2_from_h2 = (x2 * w22 / denom_h2) * R_h2\n else:\n R_x1_from_h2, R_x2_from_h2 = 0.0, 0.0\n \n R_x1 = R_x1_from_h1 + R_x1_from_h2\n R_x2 = R_x2_from_h1 + R_x2_from_h2\n \n conserved_hidden = math.isclose(R_h1 + R_h2, R_output, abs_tol=1e-5)\n conserved_input = math.isclose(R_x1 + R_x2, R_output, abs_tol=1e-5)\n \n return {\n \'R_output\': R_output,\n \'R_h1\': R_h1,\n \'R_h2\': R_h2,\n \'R_x1\': R_x1,\n \'R_x2\': R_x2,\n \'conserved_hidden\': conserved_hidden,\n \'conserved_input\': conserved_input\n }\n\n\ndef run_experiment_10():\n print("=" * 80)\n print("EXPERIMENT 10: LAYER-WISE RELEVANCE PROPAGATION (LRP) FROM SCRATCH")\n print("=" * 80)\n fwd = forward_propagation(x1=1, x2=1)\n lrp = layerwise_relevance_propagation(fwd)\n print(f"Logit s: {fwd[\'s\']:.1f}, Prob: {fwd[\'prob\']:.4f}")\n print(f"Input Relevance: R_x1 = {lrp[\'R_x1\']:.1f}, R_x2 = {lrp[\'R_x2\']:.1f}")\n print(f"Relevance Conserved: {lrp[\'conserved_input\']}")\n'
4
+
5
+ def experiment_10(*args, **kwargs):
6
+ """Displays the complete Python code for Experiment 10."""
7
+ print(CODE_EXPERIMENT_10)
8
+
9
+ def relu(*args, **kwargs):
10
+ print(CODE_EXPERIMENT_10)
11
+
12
+ def sigmoid(*args, **kwargs):
13
+ print(CODE_EXPERIMENT_10)
14
+
15
+ def forward_propagation(*args, **kwargs):
16
+ print(CODE_EXPERIMENT_10)
17
+
18
+ def layerwise_relevance_propagation(*args, **kwargs):
19
+ print(CODE_EXPERIMENT_10)
20
+
21
+ run_experiment_10 = experiment_10
22
+ exp10 = experiment_10
@@ -0,0 +1,19 @@
1
+ """Experiment 4: PDP-Based Feature Importance"""
2
+
3
+ CODE_EXPERIMENT_4 = '"""\nPartial Dependence Plot (PDP) Feature Importance Module\n=======================================================\nImplements PDP and PDP-based importance following Greenwell, Boehmke, & McCarthy (2018).\n"""\n\nimport math\n\n\ndef compute_pdp(dataset, feature_index, grid_values, predict_fn):\n """Computes Partial Dependence Plot (PDP) values from scratch."""\n n = len(dataset)\n pdp_values = []\n \n for val in grid_values:\n total_pred = 0.0\n for sample in dataset:\n modified_sample = list(sample)\n modified_sample[feature_index] = val\n total_pred += predict_fn(modified_sample)\n pdp_values.append(total_pred / n)\n \n return pdp_values\n\n\ndef compute_numerical_pdp_importance(pdp_values):\n """Computes sample standard deviation across K PDP values."""\n K = len(pdp_values)\n if K <= 1:\n return "Numerical PDP importance cannot be calculated when there is only one unique value."\n mean_pdp = sum(pdp_values) / K\n sum_sq_diff = sum((val - mean_pdp) ** 2 for val in pdp_values)\n variance = sum_sq_diff / (K - 1)\n return math.sqrt(variance)\n\n\ndef compute_categorical_pdp_importance(pdp_values):\n """Computes categorical importance: (max(PDP) - min(PDP)) / 4."""\n if len(pdp_values) == 0:\n return 0.0\n return (max(pdp_values) - min(pdp_values)) / 4.0\n\n\ndef run_experiment_4():\n print("=" * 80)\n print("EXPERIMENT 4: PDP-BASED FEATURE IMPORTANCE")\n print("=" * 80)\n tc2 = [0.20, 0.30, 0.45, 0.60, 0.75]\n tc3 = [0.25, 0.40, 0.60, 0.70]\n print(f"Numerical Test Case: PDP = {tc2} -> Importance = {compute_numerical_pdp_importance(tc2):.4f}")\n print(f"Categorical Test Case: PDP = {tc3} -> Importance = {compute_categorical_pdp_importance(tc3):.4f}")\n'
4
+
5
+ def experiment_4(*args, **kwargs):
6
+ """Displays the complete Python code for Experiment 4."""
7
+ print(CODE_EXPERIMENT_4)
8
+
9
+ def compute_pdp(*args, **kwargs):
10
+ print(CODE_EXPERIMENT_4)
11
+
12
+ def compute_numerical_pdp_importance(*args, **kwargs):
13
+ print(CODE_EXPERIMENT_4)
14
+
15
+ def compute_categorical_pdp_importance(*args, **kwargs):
16
+ print(CODE_EXPERIMENT_4)
17
+
18
+ run_experiment_4 = experiment_4
19
+ exp4 = experiment_4
@@ -0,0 +1,19 @@
1
+ """Experiment 3: Approximate Shapley Value for Explainable AI"""
2
+
3
+ CODE_EXPERIMENT_3 = '"""\nApproximate Shapley Value Module\n================================\nImplements permutation-sampling approximation of the Shapley value from scratch.\n"""\n\nimport random\n\n\ndef linear_model(x):\n """Additive linear model from manual: f(x1, x2, x3) = 2*x1 + 3*x2 + 5*x3."""\n return 2 * x[0] + 3 * x[1] + 5 * x[2]\n\n\ndef compute_single_permutation_marginal_contribution(x, z, permutation, target_feature_idx, model):\n """Computes marginal contribution for a specific permutation ordering."""\n current_vector = list(z)\n before_vector = None\n after_vector = None\n \n for feat_idx in permutation:\n if feat_idx == target_feature_idx:\n before_vector = list(current_vector)\n current_vector[feat_idx] = x[feat_idx]\n after_vector = list(current_vector)\n break\n else:\n current_vector[feat_idx] = x[feat_idx]\n \n pred_before = model(before_vector)\n pred_after = model(after_vector)\n marginal_contrib = pred_after - pred_before\n \n return before_vector, after_vector, pred_before, pred_after, marginal_contrib\n\n\ndef approximate_shapley_sampling(x, background_dataset, model, M=1000, random_seed=42):\n """Approximates Shapley values for all features using permutation sampling over M iterations."""\n random.seed(random_seed)\n p = len(x)\n marginal_contributions = {i: [] for i in range(p)}\n first_10_iterations = []\n \n for m in range(M):\n z = random.choice(background_dataset)\n perm = list(range(p))\n random.shuffle(perm)\n \n iter_row = {\'iteration\': m + 1, \'z\': tuple(z), \'perm\': [f"X{k+1}" for k in perm]}\n \n for feat_idx in range(p):\n _, _, _, _, contrib = compute_single_permutation_marginal_contribution(\n x, z, perm, feat_idx, model\n )\n marginal_contributions[feat_idx].append(contrib)\n iter_row[f"contrib_X{feat_idx+1}"] = contrib\n \n if m < 10:\n first_10_iterations.append(iter_row)\n \n approx_shapley = {feat_idx: sum(vals) / len(vals) for feat_idx, vals in marginal_contributions.items()}\n return approx_shapley, first_10_iterations\n\n\ndef run_experiment_3():\n print("=" * 80)\n print("EXPERIMENT 3: APPROXIMATE SHAPLEY VALUE FOR EXPLAINABLE AI")\n print("=" * 80)\n x = [4, 2, 3]\n bg = [[5, 1, 4], [2, 5, 1], [3, 3, 2], [4, 1, 5], [1, 4, 3]]\n shap_vals, _ = approximate_shapley_sampling(x, bg, linear_model, M=1000)\n print("Estimated Shapley Values (M=1000):")\n for feat_idx, val in shap_vals.items():\n print(f" X{feat_idx+1}: {val:.4f}")\n'
4
+
5
+ def experiment_3(*args, **kwargs):
6
+ """Displays the complete Python code for Experiment 3."""
7
+ print(CODE_EXPERIMENT_3)
8
+
9
+ def linear_model(*args, **kwargs):
10
+ print(CODE_EXPERIMENT_3)
11
+
12
+ def compute_single_permutation_marginal_contribution(*args, **kwargs):
13
+ print(CODE_EXPERIMENT_3)
14
+
15
+ def approximate_shapley_sampling(*args, **kwargs):
16
+ print(CODE_EXPERIMENT_3)
17
+
18
+ run_experiment_3 = experiment_3
19
+ exp3 = experiment_3
@@ -0,0 +1,22 @@
1
+ """Experiment 2: Decision Tree and KNN Explainability"""
2
+
3
+ CODE_EXPERIMENT_2 = '"""\nDecision Tree and K-Nearest Neighbors Explainability Module\n===========================================================\nImplements:\n- Decision Tree rule extraction, MDI importance, permutation importance\n- KNN classification explanation (distances, neighbors, majority voting)\n- KNN regression explanation (distances, neighbors, local manual averaging)\n"""\n\nimport numpy as np\nimport pandas as pd\nfrom sklearn.datasets import load_iris\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.tree import DecisionTreeClassifier, export_text\nfrom sklearn.neighbors import KNeighborsClassifier, KNeighborsRegressor\nfrom sklearn.metrics import accuracy_score\n\n\ndef extract_if_then_rules(tree_classifier, feature_names, class_names):\n """Traverses tree structure and extracts human-readable IF-THEN rules."""\n tree = tree_classifier.tree_\n rules = []\n \n def recurse(node_id, current_rule):\n if tree.children_left[node_id] == -1 and tree.children_right[node_id] == -1:\n class_idx = np.argmax(tree.value[node_id][0])\n predicted_class = class_names[class_idx]\n samples = tree.n_node_samples[node_id]\n gini = tree.impurity[node_id]\n rule_str = " AND ".join(current_rule) if current_rule else "TRUE"\n rules.append({\n \'Rule\': f"IF {rule_str} THEN Predict: {predicted_class}",\n \'Predicted Class\': predicted_class,\n \'Samples\': samples,\n \'Gini Impurity\': round(gini, 4)\n })\n return\n \n feature_idx = tree.feature[node_id]\n threshold = tree.threshold[node_id]\n feat_name = feature_names[feature_idx]\n \n left_cond = f"{feat_name} <= {threshold:.2f}"\n recurse(tree.children_left[node_id], current_rule + [left_cond])\n right_cond = f"{feat_name} > {threshold:.2f}"\n recurse(tree.children_right[node_id], current_rule + [right_cond])\n \n recurse(0, [])\n return pd.DataFrame(rules)\n\n\ndef compute_permutation_importance(model, X_val, y_val, feature_names, n_repeats=10, random_state=42):\n """Computes Permutation Feature Importance by measuring drop in accuracy."""\n rng = np.random.RandomState(random_state)\n baseline_acc = accuracy_score(y_val, model.predict(X_val))\n \n perm_importances = []\n for col_idx, col_name in enumerate(feature_names):\n drops = []\n for _ in range(n_repeats):\n X_permuted = X_val.copy()\n shuffled_col = X_permuted[:, col_idx].copy()\n rng.shuffle(shuffled_col)\n X_permuted[:, col_idx] = shuffled_col\n \n permuted_acc = accuracy_score(y_val, model.predict(X_permuted))\n drop = baseline_acc - permuted_acc\n drops.append(drop)\n \n perm_importances.append({\n \'Feature\': col_name,\n \'Permutation Importance (Mean Drop)\': np.mean(drops),\n \'Std Dev\': np.std(drops)\n })\n \n df = pd.DataFrame(perm_importances)\n df = df.sort_values(by=\'Permutation Importance (Mean Drop)\', ascending=False).reset_index(drop=True)\n return baseline_acc, df\n\n\ndef explain_knn_classification(X_train, y_train, test_instance, class_names, k=3):\n """Step-by-step local explanation of KNN Classification."""\n distances = np.linalg.norm(X_train - test_instance, axis=1)\n nearest_indices = np.argsort(distances)[:k]\n \n neighbor_records = []\n for rank, idx in enumerate(nearest_indices, start=1):\n neighbor_records.append({\n \'Rank\': rank,\n \'Train Index\': idx,\n \'Distance\': distances[idx],\n \'Class Label\': y_train[idx],\n \'Class Name\': class_names[y_train[idx]],\n \'Features\': X_train[idx]\n })\n df_neighbors = pd.DataFrame(neighbor_records)\n votes = df_neighbors[\'Class Name\'].value_counts()\n predicted_class = votes.index[0]\n \n return {\n \'test_instance\': test_instance,\n \'k\': k,\n \'neighbors_df\': df_neighbors,\n \'vote_counts\': votes.to_dict(),\n \'predicted_class\': predicted_class\n }\n\n\ndef explain_knn_regression(X_train, y_train, test_instance, k=3):\n """Step-by-step local explanation of KNN Regression."""\n distances = np.linalg.norm(X_train - test_instance, axis=1)\n nearest_indices = np.argsort(distances)[:k]\n \n neighbor_records = []\n for rank, idx in enumerate(nearest_indices, start=1):\n neighbor_records.append({\n \'Rank\': rank,\n \'Train Index\': idx,\n \'Distance\': distances[idx],\n \'Target Value\': y_train[idx],\n \'Features\': X_train[idx]\n })\n df_neighbors = pd.DataFrame(neighbor_records)\n manual_avg = np.mean(df_neighbors[\'Target Value\'])\n \n return {\n \'test_instance\': test_instance,\n \'k\': k,\n \'neighbors_df\': df_neighbors,\n \'manual_average\': manual_avg\n }\n\n\ndef run_experiment_2():\n print("=" * 80)\n print("EXPERIMENT 2: EXPLAINABILITY OF DECISION TREES & KNN")\n print("=" * 80)\n iris = load_iris()\n X, y = iris.data, iris.target\n X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42, stratify=y)\n \n tree_clf = DecisionTreeClassifier(max_depth=3, random_state=42)\n tree_clf.fit(X_train, y_train)\n rules_df = extract_if_then_rules(tree_clf, iris.feature_names, iris.target_names)\n print("Extracted IF-THEN Rules:")\n print(rules_df[[\'Rule\', \'Samples\', \'Gini Impurity\']].to_string(index=False))\n \n knn_clf = KNeighborsClassifier(n_neighbors=3)\n knn_clf.fit(X_train, y_train)\n clf_exp = explain_knn_classification(X_train, y_train, X_test[0], iris.target_names, k=3)\n print("\\nKNN Local Explanation:")\n print(clf_exp[\'neighbors_df\'][[\'Rank\', \'Train Index\', \'Distance\', \'Class Name\']].to_string(index=False))\n'
4
+
5
+ def experiment_2(*args, **kwargs):
6
+ """Displays the complete Python code for Experiment 2."""
7
+ print(CODE_EXPERIMENT_2)
8
+
9
+ def extract_if_then_rules(*args, **kwargs):
10
+ print(CODE_EXPERIMENT_2)
11
+
12
+ def compute_permutation_importance(*args, **kwargs):
13
+ print(CODE_EXPERIMENT_2)
14
+
15
+ def explain_knn_classification(*args, **kwargs):
16
+ print(CODE_EXPERIMENT_2)
17
+
18
+ def explain_knn_regression(*args, **kwargs):
19
+ print(CODE_EXPERIMENT_2)
20
+
21
+ run_experiment_2 = experiment_2
22
+ exp2 = experiment_2
@@ -0,0 +1,16 @@
1
+ """Experiment 6: XRAI — Region-Based Image Attribution"""
2
+
3
+ CODE_EXPERIMENT_6 = '"""\nXRAI: Region-Based Image Attribution Module\n===========================================\nAggregates pixel-level attributions into predefined spatial regions and ranks them.\n"""\n\nimport numpy as np\nimport pandas as pd\n\n\ndef get_visual_intensity_bar(score, min_score=0.0, max_score=1.0):\n """Returns visual ASCII intensity bars matching the lab manual scale."""\n if max_score == min_score:\n norm = 0.5\n else:\n norm = (score - min_score) / (max_score - min_score)\n \n if norm >= 0.8:\n return "█████ (Very High)"\n elif norm >= 0.6:\n return "████ (High)"\n elif norm >= 0.4:\n return "███ (Medium)"\n elif norm >= 0.2:\n return "██ (Low)"\n else:\n return "█ (Very Low)"\n\n\ndef compute_xrai_fixed_regions(attribution_matrix, region_definitions):\n """\n Aggregates pixel attributions into regions and ranks them.\n Parameters:\n - attribution_matrix: 2D numpy array of pixel attributions\n - region_definitions: dict of region_name -> list of (row, col) coordinates\n """\n results = []\n for r_name, coords in region_definitions.items():\n pixels = [attribution_matrix[r, c] for r, c in coords]\n total_sum = sum(pixels)\n region_size = len(coords)\n score = total_sum / region_size\n results.append({\n \'Region\': r_name,\n \'Pixels\': [round(p, 2) for p in pixels],\n \'Pixel Sum\': total_sum,\n \'Size\': region_size,\n \'Score\': score\n })\n \n df = pd.DataFrame(results)\n df[\'Rank\'] = df[\'Score\'].rank(ascending=False, method=\'min\').astype(int)\n df = df.sort_values(by=[\'Rank\', \'Region\']).reset_index(drop=True)\n \n scores = df[\'Score\'].values\n min_s, max_s = min(scores), max(scores)\n df[\'Intensity\'] = [get_visual_intensity_bar(s, min_s, max_s) for s in scores]\n return df\n\n\ndef run_experiment_6():\n print("=" * 80)\n print("EXPERIMENT 6: XRAI — REGION-BASED IMAGE ATTRIBUTION WITHOUT LIBRARIES")\n print("=" * 80)\n attribution_matrix = np.array([\n [0.1, 0.2, 0.1, 0.0],\n [0.2, 0.3, 0.2, 0.1],\n [0.0, 0.1, 0.8, 0.7],\n [0.1, 0.2, 0.9, 0.8]\n ])\n region_defs = {\n \'R1\': [(0, 0), (0, 1), (1, 0), (1, 1)],\n \'R2\': [(0, 2), (0, 3), (1, 2), (1, 3)],\n \'R3\': [(2, 0), (2, 1), (3, 0), (3, 1)],\n \'R4\': [(2, 2), (2, 3), (3, 2), (3, 3)]\n }\n df = compute_xrai_fixed_regions(attribution_matrix, region_defs)\n print(df[[\'Rank\', \'Region\', \'Score\', \'Intensity\']].to_string(index=False))\n'
4
+
5
+ def experiment_6(*args, **kwargs):
6
+ """Displays the complete Python code for Experiment 6."""
7
+ print(CODE_EXPERIMENT_6)
8
+
9
+ def get_visual_intensity_bar(*args, **kwargs):
10
+ print(CODE_EXPERIMENT_6)
11
+
12
+ def compute_xrai_fixed_regions(*args, **kwargs):
13
+ print(CODE_EXPERIMENT_6)
14
+
15
+ run_experiment_6 = experiment_6
16
+ exp6 = experiment_6
@@ -0,0 +1,143 @@
1
+ Metadata-Version: 2.1
2
+ Name: exai67
3
+ Version: 0.1.0
4
+ Summary: Lightweight, dependency-minimal Explainable AI (XAI) algorithms and laboratory experiments implemented from scratch.
5
+ Author: EXAI Team
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/exai-toolkit/exai-toolkit
8
+ Project-URL: Repository, https://github.com/exai-toolkit/exai-toolkit
9
+ Keywords: xai,explainable-ai,interpretability,shapley,lime,gradcam,lrp,ice,pdp,xrai,machine-learning
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Education
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.8
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
23
+ Requires-Python: >=3.8
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: numpy>=1.20.0
27
+ Requires-Dist: pandas>=1.3.0
28
+ Requires-Dist: scikit-learn>=1.0.0
29
+
30
+ # EXAI (`exai67`)
31
+
32
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
33
+ [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
34
+
35
+ A lightweight, dependency-minimal Python library containing complete implementations of **10 foundational Explainable AI (XAI)** laboratory experiments from scratch. Calling any function prints the full Python code for that experiment directly to the screen.
36
+
37
+ ---
38
+
39
+ ## Features
40
+
41
+ - **Instant Code Display**: Calling any function or experiment displays the complete, working Python source code.
42
+ - **Pure / Minimal Dependencies**: Built with standard Python, `numpy`, `pandas`, and `scikit-learn`. Zero deep learning frameworks.
43
+ - **Exact Formulations**: Follows published literature (Greenwell et al., Ribeiro et al. LIME, Selvaraju et al. Grad-CAM, Bach et al. LRP).
44
+ - **Interactive CLI**: Display experiment codes directly from terminal with `exai run <1-10>` or `exai list`.
45
+
46
+ ---
47
+
48
+ ## Included XAI Algorithms
49
+
50
+ | Experiment | Module / Function | Description |
51
+ | :--- | :--- | :--- |
52
+ | **Index** | `exai.index()` | Index of all 10 experiments with descriptions and commands |
53
+ | **Exp 1** | `exai.exp1()`, `exai.compute_linear_regression_metrics()` | Linear ($R^2$, Adj $R^2$, $t$-stat, 95% CI) & Logistic Regression (Odds Ratio, % change) |
54
+ | **Exp 2** | `exai.exp2()`, `exai.extract_if_then_rules()` | Decision Tree IF-THEN rules, MDI vs Permutation Importance; KNN local neighbor explanations |
55
+ | **Exp 3** | `exai.exp3()`, `exai.approximate_shapley_sampling()` | Approximate Shapley Values via permutation sampling from scratch |
56
+ | **Exp 4** | `exai.exp4()`, `exai.compute_pdp()` | Partial Dependence Plot (PDP) Feature Importance (Greenwell et al., 2018) |
57
+ | **Exp 5** | `exai.exp5()`, `exai.compute_ice_scratch()` | Individual Conditional Expectation (ICE) curves and PDP average computation |
58
+ | **Exp 6** | `exai.exp6()`, `exai.compute_xrai_fixed_regions()` | XRAI Region-based image attribution and visual intensity scoring |
59
+ | **Exp 7** | `exai.exp7()`, `exai.compute_gradcam()` | Grad-CAM (Gradient-Weighted Class Activation Mapping) with ReLU heatmap generation |
60
+ | **Exp 8** | `exai.exp8()`, `exai.fit_lime_surrogate()` | LIME from scratch via Weighted Least Squares (WLS) and exponential kernels |
61
+ | **Exp 9** | `exai.exp9()`, `exai.compute_gradient_x_input()` | CBOW Word Embeddings from scratch & Gradient × Input sentiment attribution |
62
+ | **Exp 10** | `exai.exp10()`, `exai.layerwise_relevance_propagation()` | Layer-Wise Relevance Propagation (LRP) for multi-layer neural networks |
63
+
64
+ ---
65
+
66
+ ## Installation
67
+
68
+ ### From PyPI
69
+ ```bash
70
+ pip install exai67
71
+ ```
72
+
73
+ ### Local Development Install
74
+ ```bash
75
+ pip install -e .
76
+ ```
77
+
78
+ ---
79
+
80
+ ## Python API Usage
81
+
82
+ Calling any function displays the clean, complete, ready-to-run Python code for that experiment:
83
+
84
+ ```python
85
+ import exai
86
+
87
+ # 1. Print code by experiment number
88
+ exai.get_code(1) # Displays Experiment 1 code
89
+ exai.get_code(8) # Displays Experiment 8 code
90
+
91
+ # 2. Print code using shortcut functions
92
+ exai.exp1() # Linear & Logistic Regression
93
+ exai.exp2() # Decision Trees & KNN
94
+ exai.exp3() # Shapley Values
95
+ exai.exp4() # PDP Feature Importance
96
+ exai.exp5() # ICE Plot
97
+ exai.exp6() # XRAI Attribution
98
+ exai.exp7() # Grad-CAM
99
+ exai.exp8() # LIME
100
+ exai.exp9() # CBOW & Gradient x Input
101
+ exai.exp10() # LRP
102
+
103
+ # 3. Print code using algorithm names
104
+ exai.compute_gradcam()
105
+ exai.fit_lime_surrogate()
106
+ exai.layerwise_relevance_propagation()
107
+ ```
108
+
109
+ ---
110
+
111
+ ## Command-Line Interface (CLI)
112
+
113
+ ```bash
114
+ # List all 10 available experiments
115
+ exai list
116
+
117
+ # Display code for a specific experiment
118
+ exai run 8
119
+
120
+ # Display code for all experiments
121
+ exai run all
122
+ ```
123
+
124
+ ---
125
+
126
+ ## Publishing to PyPI
127
+
128
+ ```bash
129
+ # 1. Install build tools
130
+ pip install --upgrade build twine
131
+
132
+ # 2. Build the package
133
+ python3 setup.py sdist
134
+
135
+ # 3. Upload to PyPI
136
+ twine upload dist/*
137
+ ```
138
+
139
+ ---
140
+
141
+ ## License
142
+
143
+ This project is licensed under the [MIT License](LICENSE).
@@ -0,0 +1,25 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ setup.cfg
5
+ setup.py
6
+ src/exai/__init__.py
7
+ src/exai/cbow_gradient.py
8
+ src/exai/cli.py
9
+ src/exai/gradcam.py
10
+ src/exai/ice.py
11
+ src/exai/index.py
12
+ src/exai/lime.py
13
+ src/exai/linear_logistic.py
14
+ src/exai/lrp.py
15
+ src/exai/pdp.py
16
+ src/exai/shapley.py
17
+ src/exai/tree_knn.py
18
+ src/exai/xrai.py
19
+ src/exai67.egg-info/PKG-INFO
20
+ src/exai67.egg-info/SOURCES.txt
21
+ src/exai67.egg-info/dependency_links.txt
22
+ src/exai67.egg-info/entry_points.txt
23
+ src/exai67.egg-info/requires.txt
24
+ src/exai67.egg-info/top_level.txt
25
+ tests/test_library.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ exai = exai.cli:main
@@ -0,0 +1,3 @@
1
+ numpy>=1.20.0
2
+ pandas>=1.3.0
3
+ scikit-learn>=1.0.0
@@ -0,0 +1 @@
1
+ exai
@@ -0,0 +1,72 @@
1
+ """
2
+ Unit Test Suite for EXAI Library
3
+ Verifies that calling any experiment or function outputs the code cleanly.
4
+ """
5
+
6
+ import unittest
7
+ import io
8
+ import sys
9
+ import os
10
+
11
+ # Ensure src is in sys.path
12
+ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../src")))
13
+
14
+ import exai
15
+
16
+
17
+ class TestEXAILibrary(unittest.TestCase):
18
+ def test_version(self):
19
+ self.assertEqual(exai.__version__, "0.1.0")
20
+
21
+ def test_index(self):
22
+ captured = io.StringIO()
23
+ sys.stdout = captured
24
+ try:
25
+ exai.index()
26
+ finally:
27
+ sys.stdout = sys.__stdout__
28
+ output = captured.getvalue()
29
+ self.assertIn("Index of All 10 Laboratory Experiments", output)
30
+ self.assertIn("Linear & Logistic Regression", output)
31
+ self.assertIn("Layer-Wise Relevance Propagation", output)
32
+
33
+ def test_get_code_all_experiments(self):
34
+ for i in range(1, 11):
35
+ captured = io.StringIO()
36
+ sys.stdout = captured
37
+ try:
38
+ exai.get_code(i)
39
+ finally:
40
+ sys.stdout = sys.__stdout__
41
+ output = captured.getvalue()
42
+ self.assertTrue(len(output) > 100, f"Exp {i} output too short!")
43
+ self.assertIn("def ", output)
44
+
45
+ def test_individual_function_calls_print_code(self):
46
+ functions_to_test = [
47
+ exai.exp1,
48
+ exai.compute_linear_regression_metrics,
49
+ exai.extract_if_then_rules,
50
+ exai.approximate_shapley_sampling,
51
+ exai.compute_pdp,
52
+ exai.compute_ice_scratch,
53
+ exai.compute_xrai_fixed_regions,
54
+ exai.compute_gradcam,
55
+ exai.fit_lime_surrogate,
56
+ exai.compute_gradient_x_input,
57
+ exai.layerwise_relevance_propagation
58
+ ]
59
+ for fn in functions_to_test:
60
+ captured = io.StringIO()
61
+ sys.stdout = captured
62
+ try:
63
+ fn()
64
+ finally:
65
+ sys.stdout = sys.__stdout__
66
+ output = captured.getvalue()
67
+ self.assertTrue(len(output) > 100, f"{fn.__name__} did not print code!")
68
+ self.assertIn("def ", output)
69
+
70
+
71
+ if __name__ == "__main__":
72
+ unittest.main()