Skip to content

Repository files navigation

Pre-Generation Hallucination Detection
via Internal Representation Drift

Catching RAG hallucinations from a transformer's hidden states: before the first wrong token is emitted.

Live Demo Gallery Paper AUROC Pre-Onset Model Python PyTorch License: MIT

AUROC 0.6511 on RAGTruth held-out test  ·  drift peaks at t−2 before text onset  ·  0.6450 zero-shot on HaluEval



Explore real recorded runs from Qwen2.5-1.5B token-by-token and inspect mounted research plots right in your browser.


Interactive token-level decode demo

Real recorded runs from Qwen2.5-1.5B. The detector's hottest tokens on the fabricated answer are
the invented content: 1905 and Olympic. Click the animation to run the live console.


Executive Summary

When a retrieval-augmented generation (RAG) system retrieves relevant documents, it can still generate content completely absent from the source. Traditional detectors only operate after generation finishes: employing external LLM judges, token frequency heuristics, or multi-sample logit consistency. These solutions introduce multiple seconds of latency, multiply token costs, and provide zero mechanistic explanation.

This project addresses a fundamental question: does the model's internal residual stream already reflect the hallucination before it is written?

By running the generation prompt under two conditions (with retrieved evidence $D$ vs with empty context $\emptyset$), we track internal representation drift across depth. The empirical discovery: drift peaks at $t-2$, two tokens before the hallucination appears in the generated text ($p = 2.86 \times 10^{-5}$). This pre-onset gap makes genuine pre-generation intervention possible in a single forward pass.


🎮 Interactive Web Demo & Visual Showcase

The project includes an interactive web application deployed live on GitHub Pages, featuring native CSS scroll-driven animations, active navigation scroll spy, and GPU-accelerated SVG instrumentation:

🔗 https://chirudeva-reddy.github.io/NLP-Proj/

1. Token-by-Token Playback Console

  • Real-time generation scrubber: Scrub forward and backward across decoded tokens at generation pace.
  • Synchronized representation sparkline: Dynamic SVG trace tracks residual drift position by position.
  • Multi-metric switching: Inspect individual signals (Cosine, Mahalanobis, Logit Lens, PCA) and their consensus ranking.
  • Keyboard controls: Press Space to play/pause, / to step tokens, and 15 to switch test cases.

2. Mounted Figure Exhibition Gallery

  • Lab-grade figure mounting: All 7 pipeline plots mounted inside neutral matte exhibition stages with elevation framing.
  • Scroll-driven entrance choreography: Native CSS animation-timeline: view() entrance transitions with staggered fallback.
  • Instant category filtering: Synchronously filter between All plots, Core findings, and Diagnostic traces.
  • Display modes & lightbox: Switch between multi-column grid and horizontal scroll stream, or click any figure to open the high-resolution lightbox with 1-click CLI reproduction commands.

3. Pre-Onset Peak Explorer

  • Animated trajectory draw: Interactive SVG curve shows representation drift peaking at t−2, two positions prior to the first hallucinated token ($p = 2.86 \times 10^{-5}$).
  • Signal comparison: Switch between Cosine drift, Mahalanobis distance, Logit Lens, and CIE directly on the chart.

4. Dynamic Benchmark Matrix

  • Multi-metric column switching: Toggle across AUROC, Span F1, Spearman ρ, and Expected Calibration Error (ECE).
  • Bootstrap uncertainty bars: Dynamic SVG whisker bars visualizing 1,000-iteration bootstrap confidence intervals with chance baselines.

5. Drift Reader Experience

  • Real-time representation heatmapping: Words illuminate as you scroll: cold where representations stay on the faithful manifold, hot where they depart.
  • Side-by-side evidence tracking: Direct comparison against the retrieved source passage with live peak metric counters.

6. Active Nav & Full Responsiveness

  • Scroll spy header: Dynamically tracks viewport position with vermilion indicators.
  • Zero horizontal overflow: Verified 100% responsive across mobile, tablet, and desktop viewports (360px–1280px).

