Reproducible representation-geometry analysis for transformer language models.
Project description
representation-geometry
representation-geometry is a Python toolkit for measuring representation geometry inside transformer models. It collects internal activations with PyTorch hooks, updates streaming covariance statistics, computes spectral and subspace measurements, and saves reproducible artifact bundles.
The package follows the project vision in VISION.md: it provides measurement infrastructure, not paper-specific interpretation. It is model-agnostic where possible, stores reusable quantitative outputs, and stays isolated from manuscript code, figures, tables, and LaTeX files.
Features (Current)
- Analyze loaded
torch.nn.Modulemodels or Hugging Face causal language model ids. - Collect residual block inputs, residual block outputs, or named submodule inputs/outputs.
- Accumulate covariance statistics with a streaming Welford-style backend.
- Compute spectral geometry metrics, sample-SVD metrics, and between-layer novelty metrics.
- Optionally run a normalization ablation diagnostic.
- Optionally collect simple MoE router load statistics when model outputs expose router logits.
- Save JSON, CSV, or bundle artifacts with versioned metadata, metrics, diagnostics, router stats, and eigenvalues.
Architecture Overview
The package is organized into four layers that can evolve independently:
- Activation collection through PyTorch hooks.
- Streaming statistical backends for covariance and sampled activations.
- Geometry metrics and diagnostics that consume those statistical objects.
- Artifact generation for DataFrames, JSON, CSV, NumPy arrays, and reproducibility metadata.
This separation is intentional. New measurements should be implemented against the statistical backend rather than directly inside model-specific collection code.
Why Streaming?
Many activation-analysis scripts store all hidden states before computing metrics. representation-geometry instead updates statistics incrementally whenever possible. The current covariance backend keeps streaming means, covariance numerators, and a bounded activation sample, which allows larger token budgets without keeping every activation in memory.
Dense covariance is still hidden_dim x hidden_dim, so very wide models require care, but the package is designed around bounded-memory measurement rather than one-off activation dumps.
Reproducibility
Every saved artifact bundle records the measurement protocol, not just the final numbers. Metadata includes the artifact schema version, model information, hook targets, selected layers, token budget, observed tokens, metric and diagnostic configuration, tokenizer and dataset labels when supplied, software versions, runtime, and optional user metadata.
The goal is for downstream research projects to reproduce the measurement independently of the original paper scripts.
Install
From this directory:
python -m pip install -e ".[dev]"
For Hugging Face loading and examples:
python -m pip install -e ".[dev,hf]"
The core package depends on numpy, pandas, and torch. The hf extra installs transformers, accelerate, datasets, and safetensors.
Development tooling is included in the dev extra: pytest, ruff, and build.
Quick Start
from representation_geometry import analyze_model
results = analyze_model(
model_name=model,
dataloader=dataloader,
max_tokens=100_000,
layers="all",
hook_point="residual_input",
metrics="default",
save_mode="bundle",
save_dir="runs/my_model",
tokenizer_name="my-tokenizer",
dataset_name="my-dataset",
)
print(results.metrics.head())
print(results.novelty.head())
print(results.metadata)
model_name may be:
- a loaded
torch.nn.Module, - a Hugging Face model id string, if
transformersis installed, - or omitted when using the explicit
model=argument.
For large models, prefer passing a preloaded model so you control dtype, device placement, quantization, and device_map.
Dataloader Format
dataloader must be an iterable of model batches. Dictionary batches with input_ids are preferred:
yield {
"input_ids": input_ids,
"attention_mask": attention_mask,
}
The package moves tensors to the selected input device recursively. It calls the model under torch.no_grad() and tries use_cache=False for transformer-style forward methods.
max_tokens is a stopping budget based on observed batch tokens. The current implementation processes whole batches, so tokens_observed can exceed max_tokens on the final batch.
Hook Points
The default hook is the input to each transformer block:
analyze_model(model_name=model, dataloader=dataloader, hook_point="residual_input")
Supported hook points are:
residual_input: forward pre-hook on transformer blocks.residual_output: forward hook on transformer blocks.module_input:<module_path>: forward pre-hook on a named submodule.module_output:<module_path>: forward hook on a named submodule.
Examples:
analyze_model(model_name=model, dataloader=dataloader, layers=[0, 3, 7])
analyze_model(
model_name=model,
dataloader=dataloader,
hook_point="module_output:blocks.0.proj",
)
For residual hooks, transformer blocks are auto-detected from common containers such as .transformer.h, .model.layers, .gpt_neox.layers, .decoder.layers, .layers, .blocks, or .h.
For named module hooks, use paths from dict(model.named_modules()). Named module hooks currently collect a single target and should be called with the default layers="all".
Metrics
metrics="default", metrics=None, or metrics=[] computes all currently implemented metric families:
spectrumsamplenovelty
You can request a narrower set:
analyze_model(model_name=model, dataloader=dataloader, metrics=["spectrum"])
Spectrum Metrics
Computed from covariance eigenvalues:
d_prd_pr_normd_entropyd_entropy_normstable_ranktop1_sharetop10_sharetop32_sharek90k95log_spectrum_slope_10_200
The near-rank-1 criterion used by the original paper workflow can be computed downstream:
near_rank1 = (results.metrics["top1_share"] >= 0.99) & (results.metrics["k90"] <= 1)
Activation Sample Metrics
Computed from a bounded activation sample stored by the streaming accumulator:
sv_effective_ranksv_stable_ranksv_participation_ratiocos_sim_meancos_sim_stdve_4ve_8ve_16ve_32
The sample size is controlled by sample_limit, default 512.
Novelty Metrics
Computed between consecutive collected layers:
feature_subspace_noveltylinear_ckalayer_cosine_sim
novelty_k controls the number of top covariance eigenvectors used for feature-subspace novelty.
Diagnostics
Currently supported diagnostics:
diagnostics=["normalization_ablation"]
This diagnostic recomputes spectrum metrics on sampled activations under:
rawtoken_l2token_rmsfeature_standardized
Diagnostics are stored in results.diagnostics and saved under diagnostics/ when save_mode is enabled.
Extending The Package
The current public API does not yet accept arbitrary user-defined metrics at runtime. Internally, however, metrics are already separated from activation collection: api.py collects activations into streaming statistics, and metrics.py computes measurements from those statistics.
To add a new built-in metric today, implement it as a focused function in metrics.py, thread it through the metric-family selection in api.py, add tests, and document the new output columns. Diagnostics follow the same pattern through the small DIAGNOSTIC_REGISTRY in diagnostics.py.
This is a stepping stone toward the longer-term vision of a registry-style metric API.
Outputs
analyze_model returns an AnalysisResults object:
results.metrics # pandas.DataFrame
results.novelty # pandas.DataFrame
results.router # pandas.DataFrame, empty unless router stats are available
results.diagnostics # dict[str, pandas.DataFrame]
results.eigenvalues # dict[str, numpy.ndarray]
results.metadata # dict
results.artifact_dir # pathlib.Path after save()
Supported save modes:
save_mode=None: no files are written.save_mode="json": writes JSON tables.save_mode="csv": writes CSV tables.save_mode="bundle": writes CSV tables plus bundle metadata and eigenvalues.
Saved bundle contents:
metrics.csv or metrics.json
novelty.csv or novelty.json
router.csv or router.json # only when router stats are available
diagnostics/* # only when diagnostics are requested
eigenvalues.npz
metadata.json
Metadata includes artifact_schema_version, model id, model class, model config summary, token budget, observed tokens, batch count, hook point, hook targets, selected layers, metric names, diagnostic names, tokenizer label, dataset label, input device, software versions, runtime, save mode, and optional run_metadata.
Hugging Face Usage
Small runnable example:
from pathlib import Path
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from representation_geometry import analyze_model
model_id = "hf-internal-testing/tiny-random-gpt2"
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(model_id)
model.eval()
tokens = tokenizer(
"Residual geometry measures covariance structure inside transformer layers.",
return_tensors="pt",
)
results = analyze_model(
model_name=model,
dataloader=[tokens],
max_tokens=16,
layers=[0],
save_mode="json",
save_dir=Path(".tmp") / "hf_usage_example",
tokenizer_name=model_id,
dataset_name="inline example sentence",
)
print(results.metrics[["layer", "d_pr_norm", "top1_share", "k90"]])
The same pattern is available as:
python examples/hf_tiny_random_gpt2.py
Examples
Tiny in-memory model with no Hugging Face dependency:
python examples/tiny_in_memory.py
GPT-2 Hugging Face example:
python examples/minimal_hf_gpt2.py
Tiny Hugging Face model example:
python examples/hf_tiny_random_gpt2.py
Package smoke test:
python -m representation_geometry.smoke --out-dir .tmp/smoke
After installation, the console script is:
representation-geometry-smoke --out-dir .tmp/smoke
Tests
Run the test suite:
python -m pytest
Run linting:
python -m ruff check .
Build a wheel and source distribution:
python -m build
The tests cover covariance streaming, metric formulas, diagnostics, JSON/bundle saving, named module hooks, residual output hooks, and metric subset selection.
Current Limitations
analyze_modelcurrently loads Hugging Face ids withAutoModelForCausalLM.- Residual block discovery uses common transformer container names; uncommon architectures may need named module hooks.
- Named module hooks collect one target per call.
- Covariance matrices are stored on CPU as dense
hidden_dim x hidden_dimtensors, so very large hidden dimensions still need care. - Distributed collection, sketching, randomized eigensolvers, dashboards, multimodal-specific helpers, and causal intervention APIs are vision items, not implemented yet.
Design Boundary
This package owns reusable measurement infrastructure:
- activation collection,
- streaming statistics,
- geometry metrics,
- diagnostics,
- artifact serialization.
It deliberately does not own:
- paper prose,
- LaTeX generation,
- publication tables,
- manuscript figures,
- benchmark leaderboards,
- research conclusions.
Downstream projects should interpret the measurements; representation-geometry should make the measurements reproducible.
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 representation_geometry-0.1.0.tar.gz.
File metadata
- Download URL: representation_geometry-0.1.0.tar.gz
- Upload date:
- Size: 24.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e317081417902cd14653999626c94881a85b4ac6eab6c7c0576ce1a7463f2afd
|
|
| MD5 |
6fa93c296ef4e904a0b80f2e8d6e653e
|
|
| BLAKE2b-256 |
2169fb826e94ea699db6fb6f56d2cc4b03ab2e1d4af097db9da314bbcf385f1c
|
File details
Details for the file representation_geometry-0.1.0-py3-none-any.whl.
File metadata
- Download URL: representation_geometry-0.1.0-py3-none-any.whl
- Upload date:
- Size: 21.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5a2ae44e2de6c7b48d91cc0bd088166e1282d688b0ce2f7827a34f6c6e742e85
|
|
| MD5 |
41754fd2ef8804b37a9042a02cb705f5
|
|
| BLAKE2b-256 |
ae2745bf04c8d834a6f50c78064f1f846558f64b977a260b09f6e83b32ec66e6
|