lime-audit 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.
- lime_audit-0.1.0/.gitignore +27 -0
- lime_audit-0.1.0/LICENSE +21 -0
- lime_audit-0.1.0/Makefile +20 -0
- lime_audit-0.1.0/PKG-INFO +248 -0
- lime_audit-0.1.0/README.md +218 -0
- lime_audit-0.1.0/article.md +103 -0
- lime_audit-0.1.0/audit/__init__.py +0 -0
- lime_audit-0.1.0/audit/analyse_results.py +343 -0
- lime_audit-0.1.0/audit/config.py +24 -0
- lime_audit-0.1.0/audit/failure_memo.md +119 -0
- lime_audit-0.1.0/audit/generate_charts.py +209 -0
- lime_audit-0.1.0/audit/metrics.py +94 -0
- lime_audit-0.1.0/audit/protocol.md +37 -0
- lime_audit-0.1.0/audit/requirements.txt +7 -0
- lime_audit-0.1.0/audit/results/chart_faithfulness_bars.png +0 -0
- lime_audit-0.1.0/audit/results/chart_faithfulness_scatter.png +0 -0
- lime_audit-0.1.0/audit/results/chart_jaccard_by_input.png +0 -0
- lime_audit-0.1.0/audit/results/deletion_faithfulness.csv +29 -0
- lime_audit-0.1.0/audit/results/environment.json +23 -0
- lime_audit-0.1.0/audit/results/raw_attributions.csv +146 -0
- lime_audit-0.1.0/audit/results/stability_metrics.csv +30 -0
- lime_audit-0.1.0/audit/results/summary.json +107 -0
- lime_audit-0.1.0/audit/runner.py +506 -0
- lime_audit-0.1.0/audit/test_set.json +187 -0
- lime_audit-0.1.0/audit/verify.py +71 -0
- lime_audit-0.1.0/backend/.dockerignore +8 -0
- lime_audit-0.1.0/backend/Dockerfile +16 -0
- lime_audit-0.1.0/backend/analyser.py +155 -0
- lime_audit-0.1.0/backend/main.py +145 -0
- lime_audit-0.1.0/backend/requirements.txt +9 -0
- lime_audit-0.1.0/frontend/.gitignore +41 -0
- lime_audit-0.1.0/frontend/app/audit/page.js +297 -0
- lime_audit-0.1.0/frontend/app/favicon.ico +0 -0
- lime_audit-0.1.0/frontend/app/globals.css +26 -0
- lime_audit-0.1.0/frontend/app/layout.js +38 -0
- lime_audit-0.1.0/frontend/app/page.js +1022 -0
- lime_audit-0.1.0/frontend/eslint.config.mjs +16 -0
- lime_audit-0.1.0/frontend/jsconfig.json +7 -0
- lime_audit-0.1.0/frontend/next.config.mjs +6 -0
- lime_audit-0.1.0/frontend/package-lock.json +6581 -0
- lime_audit-0.1.0/frontend/package.json +23 -0
- lime_audit-0.1.0/frontend/postcss.config.mjs +7 -0
- lime_audit-0.1.0/lime_audit_results/chart_faithfulness_bars.png +0 -0
- lime_audit-0.1.0/lime_audit_results/chart_jaccard_by_input.png +0 -0
- lime_audit-0.1.0/lime_audit_results/deletion_faithfulness.csv +30 -0
- lime_audit-0.1.0/lime_audit_results/environment.json +29 -0
- lime_audit-0.1.0/lime_audit_results/raw_attributions.csv +146 -0
- lime_audit-0.1.0/lime_audit_results/stability_metrics.csv +30 -0
- lime_audit-0.1.0/lime_audit_results/summary.json +178 -0
- lime_audit-0.1.0/public/image-1.png +0 -0
- lime_audit-0.1.0/public/image-2.png +0 -0
- lime_audit-0.1.0/public/image-3.png +0 -0
- lime_audit-0.1.0/public/image-4.png +0 -0
- lime_audit-0.1.0/public/image-5.png +0 -0
- lime_audit-0.1.0/public/image.png +0 -0
- lime_audit-0.1.0/pyproject.toml +48 -0
- lime_audit-0.1.0/scripts/benchmark_latency.py +80 -0
- lime_audit-0.1.0/src/lime_audit/__init__.py +3 -0
- lime_audit-0.1.0/src/lime_audit/analyse.py +290 -0
- lime_audit-0.1.0/src/lime_audit/charts.py +179 -0
- lime_audit-0.1.0/src/lime_audit/cli.py +75 -0
- lime_audit-0.1.0/src/lime_audit/config.py +14 -0
- lime_audit-0.1.0/src/lime_audit/default_test_set.json +36 -0
- lime_audit-0.1.0/src/lime_audit/metrics.py +96 -0
- lime_audit-0.1.0/src/lime_audit/runner.py +297 -0
- lime_audit-0.1.0/tests/__init__.py +0 -0
- lime_audit-0.1.0/tests/test_regression.py +34 -0
- lime_audit-0.1.0/xai_forensics_explained.md +232 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
.env
|
|
2
|
+
.env.local
|
|
3
|
+
audit/__pycache__
|
|
4
|
+
__pycache__/
|
|
5
|
+
skills-lock.json
|
|
6
|
+
*.pyc
|
|
7
|
+
*.pyo
|
|
8
|
+
REVIEW.md
|
|
9
|
+
venv/
|
|
10
|
+
.venv/
|
|
11
|
+
backend/venv/
|
|
12
|
+
backend/.venv/
|
|
13
|
+
|
|
14
|
+
node_modules/
|
|
15
|
+
.next/
|
|
16
|
+
out/
|
|
17
|
+
|
|
18
|
+
*.egg-info/
|
|
19
|
+
dist/
|
|
20
|
+
build/
|
|
21
|
+
|
|
22
|
+
.cache/
|
|
23
|
+
.DS_Store
|
|
24
|
+
|
|
25
|
+
.agents
|
|
26
|
+
Decisions.md
|
|
27
|
+
Rules.md
|
lime_audit-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Parshvi Jain
|
|
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.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
.PHONY: audit analyse charts verify clean
|
|
2
|
+
|
|
3
|
+
PYTHON ?= .venv/Scripts/python
|
|
4
|
+
|
|
5
|
+
audit:
|
|
6
|
+
$(PYTHON) -m audit.runner
|
|
7
|
+
$(PYTHON) -m audit.analyse_results
|
|
8
|
+
$(PYTHON) -m audit.generate_charts
|
|
9
|
+
|
|
10
|
+
analyse:
|
|
11
|
+
$(PYTHON) -m audit.analyse_results
|
|
12
|
+
|
|
13
|
+
charts:
|
|
14
|
+
$(PYTHON) -m audit.generate_charts
|
|
15
|
+
|
|
16
|
+
verify:
|
|
17
|
+
$(PYTHON) -c "from audit.verify import verify_reproducibility; verify_reproducibility()"
|
|
18
|
+
|
|
19
|
+
clean:
|
|
20
|
+
$(PYTHON) -c "import glob, os; [os.remove(f) for f in glob.glob('audit/results/*.csv') + glob.glob('audit/results/*.json') + glob.glob('audit/results/*.png')]"
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: lime-audit
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Audit LIME explanations for stability and faithfulness on any HuggingFace text classifier
|
|
5
|
+
Project-URL: Homepage, https://github.com/parshvi1508/XAI_Forensic
|
|
6
|
+
Project-URL: Documentation, https://github.com/parshvi1508/XAI_Forensic#readme
|
|
7
|
+
Project-URL: Issues, https://github.com/parshvi1508/XAI_Forensic/issues
|
|
8
|
+
Author-email: Parshvi Jain <parshvijain1508@gmail.com>
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: LIME,NLP,XAI,audit,explainability,transformers
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Requires-Dist: lime>=0.2
|
|
19
|
+
Requires-Dist: matplotlib>=3.7
|
|
20
|
+
Requires-Dist: numpy>=1.24
|
|
21
|
+
Requires-Dist: scikit-learn>=1.2
|
|
22
|
+
Requires-Dist: scipy>=1.10
|
|
23
|
+
Requires-Dist: torch>=2.0
|
|
24
|
+
Requires-Dist: transformers>=4.30
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: build; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
28
|
+
Requires-Dist: twine; extra == 'dev'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# XAI Forensics
|
|
32
|
+
|
|
33
|
+
A diagnostic tool for evaluating when LIME token attributions are trustworthy on transformer sentiment classifiers. Runs three independent forensic checks (attribution stability, counterfactual faithfulness, cross-model agreement) and validates results against a 30-input pre-registered audit.
|
|
34
|
+
|
|
35
|
+
**Key finding:** high-confidence predictions (>95%) produce the least stable attributions (mean Jaccard 0.46 on strong baselines), while lexical-shortcut inputs are perfectly faithful (Jaccard 1.0, 100% flip rate). This tool shows you when LIME is signal vs. noise.
|
|
36
|
+
|
|
37
|
+
Sentiment classification is the controlled test task. The project evaluates the explanation method, not the classifier output.
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
## Live Demo
|
|
42
|
+
|
|
43
|
+
| Component | Link |
|
|
44
|
+
|-----------|------|
|
|
45
|
+
| Frontend | [xai-forensic.vercel.app](https://xai-forensic.vercel.app/) |
|
|
46
|
+
| Backend API | [jainparshvi-xai-forensics-backend.hf.space](https://jainparshvi-xai-forensics-backend.hf.space) |
|
|
47
|
+
| API Docs (Swagger) | [/docs](https://jainparshvi-xai-forensics-backend.hf.space/docs) |
|
|
48
|
+
|
|
49
|
+
**Demo input:** `I am not entirely unhappy with this result.`
|
|
50
|
+
|
|
51
|
+
This sentence contains a double negation that causes the two models to genuinely disagree.
|
|
52
|
+
|
|
53
|
+
> The backend runs on Hugging Face Spaces free tier. If the Space has been idle, the first request triggers a cold start (30-60 seconds) while models download. Subsequent requests are faster.
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+

|
|
58
|
+

|
|
59
|
+

|
|
60
|
+

|
|
61
|
+

|
|
62
|
+

|
|
63
|
+
|
|
64
|
+
## What This Does
|
|
65
|
+
|
|
66
|
+
XAI Forensics runs three independent diagnostic checks on any short English text:
|
|
67
|
+
|
|
68
|
+
1. **WHY (Attribution Stability)** - Tests whether LIME can produce a stable, reproducible token ranking for a given prediction. Seeds the random state for deterministic output.
|
|
69
|
+
2. **FLIP (Counterfactual Faithfulness)** - Tests whether the tokens LIME identifies as important are actually causally influential. Removes the top-attributed word and measures the real confidence shift.
|
|
70
|
+
3. **DISAGREE (Cross-Model Consistency)** - Tests whether the prediction itself is domain-stable enough to warrant attribution analysis. Compares DistilBERT-SST2 against Twitter-RoBERTa.
|
|
71
|
+
|
|
72
|
+
Each check targets a different failure mode of LIME: instability under re-sampling, unfaithfulness to the model's actual reasoning, and domain sensitivity of the underlying prediction.
|
|
73
|
+
|
|
74
|
+
### Audit Results
|
|
75
|
+
|
|
76
|
+
A pre-registered 30-input audit (5 seeds each, 150 total LIME runs) found:
|
|
77
|
+
- Overall mean Jaccard stability: **0.81** (passes 0.6 threshold)
|
|
78
|
+
- Deletion faithfulness direction correct: **89.3%** (passes 70% threshold)
|
|
79
|
+
- Strong baselines category (high confidence): **Jaccard 0.67** (lowest category, driven by redundant evidence)
|
|
80
|
+
- Lexical shortcuts category: **100% label flip rate** (highest faithfulness)
|
|
81
|
+
|
|
82
|
+
Full interactive results available at [/audit](https://xai-forensic.vercel.app/audit).
|
|
83
|
+
|
|
84
|
+
## How It Works
|
|
85
|
+
|
|
86
|
+
### WHY: LIME Token Attribution
|
|
87
|
+
|
|
88
|
+
Uses [LIME](https://arxiv.org/abs/1602.04938) (Ribeiro et al., 2016) for model-agnostic local explanations. LIME generates perturbed versions of the input text, reruns the classifier on each perturbation, and fits a local linear model to estimate which tokens most influenced the prediction.
|
|
89
|
+
|
|
90
|
+
- Runs 300 perturbation samples per explanation
|
|
91
|
+
- Returns the top 10 tokens with signed weights (positive = pushes toward positive class)
|
|
92
|
+
- SHAP was considered and rejected: slower for transformers, expensive on free-tier CPU
|
|
93
|
+
- Attention weights were rejected as explanations per Jain and Wallace (2019)
|
|
94
|
+
|
|
95
|
+
### FLIP: Counterfactual Word Removal
|
|
96
|
+
|
|
97
|
+
Removes each word one at a time, reruns inference, and finds the word whose removal causes the largest confidence shift. Shows the full before-and-after comparison: original label, modified label, confidence delta, and whether the verdict changed.
|
|
98
|
+
|
|
99
|
+
- Greedy O(n) search over words in the input
|
|
100
|
+
- Deterministic, no generation model required
|
|
101
|
+
- Word removal can create ungrammatical text (documented limitation)
|
|
102
|
+
- Does not always flip the label on highly confident predictions
|
|
103
|
+
|
|
104
|
+
### DISAGREE: Dual-Model Divergence
|
|
105
|
+
|
|
106
|
+
Runs the same text through both models and computes the absolute difference in their positive-class confidence scores. A high divergence score means the models have different views on the same text, which reveals linguistic ambiguity across training domains.
|
|
107
|
+
|
|
108
|
+
- Divergence = abs(positive_score_A - positive_score_B)
|
|
109
|
+
- This is an interpretable confidence delta, not a formal divergence metric like KL divergence
|
|
110
|
+
- KL divergence was considered and rejected: harder to interpret for non-technical audiences
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
## Models Used
|
|
114
|
+
|
|
115
|
+
| Model | Training Data | Strength |
|
|
116
|
+
|-------|---------------|----------|
|
|
117
|
+
| [distilbert-base-uncased-finetuned-sst-2-english](https://huggingface.co/distilbert-base-uncased-finetuned-sst-2-english) | SST-2 movie reviews | Formal, structured English |
|
|
118
|
+
| [cardiffnlp/twitter-roberta-base-sentiment-latest](https://huggingface.co/cardiffnlp/twitter-roberta-base-sentiment-latest) | 124M tweets | Sarcasm, slang, informal tone |
|
|
119
|
+
|
|
120
|
+
These two models are chosen because they genuinely disagree on informal or ambiguous language. DistilBERT expects clean, formal text. Twitter-RoBERTa handles internet language better. This domain mismatch makes the DISAGREE panel meaningful rather than artificial.
|
|
121
|
+
|
|
122
|
+
## Architecture
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
Frontend (Vercel) Backend (HF Spaces Docker)
|
|
126
|
+
Next.js + Tailwind FastAPI + PyTorch
|
|
127
|
+
| |
|
|
128
|
+
|--- POST /why ------------->|--- LIME (300 perturbations)
|
|
129
|
+
|--- POST /flip ------------->|--- Greedy word removal
|
|
130
|
+
|--- POST /disagree ---------->|--- Dual model inference
|
|
131
|
+
| |
|
|
132
|
+
|<---- JSON responses ---------|
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
- Frontend and backend are fully decoupled
|
|
136
|
+
- All endpoints accept `{"text": "..."}` and return structured JSON
|
|
137
|
+
- Frontend calls all three endpoints in parallel using `Promise.allSettled`
|
|
138
|
+
- Models load once at container startup, not per request
|
|
139
|
+
|
|
140
|
+
## Run it Locally
|
|
141
|
+
|
|
142
|
+
### Backend
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
cd backend
|
|
146
|
+
python -m venv venv
|
|
147
|
+
source venv/bin/activate # Windows: venv\Scripts\activate
|
|
148
|
+
pip install -r requirements.txt
|
|
149
|
+
uvicorn main:app --reload --port 8000
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
First run downloads models (~500MB total). Subsequent starts use cached weights.
|
|
153
|
+
|
|
154
|
+
### Frontend
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
cd frontend
|
|
158
|
+
npm install
|
|
159
|
+
npm run dev
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Frontend runs at `http://localhost:3000` and calls the backend at `http://localhost:8000` by default. To change the backend URL, set `NEXT_PUBLIC_API_URL` in `frontend/.env.local`.
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
## API Endpoints
|
|
166
|
+
|
|
167
|
+
All endpoints accept POST with `Content-Type: application/json` and a body of `{"text": "your input"}`.
|
|
168
|
+
|
|
169
|
+
| Endpoint | Returns |
|
|
170
|
+
|----------|---------|
|
|
171
|
+
| `POST /why` | Predicted label, confidence, and top 10 token weights from LIME |
|
|
172
|
+
| `POST /flip` | Original and modified labels, removed word, confidence delta, verdict changed status |
|
|
173
|
+
| `POST /disagree` | Both model predictions, divergence score, agreement status |
|
|
174
|
+
| `GET /` | Health check: `{"status": "ok"}` |
|
|
175
|
+
| `GET /docs` | Interactive Swagger API documentation |
|
|
176
|
+
|
|
177
|
+
Input is limited to 1000 characters. Requests with longer text return HTTP 400.
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
## Latency Notes
|
|
181
|
+
|
|
182
|
+
| Endpoint | Approx. CPU time | Why |
|
|
183
|
+
|----------|-------------------|-----|
|
|
184
|
+
| `/why` | 15-45 seconds | LIME runs 300 model inference calls per explanation |
|
|
185
|
+
| `/flip` | 2-10 seconds | One model call per word in the input |
|
|
186
|
+
| `/disagree` | Under 1 second | Two forward passes |
|
|
187
|
+
|
|
188
|
+
LIME is the main latency source. CPU inference is used because deployment is on the Hugging Face free tier, which does not guarantee GPU availability. This is acceptable for a demo tool with short text inputs.
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
## Latency Benchmark
|
|
192
|
+
|
|
193
|
+
The backend runs on Hugging Face Spaces free-tier CPU. The table below reports median endpoint runtime across 3 runs after one warmup request. Timings are approximate because free-tier CPU performance varies.
|
|
194
|
+
|
|
195
|
+
| Words | WHY median | FLIP median | DISAGREE median |
|
|
196
|
+
|---:|---:|---:|---:|
|
|
197
|
+
| 5 | 4.5s | 414ms | 98ms |
|
|
198
|
+
| 10 | 5.8s | 270ms | 75ms |
|
|
199
|
+
| 20 | 6.1s | 475ms | 72ms |
|
|
200
|
+
| 50 | 8.3s | 4.2s | 104ms |
|
|
201
|
+
|
|
202
|
+
WHY is slowest because LIME generates perturbed versions of the input and reruns model inference many times. FLIP scales with word count because it removes candidate words one at a time and reruns inference. DISAGREE is fastest because it only runs two model forward passes.
|
|
203
|
+
|
|
204
|
+
The benchmark can be reproduced with `scripts/benchmark_latency.py`.
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
## Security and Cost
|
|
208
|
+
|
|
209
|
+
- No paid APIs. Both models are public Hugging Face models.
|
|
210
|
+
- No database. Nothing is stored.
|
|
211
|
+
- No authentication. This is a public demo tool.
|
|
212
|
+
- No user data is collected, logged, or persisted.
|
|
213
|
+
- CORS allows all origins (appropriate for a public demo with no sensitive operations).
|
|
214
|
+
- Input capped at 1000 characters to prevent LIME timeouts on free-tier CPU.
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
## Known Behavior and Limitations
|
|
218
|
+
|
|
219
|
+
Single-word inputs are not ideal for this tool. LIME works by perturbing parts of the input and observing prediction changes. With only one word, there is very little structure to perturb, so the explanation can be unstable or uninformative. In testing, short inputs such as "Fine." can produce domain-sensitive behavior because different models interpret minimal context differently.
|
|
220
|
+
|
|
221
|
+
Highly confident predictions may not flip after one-word removal. For example, strongly positive sentences such as "I absolutely love this, it is the best thing ever." often remain positive after removing one word. This does not mean every word is irrelevant. It means the model found enough evidence across the sentence that removing one token did not change the final verdict.
|
|
222
|
+
|
|
223
|
+
Counterfactual removal can create ungrammatical text because the method deletes a word rather than rewriting the sentence. This is a deliberate MVP tradeoff. The FLIP panel is a fragility test, not a full natural-language counterfactual generator. For example, removing a key word from "I am not entirely unhappy with this result." can flip the verdict, but the modified sentence may not always be natural English.
|
|
224
|
+
|
|
225
|
+
Additional limitations:
|
|
226
|
+
|
|
227
|
+
- LIME explanation takes 15-45 seconds on CPU for short text
|
|
228
|
+
- Input limited to 1000 characters
|
|
229
|
+
- Hugging Face free tier sleeps after inactivity; first request after sleep has a 30-60 second cold start
|
|
230
|
+
- Only tested on English text
|
|
231
|
+
- Sentiment-specific; adapting to other tasks would require different models and possibly different XAI methods
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
## Tech Stack
|
|
235
|
+
|
|
236
|
+
| Layer | Technology |
|
|
237
|
+
|-------|-----------|
|
|
238
|
+
| Backend | Python, FastAPI, PyTorch, Hugging Face Transformers, LIME |
|
|
239
|
+
| Frontend | Next.js, React, Tailwind CSS |
|
|
240
|
+
| Backend deployment | Hugging Face Spaces (Docker) |
|
|
241
|
+
| Frontend deployment | Vercel |
|
|
242
|
+
| ML models | DistilBERT-SST2, Twitter-RoBERTa |
|
|
243
|
+
| Infrastructure cost | Zero (free tier only) |
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
## License
|
|
247
|
+
|
|
248
|
+
MIT
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# XAI Forensics
|
|
2
|
+
|
|
3
|
+
A diagnostic tool for evaluating when LIME token attributions are trustworthy on transformer sentiment classifiers. Runs three independent forensic checks (attribution stability, counterfactual faithfulness, cross-model agreement) and validates results against a 30-input pre-registered audit.
|
|
4
|
+
|
|
5
|
+
**Key finding:** high-confidence predictions (>95%) produce the least stable attributions (mean Jaccard 0.46 on strong baselines), while lexical-shortcut inputs are perfectly faithful (Jaccard 1.0, 100% flip rate). This tool shows you when LIME is signal vs. noise.
|
|
6
|
+
|
|
7
|
+
Sentiment classification is the controlled test task. The project evaluates the explanation method, not the classifier output.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
## Live Demo
|
|
12
|
+
|
|
13
|
+
| Component | Link |
|
|
14
|
+
|-----------|------|
|
|
15
|
+
| Frontend | [xai-forensic.vercel.app](https://xai-forensic.vercel.app/) |
|
|
16
|
+
| Backend API | [jainparshvi-xai-forensics-backend.hf.space](https://jainparshvi-xai-forensics-backend.hf.space) |
|
|
17
|
+
| API Docs (Swagger) | [/docs](https://jainparshvi-xai-forensics-backend.hf.space/docs) |
|
|
18
|
+
|
|
19
|
+
**Demo input:** `I am not entirely unhappy with this result.`
|
|
20
|
+
|
|
21
|
+
This sentence contains a double negation that causes the two models to genuinely disagree.
|
|
22
|
+
|
|
23
|
+
> The backend runs on Hugging Face Spaces free tier. If the Space has been idle, the first request triggers a cold start (30-60 seconds) while models download. Subsequent requests are faster.
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+

|
|
28
|
+

|
|
29
|
+

|
|
30
|
+

|
|
31
|
+

|
|
32
|
+

|
|
33
|
+
|
|
34
|
+
## What This Does
|
|
35
|
+
|
|
36
|
+
XAI Forensics runs three independent diagnostic checks on any short English text:
|
|
37
|
+
|
|
38
|
+
1. **WHY (Attribution Stability)** - Tests whether LIME can produce a stable, reproducible token ranking for a given prediction. Seeds the random state for deterministic output.
|
|
39
|
+
2. **FLIP (Counterfactual Faithfulness)** - Tests whether the tokens LIME identifies as important are actually causally influential. Removes the top-attributed word and measures the real confidence shift.
|
|
40
|
+
3. **DISAGREE (Cross-Model Consistency)** - Tests whether the prediction itself is domain-stable enough to warrant attribution analysis. Compares DistilBERT-SST2 against Twitter-RoBERTa.
|
|
41
|
+
|
|
42
|
+
Each check targets a different failure mode of LIME: instability under re-sampling, unfaithfulness to the model's actual reasoning, and domain sensitivity of the underlying prediction.
|
|
43
|
+
|
|
44
|
+
### Audit Results
|
|
45
|
+
|
|
46
|
+
A pre-registered 30-input audit (5 seeds each, 150 total LIME runs) found:
|
|
47
|
+
- Overall mean Jaccard stability: **0.81** (passes 0.6 threshold)
|
|
48
|
+
- Deletion faithfulness direction correct: **89.3%** (passes 70% threshold)
|
|
49
|
+
- Strong baselines category (high confidence): **Jaccard 0.67** (lowest category, driven by redundant evidence)
|
|
50
|
+
- Lexical shortcuts category: **100% label flip rate** (highest faithfulness)
|
|
51
|
+
|
|
52
|
+
Full interactive results available at [/audit](https://xai-forensic.vercel.app/audit).
|
|
53
|
+
|
|
54
|
+
## How It Works
|
|
55
|
+
|
|
56
|
+
### WHY: LIME Token Attribution
|
|
57
|
+
|
|
58
|
+
Uses [LIME](https://arxiv.org/abs/1602.04938) (Ribeiro et al., 2016) for model-agnostic local explanations. LIME generates perturbed versions of the input text, reruns the classifier on each perturbation, and fits a local linear model to estimate which tokens most influenced the prediction.
|
|
59
|
+
|
|
60
|
+
- Runs 300 perturbation samples per explanation
|
|
61
|
+
- Returns the top 10 tokens with signed weights (positive = pushes toward positive class)
|
|
62
|
+
- SHAP was considered and rejected: slower for transformers, expensive on free-tier CPU
|
|
63
|
+
- Attention weights were rejected as explanations per Jain and Wallace (2019)
|
|
64
|
+
|
|
65
|
+
### FLIP: Counterfactual Word Removal
|
|
66
|
+
|
|
67
|
+
Removes each word one at a time, reruns inference, and finds the word whose removal causes the largest confidence shift. Shows the full before-and-after comparison: original label, modified label, confidence delta, and whether the verdict changed.
|
|
68
|
+
|
|
69
|
+
- Greedy O(n) search over words in the input
|
|
70
|
+
- Deterministic, no generation model required
|
|
71
|
+
- Word removal can create ungrammatical text (documented limitation)
|
|
72
|
+
- Does not always flip the label on highly confident predictions
|
|
73
|
+
|
|
74
|
+
### DISAGREE: Dual-Model Divergence
|
|
75
|
+
|
|
76
|
+
Runs the same text through both models and computes the absolute difference in their positive-class confidence scores. A high divergence score means the models have different views on the same text, which reveals linguistic ambiguity across training domains.
|
|
77
|
+
|
|
78
|
+
- Divergence = abs(positive_score_A - positive_score_B)
|
|
79
|
+
- This is an interpretable confidence delta, not a formal divergence metric like KL divergence
|
|
80
|
+
- KL divergence was considered and rejected: harder to interpret for non-technical audiences
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
## Models Used
|
|
84
|
+
|
|
85
|
+
| Model | Training Data | Strength |
|
|
86
|
+
|-------|---------------|----------|
|
|
87
|
+
| [distilbert-base-uncased-finetuned-sst-2-english](https://huggingface.co/distilbert-base-uncased-finetuned-sst-2-english) | SST-2 movie reviews | Formal, structured English |
|
|
88
|
+
| [cardiffnlp/twitter-roberta-base-sentiment-latest](https://huggingface.co/cardiffnlp/twitter-roberta-base-sentiment-latest) | 124M tweets | Sarcasm, slang, informal tone |
|
|
89
|
+
|
|
90
|
+
These two models are chosen because they genuinely disagree on informal or ambiguous language. DistilBERT expects clean, formal text. Twitter-RoBERTa handles internet language better. This domain mismatch makes the DISAGREE panel meaningful rather than artificial.
|
|
91
|
+
|
|
92
|
+
## Architecture
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
Frontend (Vercel) Backend (HF Spaces Docker)
|
|
96
|
+
Next.js + Tailwind FastAPI + PyTorch
|
|
97
|
+
| |
|
|
98
|
+
|--- POST /why ------------->|--- LIME (300 perturbations)
|
|
99
|
+
|--- POST /flip ------------->|--- Greedy word removal
|
|
100
|
+
|--- POST /disagree ---------->|--- Dual model inference
|
|
101
|
+
| |
|
|
102
|
+
|<---- JSON responses ---------|
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
- Frontend and backend are fully decoupled
|
|
106
|
+
- All endpoints accept `{"text": "..."}` and return structured JSON
|
|
107
|
+
- Frontend calls all three endpoints in parallel using `Promise.allSettled`
|
|
108
|
+
- Models load once at container startup, not per request
|
|
109
|
+
|
|
110
|
+
## Run it Locally
|
|
111
|
+
|
|
112
|
+
### Backend
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
cd backend
|
|
116
|
+
python -m venv venv
|
|
117
|
+
source venv/bin/activate # Windows: venv\Scripts\activate
|
|
118
|
+
pip install -r requirements.txt
|
|
119
|
+
uvicorn main:app --reload --port 8000
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
First run downloads models (~500MB total). Subsequent starts use cached weights.
|
|
123
|
+
|
|
124
|
+
### Frontend
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
cd frontend
|
|
128
|
+
npm install
|
|
129
|
+
npm run dev
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Frontend runs at `http://localhost:3000` and calls the backend at `http://localhost:8000` by default. To change the backend URL, set `NEXT_PUBLIC_API_URL` in `frontend/.env.local`.
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
## API Endpoints
|
|
136
|
+
|
|
137
|
+
All endpoints accept POST with `Content-Type: application/json` and a body of `{"text": "your input"}`.
|
|
138
|
+
|
|
139
|
+
| Endpoint | Returns |
|
|
140
|
+
|----------|---------|
|
|
141
|
+
| `POST /why` | Predicted label, confidence, and top 10 token weights from LIME |
|
|
142
|
+
| `POST /flip` | Original and modified labels, removed word, confidence delta, verdict changed status |
|
|
143
|
+
| `POST /disagree` | Both model predictions, divergence score, agreement status |
|
|
144
|
+
| `GET /` | Health check: `{"status": "ok"}` |
|
|
145
|
+
| `GET /docs` | Interactive Swagger API documentation |
|
|
146
|
+
|
|
147
|
+
Input is limited to 1000 characters. Requests with longer text return HTTP 400.
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
## Latency Notes
|
|
151
|
+
|
|
152
|
+
| Endpoint | Approx. CPU time | Why |
|
|
153
|
+
|----------|-------------------|-----|
|
|
154
|
+
| `/why` | 15-45 seconds | LIME runs 300 model inference calls per explanation |
|
|
155
|
+
| `/flip` | 2-10 seconds | One model call per word in the input |
|
|
156
|
+
| `/disagree` | Under 1 second | Two forward passes |
|
|
157
|
+
|
|
158
|
+
LIME is the main latency source. CPU inference is used because deployment is on the Hugging Face free tier, which does not guarantee GPU availability. This is acceptable for a demo tool with short text inputs.
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
## Latency Benchmark
|
|
162
|
+
|
|
163
|
+
The backend runs on Hugging Face Spaces free-tier CPU. The table below reports median endpoint runtime across 3 runs after one warmup request. Timings are approximate because free-tier CPU performance varies.
|
|
164
|
+
|
|
165
|
+
| Words | WHY median | FLIP median | DISAGREE median |
|
|
166
|
+
|---:|---:|---:|---:|
|
|
167
|
+
| 5 | 4.5s | 414ms | 98ms |
|
|
168
|
+
| 10 | 5.8s | 270ms | 75ms |
|
|
169
|
+
| 20 | 6.1s | 475ms | 72ms |
|
|
170
|
+
| 50 | 8.3s | 4.2s | 104ms |
|
|
171
|
+
|
|
172
|
+
WHY is slowest because LIME generates perturbed versions of the input and reruns model inference many times. FLIP scales with word count because it removes candidate words one at a time and reruns inference. DISAGREE is fastest because it only runs two model forward passes.
|
|
173
|
+
|
|
174
|
+
The benchmark can be reproduced with `scripts/benchmark_latency.py`.
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
## Security and Cost
|
|
178
|
+
|
|
179
|
+
- No paid APIs. Both models are public Hugging Face models.
|
|
180
|
+
- No database. Nothing is stored.
|
|
181
|
+
- No authentication. This is a public demo tool.
|
|
182
|
+
- No user data is collected, logged, or persisted.
|
|
183
|
+
- CORS allows all origins (appropriate for a public demo with no sensitive operations).
|
|
184
|
+
- Input capped at 1000 characters to prevent LIME timeouts on free-tier CPU.
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
## Known Behavior and Limitations
|
|
188
|
+
|
|
189
|
+
Single-word inputs are not ideal for this tool. LIME works by perturbing parts of the input and observing prediction changes. With only one word, there is very little structure to perturb, so the explanation can be unstable or uninformative. In testing, short inputs such as "Fine." can produce domain-sensitive behavior because different models interpret minimal context differently.
|
|
190
|
+
|
|
191
|
+
Highly confident predictions may not flip after one-word removal. For example, strongly positive sentences such as "I absolutely love this, it is the best thing ever." often remain positive after removing one word. This does not mean every word is irrelevant. It means the model found enough evidence across the sentence that removing one token did not change the final verdict.
|
|
192
|
+
|
|
193
|
+
Counterfactual removal can create ungrammatical text because the method deletes a word rather than rewriting the sentence. This is a deliberate MVP tradeoff. The FLIP panel is a fragility test, not a full natural-language counterfactual generator. For example, removing a key word from "I am not entirely unhappy with this result." can flip the verdict, but the modified sentence may not always be natural English.
|
|
194
|
+
|
|
195
|
+
Additional limitations:
|
|
196
|
+
|
|
197
|
+
- LIME explanation takes 15-45 seconds on CPU for short text
|
|
198
|
+
- Input limited to 1000 characters
|
|
199
|
+
- Hugging Face free tier sleeps after inactivity; first request after sleep has a 30-60 second cold start
|
|
200
|
+
- Only tested on English text
|
|
201
|
+
- Sentiment-specific; adapting to other tasks would require different models and possibly different XAI methods
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
## Tech Stack
|
|
205
|
+
|
|
206
|
+
| Layer | Technology |
|
|
207
|
+
|-------|-----------|
|
|
208
|
+
| Backend | Python, FastAPI, PyTorch, Hugging Face Transformers, LIME |
|
|
209
|
+
| Frontend | Next.js, React, Tailwind CSS |
|
|
210
|
+
| Backend deployment | Hugging Face Spaces (Docker) |
|
|
211
|
+
| Frontend deployment | Vercel |
|
|
212
|
+
| ML models | DistilBERT-SST2, Twitter-RoBERTa |
|
|
213
|
+
| Infrastructure cost | Zero (free tier only) |
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
## License
|
|
217
|
+
|
|
218
|
+
MIT
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# I Audited LIME on 30 Inputs and Found It Fails on the Easiest Ones
|
|
2
|
+
|
|
3
|
+
LIME is one of the most cited explanation methods in machine learning. It tells you which words in a sentence drove a classifier's prediction. But does it give you the same answer twice?
|
|
4
|
+
|
|
5
|
+
I ran LIME on 30 carefully chosen inputs across 5 random seeds, measured whether the explanations stayed stable, and tested whether removing the "most important" token actually changed the model's output. The input with the worst stability was not the hardest or most ambiguous one. It was "This is the best product I have ever purchased."
|
|
6
|
+
|
|
7
|
+
## The Setup
|
|
8
|
+
|
|
9
|
+
I froze a test set of 30 inputs before looking at any results. The inputs span six categories designed to stress-test LIME: negation and minimal pairs ("This is not good" vs. "This is good"), lexical shortcuts where surface cues contradict the true sentiment, ambiguous sentences near the decision boundary, distribution shifts from domains like clinical notes and financial reports, strong baselines that any competent model should handle easily, and edge cases like repeated tokens or alternating contradictions.
|
|
10
|
+
|
|
11
|
+
The model is DistilBERT fine-tuned on SST-2 (the standard sentiment benchmark), pinned to an exact revision hash. LIME runs with 300 perturbation samples and returns the top 10 tokens ranked by attribution weight.
|
|
12
|
+
|
|
13
|
+
I measure two things:
|
|
14
|
+
|
|
15
|
+
**Stability**: For each input, I run LIME with five different random seeds and compute the Jaccard overlap of the top-5 attributed tokens across all 10 seed pairs. A Jaccard of 1.0 means every seed returned the same five tokens. A Jaccard of 0.25 means the explanations barely overlap.
|
|
16
|
+
|
|
17
|
+
**Deletion faithfulness**: Using the canonical seed, I identify the token LIME considers most important and remove it from the input. If LIME says a token has positive weight, removing it should decrease the model's positive score. If the score barely moves, the "most important" token was not actually important.
|
|
18
|
+
|
|
19
|
+
All thresholds, metrics, and the 30 inputs were frozen in a pre-registration protocol before seeing any results. The raw data, every CSV, and the runner code are public.
|
|
20
|
+
|
|
21
|
+
## The Surprise: Easy Inputs Are the Least Stable
|
|
22
|
+
|
|
23
|
+
The overall mean Jaccard is 0.81 [0.74, 0.87] (95% bootstrap CI), comfortably above the 0.60 threshold. That headline number looks fine. But the per-input distribution tells a different story.
|
|
24
|
+
|
|
25
|
+

|
|
26
|
+
|
|
27
|
+
The leftmost bar in the chart, the input with the worst stability of all 29 tested inputs, is Input 21: "This is the best product I have ever purchased." Its mean Jaccard is 0.46, with a minimum of 0.25 across seed pairs. This is an unambiguously positive sentence that any sentiment model handles with near-perfect confidence (0.9999).
|
|
28
|
+
|
|
29
|
+
Why does the easiest input have the worst explanation stability?
|
|
30
|
+
|
|
31
|
+
The sentence has nine whitespace tokens: "This", "is", "the", "best", "product", "I", "have", "ever", "purchased". Only one or two of those carry real sentiment signal ("best", maybe "purchased"). The remaining seven are function words with near-zero LIME weights. Across seeds, LIME assigns slightly different random weights to "is", "the", "I", "have", and "ever", causing them to shuffle positions in the top-5 ranking. The top-1 token is not even consistent, because "best" receives a weight of only 0.008, barely above the noise floor.
|
|
32
|
+
|
|
33
|
+
The instability is not about hard cases. It is about the signal-to-noise ratio between meaningful tokens and filler. Longer sentences with clear sentiment have more function words competing for top-5 slots with near-zero weights, making the ranking fragile.
|
|
34
|
+
|
|
35
|
+
The per-category breakdown confirms this. Strong baselines, the easiest category, have the second-worst mean Jaccard (0.67). Ambiguity, a category you would expect to be unstable, actually scores 0.97 because ambiguous sentences are short and nearly every token carries some signal.
|
|
36
|
+
|
|
37
|
+
| Category | Mean Jaccard | 95% CI |
|
|
38
|
+
|----------|-------------|--------|
|
|
39
|
+
| Ambiguity | 0.97 | [0.93, 1.00] |
|
|
40
|
+
| Edge cases | 0.94 | [0.86, 1.00] |
|
|
41
|
+
| Negation | 0.80 | [0.66, 0.93] |
|
|
42
|
+
| Lexical shortcuts | 0.77 | [0.62, 0.92] |
|
|
43
|
+
| Distribution shift | 0.72 | [0.61, 0.84] |
|
|
44
|
+
| Strong baselines | 0.67 | [0.52, 0.82] |
|
|
45
|
+
|
|
46
|
+
## LIME Says "This Token Matters." Deletion Says "Nothing Matters."
|
|
47
|
+
|
|
48
|
+
The deletion faithfulness test reveals a second, independent problem. Overall, 89% of deletions are directionally correct: if LIME says a token is positive, removing it decreases the score. That passes the 0.70 threshold. But the magnitude tells a different story.
|
|
49
|
+
|
|
50
|
+

|
|
51
|
+
|
|
52
|
+
The chart shows two distinct clusters. In the upper-left corner, lexical shortcuts: inputs like "The movie was terrible but I loved every minute of it." Removing the decisive token ("loved") causes a confidence swing of 0.987 and a full label flip. LIME works perfectly here because one token genuinely dominates the prediction.
|
|
53
|
+
|
|
54
|
+
In the lower-right corner, strong baselines: inputs where the model is 0.9999 confident. Removing the "most important" token changes the score by 0.0002. Not a single strong baseline input flips its label when the top token is removed. LIME is directionally correct but the deletion test is powerless. When a model outputs 0.9999, removing one word drops it to 0.9995. The direction is right, but the magnitude is meaningless.
|
|
55
|
+
|
|
56
|
+
The per-category faithfulness numbers make this concrete:
|
|
57
|
+
|
|
58
|
+
| Category | Direction Correct | Flip Rate | Mean |Delta| |
|
|
59
|
+
|----------|------------------|-----------|------------|
|
|
60
|
+
| Lexical shortcuts | 100% | 100% | 0.995 |
|
|
61
|
+
| Ambiguity | 100% | 40% | 0.408 |
|
|
62
|
+
| Distribution shift | 100% | 20% | 0.230 |
|
|
63
|
+
| Negation | 80% | 60% | 0.590 |
|
|
64
|
+
| Strong baselines | 80% | 0% | 0.0002 |
|
|
65
|
+
| Edge cases | 75% | 25% | 0.162 |
|
|
66
|
+
|
|
67
|
+
LIME is faithful in direction but blind to magnitude. For high-confidence predictions, the standard one-token deletion test cannot distinguish a genuinely important token from a random one.
|
|
68
|
+
|
|
69
|
+
## The Tokenizer Problem Nobody Talks About
|
|
70
|
+
|
|
71
|
+
LIME splits text on whitespace. The model uses WordPiece tokenization. These are not the same thing.
|
|
72
|
+
|
|
73
|
+
When LIME sees "I'm", it treats that as one token. When DistilBERT sees "I'm", it splits it into three subword pieces: "i", "'", "m". LIME's perturbation masks whole whitespace tokens, but the model processes subwords. This means LIME's attribution is to whitespace tokens, not to the features the model actually uses. The mismatch is fundamental and cannot be fixed without replacing LIME's perturbation strategy.
|
|
74
|
+
|
|
75
|
+
Across the 29 tested inputs, the model's WordPiece tokenizer produces more tokens than LIME's whitespace splitter in the majority of cases. The worst mismatch occurs on longer sentences where compound words, contractions, and punctuation create divergent tokenizations. LIME attributes importance to a unit of text that does not correspond to how the model reads it.
|
|
76
|
+
|
|
77
|
+
## When to Trust LIME
|
|
78
|
+
|
|
79
|
+
LIME is not useless. It works well under specific conditions:
|
|
80
|
+
|
|
81
|
+
**Trust LIME when one token dominates.** Lexical shortcuts score 100% direction-correct and 100% label flip. When a sentence has a single decisive word that, if removed, reverses the prediction, LIME finds it reliably.
|
|
82
|
+
|
|
83
|
+
**Trust LIME on short inputs.** Shorter sentences have fewer near-zero-weight function words competing for top-5 slots. "This is good" (3 tokens) has perfect Jaccard across all seeds. "The quality exceeded all my expectations and I am thrilled" (11 tokens) drops to 0.68.
|
|
84
|
+
|
|
85
|
+
**Do not trust LIME when model confidence exceeds 0.99.** The deletion test becomes powerless. LIME will tell you a token is important, but removing it changes nothing measurable.
|
|
86
|
+
|
|
87
|
+
**Do not trust the ranking below rank 1.** The top-1 token is unanimous across seeds for 66% of inputs. Ranks 2 through 5 are where the instability lives. If you are reporting LIME results, report the top token and its weight. Do not treat the full ranking as meaningful.
|
|
88
|
+
|
|
89
|
+
**Be skeptical of LIME on long sentences with clear sentiment.** Counterintuitively, these are where LIME is least stable, because function words create ranking noise.
|
|
90
|
+
|
|
91
|
+
## Limitations
|
|
92
|
+
|
|
93
|
+
This audit tests 30 inputs, giving 4 to 5 data points per category. Per-category statistics have wide confidence intervals, which is why I report bootstrap CIs rather than point estimates. Only one model (DistilBERT-SST2) is audited. The findings may not generalize to other architectures, though the tokenizer mismatch problem is architectural and affects any model that does not split on whitespace. The deletion test uses 300 perturbation samples, which is low. Higher sample counts would tighten attributions but would not fix the signal-to-noise problem on function-word-heavy sentences. Input 30 (a whitespace-only string) was intentionally included to test pipeline robustness. The pipeline correctly refused it, and it is logged as skipped rather than silently excluded.
|
|
94
|
+
|
|
95
|
+
## Try It Yourself
|
|
96
|
+
|
|
97
|
+
The live demo is at [xai-forensic.vercel.app](https://xai-forensic.vercel.app). Type a sentence and see the LIME attribution, counterfactual word removal, and dual-model divergence in real time.
|
|
98
|
+
|
|
99
|
+
The full audit code, pre-registration protocol, raw CSVs, and this article's charts are at [github.com/parshvi1508/XAI_Forensic](https://github.com/parshvi1508/XAI_Forensic). Every number in this article can be reproduced with `make audit`.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
*Parshvi Jain is a computer science student at ABES Engineering College and IIT Madras (BS Data Science), researching multimodal sentiment analysis and explainable AI. She deliberately chose LIME over attention weights as an explanation method (Jain and Wallace, 2019, showed attention is unreliable for explanation) and built XAI Forensics to make model explanations auditable.*
|
|
File without changes
|