Interactive Architecture & Pipeline

The prompt executes twice: once with retrieved evidence, once with empty context. The difference in internal state evolution provides the detection signal.

1. Interactive Flowchart (Click any block to inspect)

flowchart TD
    A["<b>1 · Input Prompt</b><br/>q + Evidence (D) vs q + Empty Context (∅)<br/>Paired forward passes on Qwen2.5-1.5B"] --> B["<b>2 · Hidden-State Extraction</b><br/>Token-level states h_t across last 18 layers<br/>Forward hooks on residual stream"]

    B --> S1["<b>Cosine Drift δ</b><br/>Trajectory instability<br/>AUROC 0.5989 · w: 0.383"]
    B --> S2["<b>Mahalanobis m</b><br/>Off-manifold displacement<br/>AUROC 0.5389 · w: 0.164"]
    B --> S3["<b>Logit Lens KL Λ</b><br/>Cross-depth disagreement<br/>AUROC 0.5732 · w: 0.206"]
    B --> S4["<b>PCA Residual ρ</b><br/>Subspace escape<br/>AUROC 0.5490 · w: 0.247"]
    B --> S5["<b>Causal Patching CIE</b><br/>Mechanistic ground truth<br/>AUROC 0.5138 · Localisation"]

    S1 --> D["<b>4 · Robust Z-Score Fusion</b><br/>z_i = (x_i - med_train) / IQR_train<br/>s = Σ w_i · z_i (Frozen in stats.pt)"]
    S2 --> D
    S3 --> D
    S4 --> D
    D --> E["<b>5 · Pre-Generation Hallucination Score</b><br/>AUROC 0.6511 · 3.3x ECE Reduction (0.0678)<br/><b>Drift peaks at t-2 before text onset</b>"]
    S5 -.->|Causal validation p < 0.05| E

    click S1 href "https://chirudeva-reddy.github.io/NLP-Proj/#signals" "Inspect Cosine Drift formulation and metrics"
    click S2 href "https://chirudeva-reddy.github.io/NLP-Proj/#signals" "Inspect Mahalanobis Distance formulation and metrics"
    click S3 href "https://chirudeva-reddy.github.io/NLP-Proj/#signals" "Inspect Logit Lens Divergence formulation and metrics"
    click S4 href "https://chirudeva-reddy.github.io/NLP-Proj/#signals" "Inspect PCA Residual Deviation formulation and metrics"
    click S5 href "https://chirudeva-reddy.github.io/NLP-Proj/#evidence" "Inspect Causal Indirect Effect & Patching"
    click D href "https://chirudeva-reddy.github.io/NLP-Proj/#bench" "Explore Benchmark Fusion & Bootstrap Intervals"
    click E href "https://chirudeva-reddy.github.io/NLP-Proj/#onset" "Inspect Pre-Onset Temporal Precedence (t-2 Peak)"
Loading

Pipeline architecture

Click the diagram to open the live interactive console on GitHub Pages.

Note on the "5 signals": Four representation signals are fused into the frozen composite shipped in stats.pt. CIE is computed per token for causal localisation, not fusion (composite_features in the configuration specifies four features).


The Five Internal Drift Signals

Signal What It Measures Mathematical Formulation
1 · Cosine Drift
δ⁽ˡ⁾ₜ
AUROC 0.5989 · w 0.383
Trajectory instability: angular rotation of the residual stream between consecutive answer positions. Grounded continuations evolve smoothly; fabrications make discontinuous angular jumps.

$$\delta_t^{(\ell)} = 1 - \frac{h_t^{(\ell)} \cdot h_{t-1}^{(\ell)}}{\lVert h_t^{(\ell)} \rVert_2 ; \lVert h_{t-1}^{(\ell)} \rVert_2}$$

