Command-line interface¶
After pip install -e . (or conda env create -f environment.yml) the
token-heatmap command is on your PATH.
YAML config files¶
All trace flags can be set in a YAML file so you don't have to repeat long
command lines. CLI flags always override YAML values.
pip install pyyaml # one-time; only needed for --config
token-heatmap trace --config configs/example.yaml
token-heatmap trace --config configs/example.yaml --max-new-tokens 128 # override one field
configs/example.yaml (included in the repo):
model: Qwen/Qwen2.5-0.5B-Instruct
prompt: "Explain what a large language model is in one sentence."
max_new_tokens: 64
temperature: 0.8
top_p: 0.95
out: outputs/example-run
capture_logit_lens: true
All keys are optional — any missing key falls back to the CLI default.
Basic trace¶
token-heatmap trace \
--model Qwen/Qwen2.5-0.5B-Instruct \
--prompt "Explain diffusion models." \
--max-new-tokens 80 \
--temperature 0.8 \
--top-p 0.95 \
--min-k 8 --max-k 64 --mass-threshold 0.95 \
--out outputs/
Output directory:
outputs/
├── generated.txt
├── adaptive_token_trace.csv
├── adaptive_token_trace.json
├── adaptive_heatmap.png
├── entropy.png
└── selected_probability.png
Run token-heatmap trace --help for the full flag list.
Viewing the result¶
trace only writes the bundle to disk. To explore it, drag
adaptive_token_trace.json onto the web viewer (cd app && npm run dev),
or open it in the desktop app. See web-app.md.
Inspecting attention and the logit lens¶
AttentionProbe and LogitLens are wired through token-heatmap trace as
opt-in flags. They are off by default because forcing eager attention is
significantly slower than the SDPA / FlashAttention kernel.
token-heatmap trace \
--model Qwen/Qwen2.5-0.5B-Instruct \
--prompt "Explain attention in one sentence." \
--max-new-tokens 12 \
--capture-attention --attention-layers all --attention-top-k 8 \
--capture-full-attention \
--capture-logit-lens --lens-layers 0,3,7,11 --lens-top-k 5 \
--out outputs/inspect/
| Flag | Meaning |
|---|---|
--capture-attention |
Attach an AttentionProbe. Forces eager attention (slow). |
--attention-layers |
all (default) or comma-separated layer indices (e.g. 0,3,7,11). |
--attention-top-k |
Top-k attended positions kept inline per head (default 8). |
--capture-full-attention |
Also write a Tier-2 attention.<step>.npz per step under <out>/attention/. Implies --capture-attention. |
--capture-logit-lens |
Attach a LogitLens. |
--lens-layers |
Same syntax as --attention-layers. |
--lens-top-k |
Top-k tokens retained per layer (default 8). |
Extra output files when these flags are active:
adaptive_token_trace.json— includes inline attention aggregates, per-layer logit-lens projections, andattention_sidecar_refpointers.attention_layer_head_grid.png— per-step layer × head entropy grid.logit_lens.png— per-layer top-k table (first step).selected_rank_heatmap.png— selected-token rank by layer × step.
The Logit Lens tab in the web app shows this data interactively, synced to the heatmap cursor.
Capturing activations¶
ActivationProbe captures per-layer / per-submodule summary statistics and,
optionally, full activation tensors as .npz sidecars.
token-heatmap trace \
--model Qwen/Qwen2.5-0.5B-Instruct \
--prompt "Explain attention in one sentence." \
--max-new-tokens 12 \
--capture-activations \
--activation-layers all \
--activation-submodules residual_post,mlp_out,o_proj \
--activation-top-k 8 \
--out outputs/activations/
Add --capture-full-activations to also write activations/activation.<step>.npz
(full hidden-state tensors) and embed activation_sidecar_ref pointers in the
JSON trace. It additionally embeds, inline in the trace, the TWERA neuron
ranking and Direct Logit Attribution — each generated token's logit
decomposed into per-layer attention (o_proj) and MLP (mlp_out) contributions,
expandable to per attention head — which the web app's Activations and
Attribution lenses read. Implies --capture-activations.
| Flag | Meaning |
|---|---|
--capture-activations |
Attach an ActivationProbe. Off by default. |
--activation-layers |
all (default) or comma-separated decoder layer indices. |
--activation-submodules |
Comma-separated submodule keys (default residual_post,mlp_out,o_proj). Supported: resid_pre/residual_pre, resid_post/residual_post, mlp_out/mlp.down_proj, o_proj. |
--activation-top-k |
Top-k highest-magnitude neurons retained per (layer, submodule) (default 8). |
--capture-full-activations |
Write full tensors as .npz sidecars under <out>/activations/. Implies --capture-activations. |
Comparing two activation traces¶
token-heatmap diff outA/adaptive_token_trace.json outB/adaptive_token_trace.json \
--out diff/ \
--metric l2
The subcommand projects each input's activation subset, calls
compare_activations with align="auto", and writes:
activation_diff.json— schema-shaped diff payload.activation_delta.png— stacked layer × step heatmap, one subplot per captured submodule.
The CLI refuses to diff (non-zero exit) when the two parent traces have
different metadata.prompt values or when zero steps align between them.
Manifold analysis¶
Analyze the geometry of the captured activation clouds — inspired by
“When Models Manipulate Manifolds”,
which finds a model encoding a scalar on a low-dimensional, curved (helical)
manifold. For each (layer, submodule) the analysis stacks the full per-token
activation vectors into a matrix and computes PCA spectrum + participation
ratio, TwoNN intrinsic dimension, a 2-D/3-D projection, trajectory curvature,
and FFT periodicity.
It reads the full activation vectors from the sidecars, so generate the trace
with --capture-full-activations first:
# 1. generate with full activation sidecars
token-heatmap trace --config configs/example.yaml \
--capture-activations --capture-full-activations
# 2. add the manifold analysis to the trace
token-heatmap manifold --trace outputs/adaptive_token_trace.json
This writes a top-level manifold field back into the trace (in place by
default), which the web app's Manifold tab then renders. The analysis is
pure-numpy — no torch needed to run it.
| Flag | Default | Meaning |
|---|---|---|
--trace |
required | Path to a trace JSON that has activation_sidecar_refs. |
--out |
overwrite --trace |
Write the augmented trace elsewhere instead of in place. |
--layers |
all captured | Subset of layer indices to analyze. |
--submodules |
all captured | Subset of submodule names to analyze. |
--components |
3 |
Number of PCA projection components to keep. |
Exits non-zero when the trace has no activation_metadata, carries no
activation_sidecar_ref (i.e. was generated without --capture-full-activations),
or when no (layer, submodule) cloud has at least two positions to analyze.
See interpreting.md for what the metrics mean.
After augmenting a trace with manifold, drag its
adaptive_token_trace.json onto the web viewer (cd app && npm run dev),
or open it in the desktop app — see web-app.md.
Building the frontend (web build)¶
Run npm install + npm run build in app to produce a static
dist/. The viewer is backend-free, so you can serve dist/ from any static
file server on a host with no Node.js.
token-heatmap web build # output: app/dist/
# then serve it anywhere, e.g.:
python -m http.server -d app/dist 8080
# open http://localhost:8080/ and drag a trace JSON onto the page
HPC: build the GPU venv (hpc setup)¶
Idempotently build the dedicated cu124 torch venv on the HPC so token-heatmap
runs on the GPU instead of silently falling back to CPU.
token-heatmap hpc setup # build/update the GPU venv
token-heatmap hpc setup --verify # also run a real GPU matmul check (queues a short srun)
| Flag | Default | Meaning |
|---|---|---|
--verify |
off | Also run a real GPU matmul check. |
--ssh-host |
j7zang-gpu |
SSH host alias. |
--remote-repo |
/work/j7zang/Token-Heatmap |
Repo checkout on the HPC. |
--remote-venv |
/work/j7zang/th-gpu |
GPU venv path. |
--anaconda-python |
base interpreter | Base interpreter used to create the venv. |
HPC round-trip (hpc run)¶
One command from your laptop. It uploads the config to the HPC, submits a Slurm
GPU job (the only remote step) that runs trace + manifold, polls it to
completion, then rsyncs the whole outputs/<name>/ folder back — so you view it
locally with no GPU and no tunnel (drag outputs/<name>/adaptive_token_trace.json
onto the web viewer, or open it in the desktop app). A pre-flight check refuses
runs that won't fit the GPU's VRAM before submitting.
token-heatmap hpc run configs/wrap-text.yaml --model Qwen/Qwen2.5-14B-Instruct \
--capture activations --probe line_position --extra "--max-new-tokens 320"
# 32B on one GPU: add --4bit
| Flag | Default | Meaning |
|---|---|---|
config |
required | Trace config YAML (its basename is the default run name). |
--name |
config basename | Run name → outputs/NAME locally + on the HPC. |
--model |
from config | Override the model id. |
--gpu |
rtx6000 |
GPU type (rtx6000 or l40s); both 48 GB. |
--qos |
qos_rtx6000_max / normal |
Slurm qos (defaults by GPU type). |
--mem |
64G / 28G |
Host memory (default depends on qos). |
--time |
01:00:00 |
Walltime HH:MM:SS. |
--capture |
full |
full = +attention (slower); activations = manifold-only. |
--probe |
none | Add a supervised manifold probe scalar (e.g. line_position). |
--extra |
none | Extra trace flags (e.g. --max-new-tokens 320). |
--4bit |
off | Load in 4-bit NF4 (for 32B+). |
--no-manifold |
off | Skip the manifold pass. |
--no-sync |
off | Don't git pull the HPC repo first. |
--no-pull |
off | Leave outputs on the HPC (no rsync back). |
--setup |
off | Build/verify the GPU venv on the HPC first (one-time). |
--force |
off | Skip the pre-flight "won't fit in VRAM" size check. |
Connection / path overrides also exist (--ssh-host, --remote-repo,
--remote-venv, --anaconda-python, --remote-bin-gpu, --poll-seconds); run
token-heatmap hpc run --help for the full list.