Skip to main content

NNDbg — a diagnostic toolkit for neural networks

CI Python 3.10+ License: MIT

NNDbg wraps a PyTorch or HuggingFace model in a single Inspector and answers the questions people actually ask when interpreting a neural network: where a concept is encoded, which inputs caused an output, what each attention head is doing, which layer causally produces a behaviour, what features a layer's activations decompose into, and how similar the representations of different layers (or different models) are.

Analysis planes

inspector.<plane> Answers Method
probing Where is concept X encoded? cross-validated linear / SVM / MLP probes
attribution Which inputs caused this output? saliency, gradient×input, SmoothGrad, IG, Grad-CAM
attention What does each head attend to? per-head heatmaps, rollout, entropy
patching Which layers causally produce a behaviour? causal tracing, mean ablation
sae What sparse features does a layer learn? sparse autoencoder (ReLU or top-k)
latent Where do activations sit in a compressed space? VAE latent space + anomaly detection
geometry How similar are layers to each other (or to another model)? linear CKA, PCA, UMAP
neurons What is each neuron doing? dead neurons, top examples, kurtosis
erasure How do I remove a concept from representations? INLP null-space projection

Every result is a plain dataclass with a .plot() method (matplotlib, zero-config) and an optional .plotly() method if you have plotly installed.

Installation

pip install nndbg

# with interactive Plotly figures
pip install nndbg[plotly]

Quick start

from transformers import AutoModelForCausalLM, AutoTokenizer
from nndbg import Inspector

model = AutoModelForCausalLM.from_pretrained("gpt2")
tokenizer = AutoTokenizer.from_pretrained("gpt2")
inspector = Inspector(model, tokenizer)

inspector.summary()          # model + all available planes
inspector.layers()[:5]       # every layer name you can pass to any plane

# Attribution — which input tokens drove the prediction?
input_ids = tokenizer("The capital of France is", return_tensors="pt").input_ids
inspector.attribution.saliency(input_ids).plot()
inspector.attribution.smoothgrad(input_ids).plot()   # noise-averaged saliency

# Attention — what does head 0 of layer 0 attend to?
inspector.attention.heads(input_ids, layer=0).plot()
inspector.attention.rollout(input_ids).plot()         # cumulative information flow

# Probing — is sentiment decodable, and from which layer?
dataset = [
    (tokenizer(text, return_tensors="pt").input_ids, label)
    for text, label in [
        ("I love this movie", 1), ("I hate this movie", 0),
        ("This is wonderful", 1), ("This is terrible", 0),
    ]
]
inspector.probing.fit(dataset, concept="sentiment").plot()
inspector.probing.fit(dataset, concept="sentiment", method="svm").plot()

Any plain nn.Module works too — tokenizer is optional and only needed for token-level labeling.

Activation patching / causal tracing

clean = tokenizer("The Eiffel Tower is in the city of", return_tensors="pt").input_ids
corrupted = tokenizer("The Space Needle is in the city of", return_tensors="pt").input_ids

# Causal trace: which (layer, position) recovers the clean prediction?
result = inspector.patching.causal_trace(
    clean, corrupted, layers=inspector.find_layers(r"h\.\d+$")
)
result.plot()  # (layer × position) logit-recovery heatmap

# Mean ablation: which positions carry above-average information?
corpus = [tokenizer(t, return_tensors="pt").input_ids for t in texts]
inspector.patching.mean_ablation(clean, corpus).plot()

Sparse autoencoders and VAE latent analysis

dataset = [tokenizer(t, return_tensors="pt").input_ids for t in texts]

# Sparse feature decomposition (ReLU or exact top-k)
inspector.sae.train(dataset, layer="transformer.h.6", n_features=512)
inspector.sae.train(dataset, layer="transformer.h.6", n_features=512, activation="topk:32")
inspector.sae.decompose(dataset, layer="transformer.h.6").plot()

# Compressed latent space + reconstruction-error anomaly detection
inspector.latent.train(dataset, layer="transformer.h.6", latent_dim=2)
result = inspector.latent.encode(dataset, layer="transformer.h.6")
result.plot()
result.anomalies()  # indices of outlier examples

Representational geometry

# How similar are the layers to each other?
layers = inspector.find_layers(r"h\.\d+$")
inspector.geometry.layer_similarity(dataset, layers=layers).plot()

# How much did fine-tuning change each layer?
base_inspector = Inspector(base_model, tokenizer)
inspector.geometry.compare(base_inspector, dataset, layers=layers).plot()

# 2-D PCA scatter of a layer's representations
inspector.geometry.pca(dataset, layer="transformer.h.6", labels=class_labels).plot()

Neuron analysis and concept erasure

# Dead neurons, top-activating examples, polysemanticity proxy
result = inspector.neurons.stats(dataset, layer="transformer.h.6.mlp.c_fc")
print(result)                          # NeuronResult(... dead=12/3072 ...)
result.plot()                          # bar chart, dead neurons in red
result.polysemantic_neurons()          # low-kurtosis (broadly-activating) indices

# Remove a concept from a layer's representations (INLP)
result = inspector.erasure.inlp(dataset, concept="sentiment", layer="transformer.h.4")
result.plot()                          # probe accuracy decay over iterations
erased = result.apply(some_activations)  # project new activations through erasure

Development

git clone https://github.com/Darsh-Nandu/neural-network-debugger
cd neural-network-debugger
pip install -e ".[dev]"
ruff check nndbg tests
pytest

Release files for nndbg 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for nndbg 0.2.1
File Size Uploaded
nndbg-0.2.1.tar.gz 43.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for nndbg 0.2.1
File Interpreter ABI Platform
nndbg-0.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 94.8 kB

Release files / nndbg-0.2.1.tar.gz

Download URL nndbg-0.2.1.tar.gz
Size 43.4 kB
Tags Source
SHA-256 checksum
How to use checksums
b07f4549b47f10c15e9f5462d1d7a7334646423b20fad228d559a937e0856b75
BLAKE2b-256 checksum
How to use checksums
07c9c4fa0e292de67fcdd4cbfcf2096c548126e884735a8f07623769f803ccf4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.4

Release files / nndbg-0.2.1-py3-none-any.whl

Download URL nndbg-0.2.1-py3-none-any.whl
Size 51.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a1b4240f9998a667225c40f2a644ead2e0497c549ad9f830c8340ffdb5983016
BLAKE2b-256 checksum
How to use checksums
49542d93e5dacad57fd2a92952e4c97fed32a89b3365b3d12d6e97aa9df06206
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.4

Release history Release notifications | RSS feed

This release

0.2.1 This release

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page