2 · Mahalanobis
m⁽ˡ⁾ₜ
AUROC 0.5389 · w 0.164
Off-manifold displacement from the empirical distribution of faithful tokens. Mean vector $\mu$ and covariance $\Sigma$ are estimated strictly on the training split.

$$m_t^{(\ell)} = \sqrt{(h_t^{(\ell)} - \mu_\ell)^\top \Sigma_\ell^{-1} (h_t^{(\ell)} - \mu_\ell)}$$

3 · Logit Lens KL
Λ⁽ˡ⁾ₜ
AUROC 0.5732 · w 0.206
Cross-depth disagreement: projects intermediate layers directly through the unembedding matrix $W_U$ and measures KL divergence between distributions with evidence vs without it.

$$\Lambda_t^{(\ell)} = D_{\text{KL}}\Big(\hat{P}_t^{(\ell)}(\mathcal{D}) ;\big\Vert; \hat{P}_t^{(\ell)}(\emptyset)\Big)$$

4 · PCA Residual
ρ⁽ˡ⁾ₜ
AUROC 0.5490 · w 0.247
Energy escaping outside the 16-component principal subspace spanned by faithful activations. Captures orthogonal directional departures rather than scaled variance.

$$\rho_t^{(\ell)} = \Big\lVert h_t^{(\ell)} - V_\ell V_\ell^\top h_t^{(\ell)} \Big\rVert_2$$

5 · Causal IE
CIE(c)
AUROC 0.5138 · Localisation
Direct causal intervention: swaps activations between faithful and hallucinated forward passes in both directions, measuring shifts in target-token generation probability.

$$\text{CIE}(c) = P_{\mathcal{M}}^{\text{patch}(c)}(y_t^{\ast}) - P_{\mathcal{M}}^{\text{corrupt}}(y_t^{\ast})$$


Benchmark Results

RAGTruth held-out test split · 2,655 responses · 38.9% hallucination rate · 1,000-iteration bootstrap CIs

Detector AUROC ↑ 95% Bootstrap CI F1 Span ↑ Spearman ρ ↑ ECE ↓
Attention Entropy (baseline B1) 0.6106 [0.5891, 0.6318] 0.5351 0.1869 0.2230
Logit Confidence (baseline B2) 0.5497 [0.5274, 0.5716] 0.4737 0.0839 0.1249
Cosine Drift 0.5989 [0.5772, 0.6201] 0.5181 0.1670 0.0472
Mahalanobis Distance 0.5389 [0.5168, 0.5612] 0.4528 0.0657 0.0814
Logit Lens Divergence 0.5732 [0.5510, 0.5954] 0.4836 0.1237 0.1112
PCA Deviation 0.5490 [0.5269, 0.5711] 0.4437 0.0828 0.0778
CIE (Top-3 Layers) 0.5138 [0.4916, 0.5360] 0.4286 0.0233 0.0824
✨ Full Composite Detector 0.6511 [0.6307, 0.6614] 0.5548 0.2553 0.0678

Production Context for Technical Recruiters & Engineers: An AUROC of 0.6511 is a validated single-pass mechanistic signal. It delivers +0.0405 AUROC over attention-entropy, a 3.3× reduction in calibration error (0.2230 down to 0.0678), and closes 19.51% of the performance gap to multi-pass ReDeEP (0.82) : all from a single forward pass of a lightweight 1.5B model with 0 API sampling overhead. Explore the interactive benchmark table →


In-Depth Experimental Analyses

🕐 Pre-onset temporal precedence (E4) : The core discovery

Aligning every hallucinated span at its initial hallucinated token ($t = 0$) and evaluating leading tokens, cosine drift peaks at $t-2$ (Mann–Whitney U test, $p = 2.86 \times 10^{-5}$).

The transformer's internal representations leave the faithful manifold two tokens prior to committing the erroneous content to text, providing the physical window required for early pre-emption.

