You have two checkpoints of the same architecture and you want to know, precisely, where they differ. This recipe records both on the same input, aligns the recordings, and produces a layer-wise diff you can read.
Problem
Two BERT checkpoints — base and base-finetuned — give different answers on the same prompt. You want to attribute the disagreement to specific layers.
Prerequisites
- NeuronScope
v0.9.2installed with the[torch,hf]extras. - Both checkpoints reachable from Hugging Face or a local path.
- A single input the two models would legally accept.
Recipe
compare_models.py··python
import neuronscope as ns
from transformers import AutoModel, AutoTokenizer
BASE = "google-bert/bert-base-uncased"
TUNED = "my-org/bert-base-uncased-finetuned"
tok = AutoTokenizer.from_pretrained(BASE)
prompt = ns.Prompt("The quick brown fox jumps over the lazy dog.")
inputs = tok(prompt.text, return_tensors="pt")
# Record both on the same input with the same probe.
probe = ns.probes.LayerActivations(layers="encoder.layer.*")
with ns.record(model=AutoModel.from_pretrained(BASE), probe=probe) as a:
a.forward(**inputs)
with ns.record(model=AutoModel.from_pretrained(TUNED), probe=probe) as b:
b.forward(**inputs)
# Fingerprint check — same architecture, same input.
ns.assert_matches(a.model, b.model, kind="architecture")
ns.assert_matches(a.prompt, b.prompt, kind="prompt")
# Structured diff.
diff = ns.compare(a.artifact, b.artifact, metric="cosine")
diff.summary().to_markdown("compare.md")Expected output
shellfocus a line, then ⌘/Ctrl+C or Enterbash
$ cat compare.md | head -20$ # Comparison — base.nsz vs tuned.nsz
| layer | cosine | l2 | verdict |
| ------------------------- | ------ | ------- | ------- |
| encoder.layer.0.output | 0.998 | 0.031 | equal |
| encoder.layer.1.output | 0.994 | 0.048 | equal |
| encoder.layer.2.output | 0.981 | 0.126 | drift |
| encoder.layer.3.output | 0.942 | 0.310 | drift |
| encoder.layer.4.output | 0.812 | 0.892 | large |
| encoder.layer.5.output | 0.734 | 1.204 | large |
| encoder.layer.6.output | 0.681 | 1.517 | large |
| ...Interpreting the diff
The table above is the shape you want: near-equal representations in the early layers, then a clean divergence starting at layer 4. That is the signature of fine-tuning that reused the embedding and lower encoder stack and rewrote the higher layers.
Variants
- Batch input. Pass a list of prompts and get one diff per prompt. Aggregate with
diff.by_prompt(). - Sub-selecting layers. Restrict the probe with a glob:
layers="encoder.layer.[4-9].*". - Cross-checkpoint CI. Save the reference
.nszin your repo and compare against it in CI — see the CI/CD recipe.