This repository reproduces and extends "Bilinear MLPs enable weight-based mechanistic interpretability" (Pearce et al., ICLR 2025). It targets:
The goal of this README is to make the codebase reproducible without extra effort, and to document the report and presentation build steps.
These commands verify the environment and run minimal tests.
# Vision (2 epochs, MPS-friendly)
./scripts/train/run_vision.sh test
# Language (quick tests)
./scripts/train/run_language.sh test
# Cross-dataset robustness (2 epochs, MNIST only)
./scripts/train/run_extension_cross_dataset.sh test
# Snellius (GPU)
conda env create -f environment.yml && conda activate bilinear_mlp
# Local (CPU/MPS)
conda env create -f environment_cpu.yml && conda activate bilinear_mlp_cpu
The notebooks/ directory contains Jupyter notebooks that reproduce all figures and results from the report. Each notebook can be run end-to-end and automatically downloads required checkpoints/results from Google Drive.
| Notebook | Description | Report Sections |
|---|---|---|
01_reproduction_vision.ipynb | Vision experiments (Section 4): eigenspectrum analysis, eigenvector visualization, ablation studies. Verifies low-rank emergence, weight decay effectiveness, and interpretable eigenvectors. | Figures 1-7 |
02_reproduction_language.ipynb | Language experiments (Section 5): correlation analysis, negation circuits, SAE training effect. Verifies low-rank approximation, negation AND-gate structure, and SAE training correlation. | Figures 8-10 |
03a_extension_cross_dataset_robustness.ipynb | Extension 1: Cross-Dataset Robustness: Tests whether regularized models learn universal "Platonic forms" (shape geometry) vs dataset-specific artifacts. Validates via EMNIST-Digits transfer, USPS domain shift, and geometric letter semantics (O→0, I→1). | Extension 1 |
03b_cp_extension.ipynb | Extension 2: CP Decomposition: Analyzes architectural rank constraints (Fixed, Lambda, Gated CP) vs emergent low-rank from regularization. Compares accuracy-interpretability trade-offs and eigenvector quality. | Extension 2 |
# Activate environment
conda activate bilinear_mlp_cpu # or bilinear_mlp for GPU
# Start Jupyter
jupyter notebook notebooks/
# Or run specific notebook from command line
jupyter nbconvert --to notebook --execute notebooks/01_reproduction_vision.ipynb
Note: Checkpoints and results are automatically downloaded on first run. All plotting code is imported from src/plot_utils/ modules.
# Base configs (4 configs x 5 seeds x MNIST+Fashion)
./scripts/train/run_vision.sh train base
# Noise sweep (Figure 4)
./scripts/train/run_vision.sh train noise
# Model size sweep (Figure 5)
./scripts/train/run_vision.sh train size
# Challenge task (Figure 6)
./scripts/train/run_vision.sh train challenge
# Adversarial robustness (Figure 7) - uses existing checkpoints
./scripts/train/run_vision.sh figures # Generates Figure 7 from noise sweep
# Generate all vision figures
./scripts/train/run_vision.sh figures
# Run all vision experiments
./scripts/train/run_vision.sh all
# Figure 8 negation circuit visualization
./scripts/train/run_language.sh figure8 --device mps
# Figure 9 correlation sweep (all 3 models)
./scripts/train/run_language.sh figure9
# Figure 10 SAE training time analysis
./scripts/train/run_language.sh figure10
# Negation discovery
./scripts/train/run_language.sh negation
# Interaction analysis
./scripts/train/run_language.sh interaction
# Generate all language figures
./scripts/train/run_language.sh figures
# Run all language experiments
./scripts/train/run_language.sh all
# Train all cross-dataset models (MNIST + EMNIST, CoM enabled)
./scripts/train/run_extension_cross_dataset.sh train all
# Generate all cross-dataset figures
./scripts/train/run_extension_cross_dataset.sh figures
# Run full pipeline (train + figures)
./scripts/train/run_extension_cross_dataset.sh all
# Train CP models (rank sweep, all modes)
./scripts/train/run_extension_cp.sh train all
# Generate all CP figures
./scripts/train/run_extension_cp.sh figures
# Run full pipeline (train + figures)
./scripts/train/run_extension_cp.sh all
# Quick test (2 epochs)
./scripts/train/run_extension_cp.sh test
checkpoints/ (vision, extension_cross_dataset, CP)results/Report/figures/Report/paper_hub_bundle/, results/interactive/All datasets are automatically downloaded via PyTorch's torchvision on first use:
| Dataset | Source | Usage |
|---|---|---|
| MNIST | torchvision.datasets.MNIST | Vision (Section 4), baseline digit classification |
| Fashion-MNIST | torchvision.datasets.FashionMNIST | Vision (Section 4), more complex classification |
| EMNIST (Letters) | torchvision.datasets.EMNIST | Extension 1, cross-dataset robustness |
| USPS | torchvision.datasets.USPS | Extension 1, transfer learning validation |
Language models (Section 5) use pre-trained models from HuggingFace:
tdooms/ts-medium (6L TinyStories)tdooms/fw-small (12L FineWeb)tdooms/fw-medium (16L FineWeb)SAE checkpoints are also from HuggingFace (tdooms/fw-medium-scope, etc.).
Data directory: Downloaded datasets are cached in data/ (gitignored). The first run may take a few minutes to download.
Due to file size constraints, trained model checkpoints and full result files are hosted on Google Drive.
Artifacts are downloaded automatically when running figure generation scripts via src/artifact_loader.py. To manually trigger download:
from src.artifact_loader import ensure_artifacts
ensure_artifacts() # Downloads checkpoints.zip and results.zip if not present
Or via CLI:
python -m src.artifact_loader
If automatic download fails (e.g., SSL issues, rate limiting), download manually:
Download the zip files:
Place them in the project root and extract:
# Using Python (recommended)
python -c "from src.artifact_loader import extract_zip; extract_zip('checkpoints.zip', 'checkpoints'); extract_zip('results.zip', 'results')"
# Or using command line
unzip checkpoints.zip
unzip results.zip
Verify the structure:
bilinear_mlp_reproduction/
├── checkpoints/
│ ├── vision/
│ │ ├── mnist/
│ │ ├── fashion/
│ │ ├── challenge/
│ │ ├── noise_sweep/
│ │ └── size_sweep/
│ ├── extension_cross_dataset/
│ └── extension_cp/
└── results/
├── language/
├── extension_cross_dataset/
└── ...
| File | Size | MD5 |
|---|---|---|
| checkpoints.zip | ~3.3 GB | 33e1c3e2a2f7eb7b1f011a03c2c9ee78 |
| results.zip | ~196 MB | 2d9bbcd60dd5b5925fdee1b14a42a2d6 |
Verify with: md5 checkpoints.zip results.zip (macOS) or md5sum checkpoints.zip results.zip (Linux)
These checkpoints enable full reproduction of all figures without retraining (~40 GPU hours).
cd Report
pdflatex main.tex
bibtex main
pdflatex main.tex
pdflatex main.tex
The final presentation lives in presentation/.
cd presentation
pdflatex main.tex
These are enforced project conventions:
bilinear-decomposition-main/ contains the original paper code with minor modifications for MPS compatibility (Apple Silicon). Changes include CPU fallbacks for torch.linalg.eigh() which is unsupported on MPS.[42, 43, 44, 45, 46].wandb and track CO2 via codecarbon.einsum ops are forced to CPU on MPS (language).python -m pytest tests/ -v
python -m pytest tests/ -v -k "test_effective_rank"
bilinear_mlp_reproduction/
├── notebooks/ # Jupyter notebooks with all results (run these!)
│ ├── 01_reproduction_vision.ipynb # Vision experiments (Section 4)
│ ├── 02_reproduction_language.ipynb # Language experiments (Section 5)
│ ├── 03a_extension_cross_dataset_robustness.ipynb # Extension 1: Cross-dataset transfer
│ └── 03b_cp_extension.ipynb # Extension 2: CP decomposition analysis
├── src/ # Core code (vision, language, models, plot_utils)
├── scripts/ # Experiment runners and figure generation
├── configs/ # YAML experiment configs
├── tests/ # Unit tests (pytest)
├── Report/ # Final report (LaTeX + figures)
├── presentation/ # Final slides
├── environment.yml # GPU environment (CUDA)
├── environment_cpu.yml # CPU/MPS environment (Mac/local)
└── bilinear-decomposition-main/ # Original paper code (wrapped, not modified)
src/: All reusable code (models, data loading, spectral analysis, plotting)scripts/: Shell scripts and Python runners for experimentsnotebooks/: Self-contained notebooks that generate all report figurestests/: Pytest unit tests for core functionalityThis repository reproduces and extends "Bilinear MLPs enable weight-based mechanistic interpretability" (Pearce et al., ICLR 2025). It targets:
The goal of this README is to make the codebase reproducible without extra effort, and to document the report and presentation build steps.
These commands verify the environment and run minimal tests.
# Vision (2 epochs, MPS-friendly)
./scripts/train/run_vision.sh test
# Language (quick tests)
./scripts/train/run_language.sh test
# Cross-dataset robustness (2 epochs, MNIST only)
./scripts/train/run_extension_cross_dataset.sh test
# Snellius (GPU)
conda env create -f environment.yml && conda activate bilinear_mlp
# Local (CPU/MPS)
conda env create -f environment_cpu.yml && conda activate bilinear_mlp_cpu
The notebooks/ directory contains Jupyter notebooks that reproduce all figures and results from the report. Each notebook can be run end-to-end and automatically downloads required checkpoints/results from Google Drive.
| Notebook | Description | Report Sections |
|---|---|---|
01_reproduction_vision.ipynb | Vision experiments (Section 4): eigenspectrum analysis, eigenvector visualization, ablation studies. Verifies low-rank emergence, weight decay effectiveness, and interpretable eigenvectors. | Figures 1-7 |
02_reproduction_language.ipynb | Language experiments (Section 5): correlation analysis, negation circuits, SAE training effect. Verifies low-rank approximation, negation AND-gate structure, and SAE training correlation. | Figures 8-10 |
03a_extension_cross_dataset_robustness.ipynb | Extension 1: Cross-Dataset Robustness: Tests whether regularized models learn universal "Platonic forms" (shape geometry) vs dataset-specific artifacts. Validates via EMNIST-Digits transfer, USPS domain shift, and geometric letter semantics (O→0, I→1). | Extension 1 |
03b_cp_extension.ipynb | Extension 2: CP Decomposition: Analyzes architectural rank constraints (Fixed, Lambda, Gated CP) vs emergent low-rank from regularization. Compares accuracy-interpretability trade-offs and eigenvector quality. | Extension 2 |
# Activate environment
conda activate bilinear_mlp_cpu # or bilinear_mlp for GPU
# Start Jupyter
jupyter notebook notebooks/
# Or run specific notebook from command line
jupyter nbconvert --to notebook --execute notebooks/01_reproduction_vision.ipynb
Note: Checkpoints and results are automatically downloaded on first run. All plotting code is imported from src/plot_utils/ modules.
# Base configs (4 configs x 5 seeds x MNIST+Fashion)
./scripts/train/run_vision.sh train base
# Noise sweep (Figure 4)
./scripts/train/run_vision.sh train noise
# Model size sweep (Figure 5)
./scripts/train/run_vision.sh train size
# Challenge task (Figure 6)
./scripts/train/run_vision.sh train challenge
# Adversarial robustness (Figure 7) - uses existing checkpoints
./scripts/train/run_vision.sh figures # Generates Figure 7 from noise sweep
# Generate all vision figures
./scripts/train/run_vision.sh figures
# Run all vision experiments
./scripts/train/run_vision.sh all
# Figure 8 negation circuit visualization
./scripts/train/run_language.sh figure8 --device mps
# Figure 9 correlation sweep (all 3 models)
./scripts/train/run_language.sh figure9
# Figure 10 SAE training time analysis
./scripts/train/run_language.sh figure10
# Negation discovery
./scripts/train/run_language.sh negation
# Interaction analysis
./scripts/train/run_language.sh interaction
# Generate all language figures
./scripts/train/run_language.sh figures
# Run all language experiments
./scripts/train/run_language.sh all
# Train all cross-dataset models (MNIST + EMNIST, CoM enabled)
./scripts/train/run_extension_cross_dataset.sh train all
# Generate all cross-dataset figures
./scripts/train/run_extension_cross_dataset.sh figures
# Run full pipeline (train + figures)
./scripts/train/run_extension_cross_dataset.sh all
# Train CP models (rank sweep, all modes)
./scripts/train/run_extension_cp.sh train all
# Generate all CP figures
./scripts/train/run_extension_cp.sh figures
# Run full pipeline (train + figures)
./scripts/train/run_extension_cp.sh all
# Quick test (2 epochs)
./scripts/train/run_extension_cp.sh test
checkpoints/ (vision, extension_cross_dataset, CP)results/Report/figures/Report/paper_hub_bundle/, results/interactive/All datasets are automatically downloaded via PyTorch's torchvision on first use:
| Dataset | Source | Usage |
|---|---|---|
| MNIST | torchvision.datasets.MNIST | Vision (Section 4), baseline digit classification |
| Fashion-MNIST | torchvision.datasets.FashionMNIST | Vision (Section 4), more complex classification |
| EMNIST (Letters) | torchvision.datasets.EMNIST | Extension 1, cross-dataset robustness |
| USPS | torchvision.datasets.USPS | Extension 1, transfer learning validation |
Language models (Section 5) use pre-trained models from HuggingFace:
tdooms/ts-medium (6L TinyStories)tdooms/fw-small (12L FineWeb)tdooms/fw-medium (16L FineWeb)SAE checkpoints are also from HuggingFace (tdooms/fw-medium-scope, etc.).
Data directory: Downloaded datasets are cached in data/ (gitignored). The first run may take a few minutes to download.
Due to file size constraints, trained model checkpoints and full result files are hosted on Google Drive.
Artifacts are downloaded automatically when running figure generation scripts via src/artifact_loader.py. To manually trigger download:
from src.artifact_loader import ensure_artifacts
ensure_artifacts() # Downloads checkpoints.zip and results.zip if not present
Or via CLI:
python -m src.artifact_loader
If automatic download fails (e.g., SSL issues, rate limiting), download manually:
Download the zip files:
Place them in the project root and extract:
# Using Python (recommended)
python -c "from src.artifact_loader import extract_zip; extract_zip('checkpoints.zip', 'checkpoints'); extract_zip('results.zip', 'results')"
# Or using command line
unzip checkpoints.zip
unzip results.zip
Verify the structure:
bilinear_mlp_reproduction/
├── checkpoints/
│ ├── vision/
│ │ ├── mnist/
│ │ ├── fashion/
│ │ ├── challenge/
│ │ ├── noise_sweep/
│ │ └── size_sweep/
│ ├── extension_cross_dataset/
│ └── extension_cp/
└── results/
├── language/
├── extension_cross_dataset/
└── ...
| File | Size | MD5 |
|---|---|---|
| checkpoints.zip | ~3.3 GB | 33e1c3e2a2f7eb7b1f011a03c2c9ee78 |
| results.zip | ~196 MB | 2d9bbcd60dd5b5925fdee1b14a42a2d6 |
Verify with: md5 checkpoints.zip results.zip (macOS) or md5sum checkpoints.zip results.zip (Linux)
These checkpoints enable full reproduction of all figures without retraining (~40 GPU hours).
cd Report
pdflatex main.tex
bibtex main
pdflatex main.tex
pdflatex main.tex
The final presentation lives in presentation/.
cd presentation
pdflatex main.tex
These are enforced project conventions:
bilinear-decomposition-main/ contains the original paper code with minor modifications for MPS compatibility (Apple Silicon). Changes include CPU fallbacks for torch.linalg.eigh() which is unsupported on MPS.[42, 43, 44, 45, 46].wandb and track CO2 via codecarbon.einsum ops are forced to CPU on MPS (language).python -m pytest tests/ -v
python -m pytest tests/ -v -k "test_effective_rank"
bilinear_mlp_reproduction/
├── notebooks/ # Jupyter notebooks with all results (run these!)
│ ├── 01_reproduction_vision.ipynb # Vision experiments (Section 4)
│ ├── 02_reproduction_language.ipynb # Language experiments (Section 5)
│ ├── 03a_extension_cross_dataset_robustness.ipynb # Extension 1: Cross-dataset transfer
│ └── 03b_cp_extension.ipynb # Extension 2: CP decomposition analysis
├── src/ # Core code (vision, language, models, plot_utils)
├── scripts/ # Experiment runners and figure generation
├── configs/ # YAML experiment configs
├── tests/ # Unit tests (pytest)
├── Report/ # Final report (LaTeX + figures)
├── presentation/ # Final slides
├── environment.yml # GPU environment (CUDA)
├── environment_cpu.yml # CPU/MPS environment (Mac/local)
└── bilinear-decomposition-main/ # Original paper code (wrapped, not modified)
src/: All reusable code (models, data loading, spectral analysis, plotting)scripts/: Shell scripts and Python runners for experimentsnotebooks/: Self-contained notebooks that generate all report figurestests/: Pytest unit tests for core functionality