Token Offset Cosine Drift δ Mahalanobis m Logit Lens Λ Spearman ρ CIE
t−3 0.2668 37.9664 3.6684 87.4574 37.7385
t−2 (peak) 0.2872 34.0917 3.5820 80.0704 35.6801
t−1 0.2821 31.4590 4.2375 72.2008 31.7477
t (onset) 0.2729 36.5722 3.8713 85.6095 36.3971
t+1 0.2708 38.1772 3.5158 88.1041 38.2783
Temporal Precedence Plot
🔍 Inspect the mounted E4 figure in the live gallery · 1-click CLI reproduction and lightbox inspection
🎯 Causal localisation via bidirectional patching (E3)

Across 50+ paired counterfactual examples, activation states were swapped between faithful and hallucinated executions in both directions. Early self-attention layers dominate causal influence by an order of magnitude, with every component group passing statistical significance at $p &lt; 0.05$.

Component Group Depth Range CIE (f → h) CIE (h → f) Significance
Early Attention 1–25% −1.0991 −1.0782 $p &lt; 0.05$
Mid FFN 26–75% −0.0550 −0.0299 $p &lt; 0.05$
Late FFN 76–100% −0.2732 −0.1608 $p &lt; 0.05$
Copying Heads last 25% −0.0807 −0.0358 $p &lt; 0.05$
Bidirectional CIE Plot
🔍 Inspect the mounted E3 figure in the live gallery · 1-click CLI reproduction and lightbox inspection
🌍 Zero-shot cross-domain transfer to HaluEval (E5)

The frozen composite model (trained weights, medians, and IQRs unchanged) was applied directly to HaluEval-QA without retraining. The composite score demonstrates remarkable domain stability ($+0.0058$ AUROC delta), whereas individual standalone signals exhibit high variance.

Detector RAGTruth AUROC HaluEval AUROC Δ (RAGTruth − HaluEval) Behaviour
Full Composite 0.6508 0.6450 +0.0058 Stable & Robust
Cosine Drift 0.6244 0.8345 −0.2102 Transfers upward
Mahalanobis Distance 0.5190 0.7006 −0.1815 Transfers upward
Logit Lens Divergence 0.5917 0.4337 +0.1580 Domain sensitive
PCA Residual Deviation 0.5764 0.7186 −0.1421 Transfers upward
🧱 Layer localisation, component drift & failure traces (E2, E6, E7)

Point-biserial correlation across all 28 Qwen layers indicates discriminative signal heavily concentrates in layers 15 through 25. The top three predictive layers identified are 21, 23, and 22.

Layer Profile

Decomposing the residual updates into self-attention vs FFN sub-layers shows FFN key-value memories driving late-stage drift:

Component Drift

Deterministic failure traces:

