A focused, practical toolkit for diagnosing and interpreting neural networks: probing, attribution, attention, activation patching, sparse autoencoders, VAE latent analysis, CKA geometry, neuron analysis, and concept erasure.
Project description
NNDbg — a diagnostic toolkit for neural networks
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
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file nndbg-0.2.1.tar.gz.
File metadata
- Download URL: nndbg-0.2.1.tar.gz
- Upload date:
- Size: 43.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b07f4549b47f10c15e9f5462d1d7a7334646423b20fad228d559a937e0856b75
|
|
| MD5 |
4869aec63f971c44d0bc9d0fc458fd81
|
|
| BLAKE2b-256 |
07c9c4fa0e292de67fcdd4cbfcf2096c548126e884735a8f07623769f803ccf4
|
File details
Details for the file nndbg-0.2.1-py3-none-any.whl.
File metadata
- Download URL: nndbg-0.2.1-py3-none-any.whl
- Upload date:
- Size: 51.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a1b4240f9998a667225c40f2a644ead2e0497c549ad9f830c8340ffdb5983016
|
|
| MD5 |
37bc1a84699c1108fae3e1f404497930
|
|
| BLAKE2b-256 |
49542d93e5dacad57fd2a92952e4c97fed32a89b3365b3d12d6e97aa9df06206
|