False Negative (#1289) False Positive (#12310) Metric Disagreement (#3574)

Quickstart & Live Scoring CLI

Clone the repository and install requirements:

git clone https://github.com/Chirudeva-Reddy/NLP-Proj.git && cd NLP-Proj
python3 -m venv .venv && source .venv/bin/activate
pip install -r Requirements.txt

Score any context and answer pair locally using the pre-fitted weights in stats.pt:

python NLP-sub/scripts/live_demo.py --profile local \
  --input-file NLP-sub/examples/live_demo_inputs/eiffel_tower_hallucinated_passage.json \
  --show-aggregates
View Sample Output : Suspicious-token ranking and sample aggregates
Consensus suspicious-token ranking
rank | idx | token         | consensus | mahalanobis | logit_lens | pca_deviation
-----+-----+---------------+-----------+-------------+------------+--------------
   1 |  18 | <sp>the       |    0.9206 |     75.3978 |     9.6270 |      124.8376
   2 |  14 | 9             |    0.8095 |     79.2931 |     4.8447 |      132.1120
   3 |  15 | 0             |    0.8095 |     72.6667 |     7.6290 |      116.7789
   4 |  19 | <sp>Olympic   |    0.7937 |     69.3927 |     6.0547 |      127.3764
   5 |  13 | 1             |    0.7302 |     68.6997 |     5.4883 |      123.5343

Aggregate scores (max)
composite            0.4305

The top flagged tokens isolate the fabricated year 1905 and invented event Olympic Games : content entirely unsupported by the ground-truth document.

Score your own custom prompts by passing a JSON file:

{
  "instruction": "Use only the retrieved context to produce the answer.",
  "context": "Alexander Fleming discovered penicillin in 1928. Penicillin began being used clinically around 1941.",
  "passage": "Penicillin was discovered by Alexander Fleming, and it began being used clinically around 1941."
}

(Omit the "passage" key to prompt the model to generate its own answer before scoring its internal state).


Full Reproduction Runbook

Step 1 : Sequential 4-Stage Pipeline
# 1 · Run forward passes and generate float16 hidden-state artifacts
python NLP-sub/pipeline/1-infer.py --model Qwen/Qwen2.5-1.5B --layers last18 --device auto \
  --output-dir NLP-sub/outputs/artifacts

# 2 · Fit μ, Σ, and PCA basis V on TRAIN split only, freeze composite weights
python NLP-sub/pipeline/2-fit.py --artifacts-dir NLP-sub/outputs/artifacts \
  --output NLP-sub/outputs/stats.pt --pca-components 16

# 3 · Score the held-out test split with the frozen parameters
python NLP-sub/pipeline/3-score.py --artifacts-dir NLP-sub/outputs/artifacts \
  --stats NLP-sub/outputs/stats.pt --output-dir NLP-sub/outputs/scores_test --split test

# 4 · Compute AUROC, F1, Spearman ρ, ECE with 1,000 bootstrap iterations
python NLP-sub/pipeline/4-eval.py --scores-dir NLP-sub/outputs/scores_test \
  --aggregate max --n-boot 1000
Step 2 : Experiments E2 through E8
# E2 · Per-layer AUROC profiling
python NLP-sub/pipeline/plot.py --artifacts-dir NLP-sub/outputs/artifacts/test \
  --stats NLP-sub/outputs/stats.pt --output docs/images/layer_profile.png

# E3 · Bidirectional activation patching
python NLP-sub/scripts/e3_patching.py --model Qwen/Qwen2.5-1.5B --device auto \
  --output-dir NLP-sub/outputs/e3

# E4 · Pre-onset temporal precedence evaluation
python NLP-sub/scripts/e4_temporal.py

# E5 · Zero-shot HaluEval transfer benchmark
python NLP-sub/scripts/e5_halueval.py --model Qwen/Qwen2.5-1.5B-Instruct --layers last18 --device auto \
  --stats NLP-sub/outputs/stats.pt --qa-json dataset/halueval/qa_data.json \
  --artifacts-dir NLP-sub/outputs/halueval_artifacts --scores-dir NLP-sub/outputs/halueval_scores \
  --ragtruth-scores-dir NLP-sub/outputs/scores_test

# E6 · Self-attention vs FFN component drift
python NLP-sub/scripts/e6_component_drift.py --model Qwen/Qwen2.5-1.5B --device auto \
  --artifacts-dir NLP-sub/outputs/artifacts/test --output-dir NLP-sub/outputs/e6

# E7 · Failure-case qualitative traces
python NLP-sub/scripts/e7_failures.py --model Qwen/Qwen2.5-1.5B --device auto \
  --scores-dir NLP-sub/outputs/scores_test --stats NLP-sub/outputs/stats.pt \
  --e6-json NLP-sub/outputs/e6/component_drift.json --output-dir NLP-sub/outputs/e7

# E8 · SOTA gap comparison with ReDeEP and LUMINA
python NLP-sub/scripts/e8_sota_gap.py --scores-dir NLP-sub/outputs/scores_test \
  --aggregate max --output-dir NLP-sub/outputs/e8
Step 3 : Rebuilding the GitHub Pages Demo Site

The static demo site located in docs/ is served via GitHub Pages. It dynamically loads docs/site/demo_runs.json, generated by scoring input cases:

cd NLP-sub
python scripts/export_demo_json.py --stats outputs/stats.pt --output ../docs/site/demo_runs.json

# Local preview
cd ../docs && python3 -m http.server 8000

Repository Structure

NLP-Proj/
├── README.md                      # Primary project overview & documentation
├── Requirements.txt               # Python package dependencies
├── METRICS_VIVA.md                # Viva defense reference: derivations & logic
├── docs/
│   ├── index.html                 # Interactive GitHub Pages portfolio site
│   ├── site/demo_runs.json        # Pre-computed scoring runs for the web console
│   ├── CS_F429_Project_Report.pdf # Formal academic research report (14 pages)
│   ├── PIPELINE_RUNBOOK.md        # Pipeline execution instructions
│   └── images/                    # Figures, architecture diagrams, and traces
└── NLP-sub/
    ├── COMPREHENSIVE.md           # Deep codebase architecture guide
    ├── pipeline/                  # 1-infer · 2-fit · 3-score · 4-eval · plot
    ├── src/
    │   ├── dataset.py             # Grouped train/val/test split generator
    │   ├── inference.py           # Forward hook registration & hidden extraction
    │   ├── metrics.py             # Mathematical implementations of 5 drift signals
    │   ├── scoring.py             # Robust z-score composite scoring engine
    │   ├── evaluate.py            # Evaluation metrics (AUROC, F1, Spearman, ECE)
    │   ├── component_outputs.py   # Activation replay hooks for FFN/attention
    │   └── halueval.py            # HaluEval dataset integration
    ├── scripts/                   # Experiments E3-E8, live demo, and export
    ├── examples/live_demo_inputs/ # Pre-formatted input test scenarios
    └── outputs/                   # Cached artifacts, stats.pt, scores (git-ignored)

Key Documentation & Links

Resource Scope & Purpose
🌐 Live Interactive Demo Online token-by-token scoring console with real model activations
📊 Figure Exhibition Gallery Mounted research plots with scroll reveals, category filters, and lightbox
📄 Research Paper (14 Pages, PDF) Formal 14-page manuscript: theoretical basis, protocol, contributions, and signed contribution statement
📖 Pipeline Runbook Comprehensive CLI reference, flag specifications, parameter tuning
🛡️ Viva Defense Notes Mathematical proofs, component failure analysis, viva Q&A
📚 Codebase Architecture Source code deep-dive and modular breakdown

Authors & Acknowledgments

CS F429 Natural Language Processing · BITS Pilani, Dubai Campus
Supervised by Prof. Elakkiya Rajasekar · May 2026

Author Student ID Core Contribution Area
Sanya Wadhawan 2023A7PS0296U Methodology design, dataset processing, RAGTruth pipeline
Chirudeva Reddy 2023A7PS0331U Experimental architecture, composite metric fusion, causal patching
Yusra Hakim 2022A7PS0004U Related work synthesis, mechanistic interpretability review
Joseph Cijo 2022A7PS0019U Baseline implementations, result benchmarking, documentation

Citation

@techreport{wadhawan2026pregen,
  title       = {Pre-Generation Hallucination Detection via Internal Representation Drift},
  author      = {Wadhawan, Sanya and Reddy, Chirudeva and Hakim, Yusra and Cijo, Joseph},
  institution = {BITS Pilani, Dubai Campus},
  course      = {CS F429: Natural Language Processing},
  year        = {2026},
  month       = {May},
  url         = {https://github.com/Chirudeva-Reddy/NLP-Proj}
}

Built for NLP research at BITS Pilani, Dubai Campus · MIT Licensed

▶ Open the Live Interactive Demo  ·  📄 Read the Research Paper (14 Pages, PDF)

About

Pre-generation hallucination detection in RAG systems using transformer hidden-state dynamics and internal representation drift signals. In layman terms: Can we understand/rule out which hidden states contribute to hallucination before the generation even happens.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages